aboutsummaryrefslogtreecommitdiff
path: root/include/inja
diff options
context:
space:
mode:
Diffstat (limited to 'include/inja')
-rw-r--r--include/inja/config.hpp11
-rw-r--r--include/inja/environment.hpp87
-rw-r--r--include/inja/exceptions.hpp21
-rw-r--r--include/inja/function_storage.hpp86
-rw-r--r--include/inja/inja.hpp48
-rw-r--r--include/inja/lexer.hpp77
-rw-r--r--include/inja/node.hpp262
-rw-r--r--include/inja/parser.hpp273
-rw-r--r--include/inja/renderer.hpp524
-rw-r--r--include/inja/statistics.hpp37
-rw-r--r--include/inja/string_view.hpp1416
-rw-r--r--include/inja/template.hpp8
-rw-r--r--include/inja/token.hpp11
-rw-r--r--include/inja/utils.hpp40
14 files changed, 846 insertions, 2055 deletions
diff --git a/include/inja/config.hpp b/include/inja/config.hpp
index 3f284a4..0a8f9b7 100644
--- a/include/inja/config.hpp
+++ b/include/inja/config.hpp
@@ -1,12 +1,10 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_CONFIG_HPP_
#define INCLUDE_INJA_CONFIG_HPP_
#include <functional>
#include <string>
-#include "string_view.hpp"
+#include "template.hpp"
namespace inja {
@@ -25,7 +23,9 @@ struct LexerConfig {
std::string expression_close {"}}"};
std::string expression_close_force_rstrip {"-}}"};
std::string comment_open {"{#"};
+ std::string comment_open_force_lstrip {"{#-"};
std::string comment_close {"#}"};
+ std::string comment_close_force_rstrip {"-#}"};
std::string open_chars {"#{"};
bool trim_blocks {false};
@@ -54,6 +54,9 @@ struct LexerConfig {
if (open_chars.find(comment_open[0]) == std::string::npos) {
open_chars += comment_open[0];
}
+ if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
+ open_chars += comment_open_force_lstrip[0];
+ }
}
};
@@ -62,6 +65,8 @@ struct LexerConfig {
*/
struct ParserConfig {
bool search_included_templates_in_files {true};
+
+ std::function<Template(const std::string&, const std::string&)> include_callback;
};
/*!
diff --git a/include/inja/environment.hpp b/include/inja/environment.hpp
index ed99537..a652935 100644
--- a/include/inja/environment.hpp
+++ b/include/inja/environment.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
#define INCLUDE_INJA_ENVIRONMENT_HPP_
@@ -8,21 +6,17 @@
#include <memory>
#include <sstream>
#include <string>
-
-#include <nlohmann/json.hpp>
+#include <string_view>
#include "config.hpp"
#include "function_storage.hpp"
#include "parser.hpp"
#include "renderer.hpp"
-#include "string_view.hpp"
#include "template.hpp"
#include "utils.hpp"
namespace inja {
-using json = nlohmann::json;
-
/*!
* \brief Class for changing the configuration.
*/
@@ -38,15 +32,14 @@ class Environment {
TemplateStorage template_storage;
public:
- Environment() : Environment("") {}
+ Environment(): Environment("") {}
- explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {}
+ explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {}
- Environment(const std::string &input_path, const std::string &output_path)
- : input_path(input_path), output_path(output_path) {}
+ Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {}
/// Sets the opener and closer for template statements
- void set_statement(const std::string &open, const std::string &close) {
+ void set_statement(const std::string& open, const std::string& close) {
lexer_config.statement_open = open;
lexer_config.statement_open_no_lstrip = open + "+";
lexer_config.statement_open_force_lstrip = open + "-";
@@ -56,13 +49,13 @@ public:
}
/// Sets the opener for template line statements
- void set_line_statement(const std::string &open) {
+ void set_line_statement(const std::string& open) {
lexer_config.line_statement = open;
lexer_config.update_open_chars();
}
/// Sets the opener and closer for template expressions
- void set_expression(const std::string &open, const std::string &close) {
+ void set_expression(const std::string& open, const std::string& close) {
lexer_config.expression_open = open;
lexer_config.expression_open_force_lstrip = open + "-";
lexer_config.expression_close = close;
@@ -71,9 +64,11 @@ public:
}
/// Sets the opener and closer for template comments
- void set_comment(const std::string &open, const std::string &close) {
+ void set_comment(const std::string& open, const std::string& close) {
lexer_config.comment_open = open;
+ lexer_config.comment_open_force_lstrip = open + "-";
lexer_config.comment_close = close;
+ lexer_config.comment_close_force_rstrip = "-" + close;
lexer_config.update_open_chars();
}
@@ -97,75 +92,79 @@ public:
render_config.throw_at_missing_includes = will_throw;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.parse(input);
}
- Template parse_template(const std::string &filename) {
+ Template parse_template(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
return result;
}
- Template parse_file(const std::string &filename) {
+ Template parse_file(const std::string& filename) {
return parse_template(filename);
}
- std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); }
+ std::string render(std::string_view input, const json& data) {
+ return render(parse(input), data);
+ }
- std::string render(const Template &tmpl, const json &data) {
+ std::string render(const Template& tmpl, const json& data) {
std::stringstream os;
render_to(os, tmpl, data);
return os.str();
}
- std::string render_file(const std::string &filename, const json &data) {
+ std::string render_file(const std::string& filename, const json& data) {
return render(parse_template(filename), data);
}
- std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) {
+ std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
const json data = load_json(filename_data);
return render_file(filename, data);
}
- void write(const std::string &filename, const json &data, const std::string &filename_out) {
+ void write(const std::string& filename, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render_file(filename, data);
file.close();
}
- void write(const Template &temp, const json &data, const std::string &filename_out) {
+ void write(const Template& temp, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render(temp, data);
file.close();
}
- void write_with_json_file(const std::string &filename, const std::string &filename_data,
- const std::string &filename_out) {
+ void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(filename, data, filename_out);
}
- void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) {
+ void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(temp, data, filename_out);
}
- std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) {
+ std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
return os;
}
- std::string load_file(const std::string &filename) {
+ std::string load_file(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.load_file(input_path + filename);
}
- json load_json(const std::string &filename) {
+ json load_json(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(input_path + filename, file);
+ file.open(input_path + filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
+ }
json j;
file >> j;
return j;
@@ -174,51 +173,61 @@ public:
/*!
@brief Adds a variadic callback
*/
- void add_callback(const std::string &name, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, const CallbackFunction& callback) {
add_callback(name, -1, callback);
}
/*!
@brief Adds a variadic void callback
*/
- void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) {
+ void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
add_void_callback(name, -1, callback);
}
/*!
@brief Adds a callback with given number or arguments
*/
- void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
function_storage.add_callback(name, num_args, callback);
}
/*!
@brief Adds a void callback with given number or arguments
*/
- void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) {
- function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); });
+ void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
+ function_storage.add_callback(name, num_args, [callback](Arguments& args) {
+ callback(args);
+ return json();
+ });
}
/** Includes a template with a given name into the environment.
* Then, a template can be rendered in another template using the
* include "<name>" syntax.
*/
- void include_template(const std::string &name, const Template &tmpl) {
+ void include_template(const std::string& name, const Template& tmpl) {
template_storage[name] = tmpl;
}
+
+ /*!
+ @brief Sets a function that is called when an included file is not found
+ */
+ void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
+ parser_config.include_callback = callback;
+ }
};
/*!
@brief render with default settings to a string
*/
-inline std::string render(nonstd::string_view input, const json &data) {
+inline std::string render(std::string_view input, const json& data) {
return Environment().render(input, data);
}
/*!
@brief render with default settings to the given output stream
*/
-inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) {
+inline void render_to(std::ostream& os, std::string_view input, const json& data) {
Environment env;
env.render_to(os, env.parse(input), data);
}
diff --git a/include/inja/exceptions.hpp b/include/inja/exceptions.hpp
index 2784da8..f0dc8aa 100644
--- a/include/inja/exceptions.hpp
+++ b/include/inja/exceptions.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
#define INCLUDE_INJA_EXCEPTIONS_HPP_
@@ -19,30 +17,29 @@ struct InjaError : public std::runtime_error {
const SourceLocation location;
- explicit InjaError(const std::string &type, const std::string &message)
+ explicit InjaError(const std::string& type, const std::string& message)
: std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
- explicit InjaError(const std::string &type, const std::string &message, SourceLocation location)
- : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" +
- std::to_string(location.column) + ") " + message),
+ explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
+ : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message),
type(type), message(message), location(location) {}
};
struct ParserError : public InjaError {
- explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {}
+ explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {}
};
struct RenderError : public InjaError {
- explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {}
+ explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {}
};
struct FileError : public InjaError {
- explicit FileError(const std::string &message) : InjaError("file_error", message) {}
- explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {}
+ explicit FileError(const std::string& message): InjaError("file_error", message) {}
+ explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {}
};
-struct JsonError : public InjaError {
- explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {}
+struct DataError : public InjaError {
+ explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {}
};
} // namespace inja
diff --git a/include/inja/function_storage.hpp b/include/inja/function_storage.hpp
index b0091bd..891e45f 100644
--- a/include/inja/function_storage.hpp
+++ b/include/inja/function_storage.hpp
@@ -1,19 +1,14 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
+#include <string_view>
#include <vector>
-#include "string_view.hpp"
-
namespace inja {
-using json = nlohmann::json;
-
-using Arguments = std::vector<const json *>;
-using CallbackFunction = std::function<json(Arguments &args)>;
-using VoidCallbackFunction = std::function<void(Arguments &args)>;
+using Arguments = std::vector<const json*>;
+using CallbackFunction = std::function<json(Arguments& args)>;
+using VoidCallbackFunction = std::function<void(Arguments& args)>;
/*!
* \brief Class for builtin functions and user-defined callbacks.
@@ -64,6 +59,8 @@ public:
Round,
Sort,
Upper,
+ Super,
+ Join,
Callback,
ParenLeft,
ParenRight,
@@ -71,7 +68,7 @@ public:
};
struct FunctionData {
- explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {}
+ explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {}
const Operation operation;
const CallbackFunction callback;
};
@@ -80,49 +77,52 @@ private:
const int VARIADIC {-1};
std::map<std::pair<std::string, int>, FunctionData> function_storage = {
- {std::make_pair("at", 2), FunctionData { Operation::At }},
- {std::make_pair("default", 2), FunctionData { Operation::Default }},
- {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }},
- {std::make_pair("even", 1), FunctionData { Operation::Even }},
- {std::make_pair("exists", 1), FunctionData { Operation::Exists }},
- {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }},
- {std::make_pair("first", 1), FunctionData { Operation::First }},
- {std::make_pair("float", 1), FunctionData { Operation::Float }},
- {std::make_pair("int", 1), FunctionData { Operation::Int }},
- {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }},
- {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }},
- {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }},
- {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }},
- {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }},
- {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }},
- {std::make_pair("isString", 1), FunctionData { Operation::IsString }},
- {std::make_pair("last", 1), FunctionData { Operation::Last }},
- {std::make_pair("length", 1), FunctionData { Operation::Length }},
- {std::make_pair("lower", 1), FunctionData { Operation::Lower }},
- {std::make_pair("max", 1), FunctionData { Operation::Max }},
- {std::make_pair("min", 1), FunctionData { Operation::Min }},
- {std::make_pair("odd", 1), FunctionData { Operation::Odd }},
- {std::make_pair("range", 1), FunctionData { Operation::Range }},
- {std::make_pair("round", 2), FunctionData { Operation::Round }},
- {std::make_pair("sort", 1), FunctionData { Operation::Sort }},
- {std::make_pair("upper", 1), FunctionData { Operation::Upper }},
+ {std::make_pair("at", 2), FunctionData {Operation::At}},
+ {std::make_pair("default", 2), FunctionData {Operation::Default}},
+ {std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}},
+ {std::make_pair("even", 1), FunctionData {Operation::Even}},
+ {std::make_pair("exists", 1), FunctionData {Operation::Exists}},
+ {std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}},
+ {std::make_pair("first", 1), FunctionData {Operation::First}},
+ {std::make_pair("float", 1), FunctionData {Operation::Float}},
+ {std::make_pair("int", 1), FunctionData {Operation::Int}},
+ {std::make_pair("isArray", 1), FunctionData {Operation::IsArray}},
+ {std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}},
+ {std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}},
+ {std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}},
+ {std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}},
+ {std::make_pair("isObject", 1), FunctionData {Operation::IsObject}},
+ {std::make_pair("isString", 1), FunctionData {Operation::IsString}},
+ {std::make_pair("last", 1), FunctionData {Operation::Last}},
+ {std::make_pair("length", 1), FunctionData {Operation::Length}},
+ {std::make_pair("lower", 1), FunctionData {Operation::Lower}},
+ {std::make_pair("max", 1), FunctionData {Operation::Max}},
+ {std::make_pair("min", 1), FunctionData {Operation::Min}},
+ {std::make_pair("odd", 1), FunctionData {Operation::Odd}},
+ {std::make_pair("range", 1), FunctionData {Operation::Range}},
+ {std::make_pair("round", 2), FunctionData {Operation::Round}},
+ {std::make_pair("sort", 1), FunctionData {Operation::Sort}},
+ {std::make_pair("upper", 1), FunctionData {Operation::Upper}},
+ {std::make_pair("super", 0), FunctionData {Operation::Super}},
+ {std::make_pair("super", 1), FunctionData {Operation::Super}},
+ {std::make_pair("join", 2), FunctionData {Operation::Join}},
};
public:
- void add_builtin(nonstd::string_view name, int num_args, Operation op) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { op });
+ void add_builtin(std::string_view name, int num_args, Operation op) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {op});
}
- void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { Operation::Callback, callback });
+ void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {Operation::Callback, callback});
}
- FunctionData find_function(nonstd::string_view name, int num_args) const {
+ FunctionData find_function(std::string_view name, int num_args) const {
auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
if (it != function_storage.end()) {
return it->second;
- // Find variadic function
+ // Find variadic function
} else if (num_args > 0) {
it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
if (it != function_storage.end()) {
@@ -130,7 +130,7 @@ public:
}
}
- return FunctionData { Operation::None };
+ return FunctionData {Operation::None};
}
};
diff --git a/include/inja/inja.hpp b/include/inja/inja.hpp
index 86b8a0a..381da4b 100644
--- a/include/inja/inja.hpp
+++ b/include/inja/inja.hpp
@@ -1,22 +1,60 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
+/*
+ ___ _ Version 3.3
+ |_ _|_ __ (_) __ _ https://github.com/pantor/inja
+ | || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+ | || | | || | (_| |
+ |___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid
+ |__/
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
#ifndef INCLUDE_INJA_INJA_HPP_
#define INCLUDE_INJA_INJA_HPP_
#include <nlohmann/json.hpp>
+namespace inja {
+#ifndef INJA_DATA_TYPE
+using json = nlohmann::json;
+#else
+using json = INJA_DATA_TYPE;
+#endif
+} // namespace inja
+
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
- #define INJA_THROW(exception) throw exception
+#ifndef INJA_THROW
+#define INJA_THROW(exception) throw exception
+#endif
#else
- #include <cstdlib>
- #define INJA_THROW(exception) std::abort()
+#include <cstdlib>
+#ifndef INJA_THROW
+#define INJA_THROW(exception) \
+ std::abort(); \
+ std::ignore = exception
+#endif
+#ifndef INJA_NOEXCEPTION
+#define INJA_NOEXCEPTION
+#endif
#endif
#include "environment.hpp"
#include "exceptions.hpp"
#include "parser.hpp"
#include "renderer.hpp"
-#include "string_view.hpp"
#include "template.hpp"
#endif // INCLUDE_INJA_INJA_HPP_
diff --git a/include/inja/lexer.hpp b/include/inja/lexer.hpp
index e31c3d6..c777600 100644
--- a/include/inja/lexer.hpp
+++ b/include/inja/lexer.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_LEXER_HPP_
#define INCLUDE_INJA_LEXER_HPP_
@@ -28,6 +26,7 @@ class Lexer {
StatementStartForceLstrip,
StatementBody,
CommentStart,
+ CommentStartForceLstrip,
CommentBody,
};
@@ -36,22 +35,21 @@ class Lexer {
Number,
};
- const LexerConfig &config;
+ const LexerConfig& config;
State state;
MinusState minus_state;
- nonstd::string_view m_in;
+ std::string_view m_in;
size_t tok_start;
size_t pos;
-
- Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) {
+ Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
again:
// skip whitespace (except for \n as it might be a close)
if (tok_start >= m_in.size()) {
return make_token(Token::Kind::Eof);
}
- char ch = m_in[tok_start];
+ const char ch = m_in[tok_start];
if (ch == ' ' || ch == '\t' || ch == '\r') {
tok_start += 1;
goto again;
@@ -61,7 +59,7 @@ class Lexer {
if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
state = State::Text;
pos = tok_start + close_trim.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
skip_whitespaces_and_newlines();
return tok;
}
@@ -69,7 +67,7 @@ class Lexer {
if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
state = State::Text;
pos = tok_start + close.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
if (trim) {
skip_whitespaces_and_first_newline();
}
@@ -88,7 +86,7 @@ class Lexer {
return scan_id();
}
- MinusState current_minus_state = minus_state;
+ const MinusState current_minus_state = minus_state;
if (minus_state == MinusState::Operator) {
minus_state = MinusState::Number;
}
@@ -183,7 +181,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
break;
}
@@ -197,7 +195,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
// be very permissive in lexer (we'll catch errors when conversion happens)
if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
break;
@@ -213,7 +211,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos++];
+ const char ch = m_in[pos++];
if (ch == '\\') {
escape = true;
} else if (!escape && ch == m_in[tok_start]) {
@@ -225,7 +223,9 @@ class Lexer {
return make_token(Token::Kind::String);
}
- Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); }
+ Token make_token(Token::Kind kind) const {
+ return Token(kind, string_view::slice(m_in, tok_start, pos));
+ }
void skip_whitespaces_and_newlines() {
if (pos < m_in.size()) {
@@ -243,7 +243,7 @@ class Lexer {
}
if (pos < m_in.size()) {
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (ch == '\n') {
pos += 1;
} else if (ch == '\r') {
@@ -255,10 +255,10 @@ class Lexer {
}
}
- static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) {
- nonstd::string_view result = text;
+ static std::string_view clear_final_line_if_whitespace(std::string_view text) {
+ std::string_view result = text;
while (!result.empty()) {
- char ch = result.back();
+ const char ch = result.back();
if (ch == ' ' || ch == '\t') {
result.remove_suffix(1);
} else if (ch == '\n' || ch == '\r') {
@@ -271,13 +271,13 @@ class Lexer {
}
public:
- explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {}
+ explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {}
SourceLocation current_position() const {
return get_source_location(m_in, tok_start);
}
- void start(nonstd::string_view input) {
+ void start(std::string_view input) {
m_in = input;
tok_start = 0;
pos = 0;
@@ -302,8 +302,8 @@ public:
default:
case State::Text: {
// fast-scan to first open character
- size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
- if (open_start == nonstd::string_view::npos) {
+ const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
+ if (open_start == std::string_view::npos) {
// didn't find open, return remaining text as text token
pos = m_in.size();
return make_token(Token::Kind::Text);
@@ -311,7 +311,7 @@ public:
pos += open_start;
// try to match one of the opening sequences, and get the close
- nonstd::string_view open_str = m_in.substr(pos);
+ std::string_view open_str = m_in.substr(pos);
bool must_lstrip = false;
if (inja::string_view::starts_with(open_str, config.expression_open)) {
if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
@@ -323,7 +323,7 @@ public:
} else if (inja::string_view::starts_with(open_str, config.statement_open)) {
if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
state = State::StatementStartNoLstrip;
- } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) {
+ } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) {
state = State::StatementStartForceLstrip;
must_lstrip = true;
} else {
@@ -331,8 +331,13 @@ public:
must_lstrip = config.lstrip_blocks;
}
} else if (inja::string_view::starts_with(open_str, config.comment_open)) {
- state = State::CommentStart;
- must_lstrip = config.lstrip_blocks;
+ if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) {
+ state = State::CommentStartForceLstrip;
+ must_lstrip = true;
+ } else {
+ state = State::CommentStart;
+ must_lstrip = config.lstrip_blocks;
+ }
} else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
state = State::LineStart;
} else {
@@ -340,7 +345,7 @@ public:
goto again;
}
- nonstd::string_view text = string_view::slice(m_in, tok_start, pos);
+ std::string_view text = string_view::slice(m_in, tok_start, pos);
if (must_lstrip) {
text = clear_final_line_if_whitespace(text);
}
@@ -385,6 +390,11 @@ public:
pos += config.comment_open.size();
return make_token(Token::Kind::CommentOpen);
}
+ case State::CommentStartForceLstrip: {
+ state = State::CommentBody;
+ pos += config.comment_open_force_lstrip.size();
+ return make_token(Token::Kind::CommentOpen);
+ }
case State::ExpressionBody:
return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
case State::LineBody:
@@ -393,16 +403,21 @@ public:
return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
case State::CommentBody: {
// fast-scan to comment close
- size_t end = m_in.substr(pos).find(config.comment_close);
- if (end == nonstd::string_view::npos) {
+ const size_t end = m_in.substr(pos).find(config.comment_close);
+ if (end == std::string_view::npos) {
pos = m_in.size();
return make_token(Token::Kind::Eof);
}
+
+ // Check for trim pattern
+ const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
+
// return the entire comment in the close token
state = State::Text;
pos += end + config.comment_close.size();
Token tok = make_token(Token::Kind::CommentClose);
- if (config.trim_blocks) {
+
+ if (must_rstrip || config.trim_blocks) {
skip_whitespaces_and_first_newline();
}
return tok;
@@ -410,7 +425,7 @@ public:
}
}
- const LexerConfig &get_config() const {
+ const LexerConfig& get_config() const {
return config;
}
};
diff --git a/include/inja/node.hpp b/include/inja/node.hpp
index 84a8b8a..1946805 100644
--- a/include/inja/node.hpp
+++ b/include/inja/node.hpp
@@ -1,16 +1,12 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_NODE_HPP_
#define INCLUDE_INJA_NODE_HPP_
#include <string>
+#include <string_view>
#include <utility>
-#include <nlohmann/json.hpp>
-
#include "function_storage.hpp"
-#include "string_view.hpp"
-
+#include "utils.hpp"
namespace inja {
@@ -19,7 +15,7 @@ class BlockNode;
class TextNode;
class ExpressionNode;
class LiteralNode;
-class JsonNode;
+class DataNode;
class FunctionNode;
class ExpressionListNode;
class StatementNode;
@@ -28,16 +24,19 @@ class ForArrayStatementNode;
class ForObjectStatementNode;
class IfStatementNode;
class IncludeStatementNode;
+class ExtendsStatementNode;
+class BlockStatementNode;
class SetStatementNode;
-
class NodeVisitor {
public:
+ virtual ~NodeVisitor() = default;
+
virtual void visit(const BlockNode& node) = 0;
virtual void visit(const TextNode& node) = 0;
virtual void visit(const ExpressionNode& node) = 0;
virtual void visit(const LiteralNode& node) = 0;
- virtual void visit(const JsonNode& node) = 0;
+ virtual void visit(const DataNode& node) = 0;
virtual void visit(const FunctionNode& node) = 0;
virtual void visit(const ExpressionListNode& node) = 0;
virtual void visit(const StatementNode& node) = 0;
@@ -46,6 +45,8 @@ public:
virtual void visit(const ForObjectStatementNode& node) = 0;
virtual void visit(const IfStatementNode& node) = 0;
virtual void visit(const IncludeStatementNode& node) = 0;
+ virtual void visit(const ExtendsStatementNode& node) = 0;
+ virtual void visit(const BlockStatementNode& node) = 0;
virtual void visit(const SetStatementNode& node) = 0;
};
@@ -58,16 +59,15 @@ public:
size_t pos;
- AstNode(size_t pos) : pos(pos) { }
- virtual ~AstNode() { };
+ AstNode(size_t pos): pos(pos) {}
+ virtual ~AstNode() {}
};
-
class BlockNode : public AstNode {
public:
std::vector<std::shared_ptr<AstNode>> nodes;
- explicit BlockNode() : AstNode(0) {}
+ explicit BlockNode(): AstNode(0) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -78,7 +78,7 @@ class TextNode : public AstNode {
public:
const size_t length;
- explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { }
+ explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -87,7 +87,7 @@ public:
class ExpressionNode : public AstNode {
public:
- explicit ExpressionNode(size_t pos) : AstNode(pos) {}
+ explicit ExpressionNode(size_t pos): AstNode(pos) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -96,24 +96,24 @@ public:
class LiteralNode : public ExpressionNode {
public:
- const nlohmann::json value;
+ const json value;
- explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { }
+ explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
}
};
-class JsonNode : public ExpressionNode {
+class DataNode : public ExpressionNode {
public:
const std::string name;
const json::json_pointer ptr;
- static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) {
+ static std::string convert_dot_to_ptr(std::string_view ptr_name) {
std::string result;
do {
- nonstd::string_view part;
+ std::string_view part;
std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
result.push_back('/');
result.append(part.begin(), part.end());
@@ -121,7 +121,7 @@ public:
return result;
}
- explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { }
+ explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -144,83 +144,102 @@ public:
std::string name;
int number_args; // Should also be negative -> -1 for unknown number
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
CallbackFunction callback;
- explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { }
- explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) {
+ explicit FunctionNode(std::string_view name, size_t pos)
+ : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
+ explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
switch (operation) {
- case Op::Not: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::And: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::Or: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::In: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Equal: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::NotEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Greater: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::GreaterEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Less: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::LessEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Add: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Subtract: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Multiplication: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Division: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Power: {
- precedence = 5;
- associativity = Associativity::Right;
- } break;
- case Op::Modulo: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::AtId: {
- precedence = 8;
- associativity = Associativity::Left;
- } break;
- default: {
- precedence = 1;
- associativity = Associativity::Left;
- }
+ case Op::Not: {
+ number_args = 1;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::And: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Or: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::In: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Equal: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::NotEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Greater: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::GreaterEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Less: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::LessEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Add: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Subtract: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Multiplication: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Division: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Power: {
+ number_args = 2;
+ precedence = 5;
+ associativity = Associativity::Right;
+ } break;
+ case Op::Modulo: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::AtId: {
+ number_args = 2;
+ precedence = 8;
+ associativity = Associativity::Left;
+ } break;
+ default: {
+ precedence = 1;
+ associativity = Associativity::Left;
+ }
}
}
@@ -231,10 +250,10 @@ public:
class ExpressionListNode : public AstNode {
public:
- std::vector<std::shared_ptr<ExpressionNode>> rpn_output;
+ std::shared_ptr<ExpressionNode> root;
- explicit ExpressionListNode() : AstNode(0) { }
- explicit ExpressionListNode(size_t pos) : AstNode(pos) { }
+ explicit ExpressionListNode(): AstNode(0) {}
+ explicit ExpressionListNode(size_t pos): AstNode(pos) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -243,7 +262,7 @@ public:
class StatementNode : public AstNode {
public:
- StatementNode(size_t pos) : AstNode(pos) { }
+ StatementNode(size_t pos): AstNode(pos) {}
virtual void accept(NodeVisitor& v) const = 0;
};
@@ -252,9 +271,9 @@ class ForStatementNode : public StatementNode {
public:
ExpressionListNode condition;
BlockNode body;
- BlockNode *const parent;
+ BlockNode* const parent;
- ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { }
+ ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {}
virtual void accept(NodeVisitor& v) const = 0;
};
@@ -263,7 +282,7 @@ class ForArrayStatementNode : public ForStatementNode {
public:
const std::string value;
- explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { }
+ explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -275,7 +294,8 @@ public:
const std::string key;
const std::string value;
- explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { }
+ explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
+ : ForStatementNode(parent, pos), key(key), value(value) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -287,13 +307,13 @@ public:
ExpressionListNode condition;
BlockNode true_statement;
BlockNode false_statement;
- BlockNode *const parent;
+ BlockNode* const parent;
const bool is_nested;
bool has_false_statement {false};
- explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { }
- explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { }
+ explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {}
+ explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -304,7 +324,31 @@ class IncludeStatementNode : public StatementNode {
public:
const std::string file;
- explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { }
+ explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
+
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ }
+};
+
+class ExtendsStatementNode : public StatementNode {
+public:
+ const std::string file;
+
+ explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
+
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ };
+};
+
+class BlockStatementNode : public StatementNode {
+public:
+ const std::string name;
+ BlockNode block;
+ BlockNode* const parent;
+
+ explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -316,11 +360,11 @@ public:
const std::string key;
ExpressionListNode expression;
- explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { }
+ explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
- };
+ }
};
} // namespace inja
diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp
index 6266c4a..3ea1793 100644
--- a/include/inja/parser.hpp
+++ b/include/inja/parser.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_PARSER_HPP_
#define INCLUDE_INJA_PARSER_HPP_
@@ -7,7 +5,6 @@
#include <stack>
#include <string>
#include <utility>
-#include <queue>
#include <vector>
#include "config.hpp"
@@ -19,19 +16,17 @@
#include "token.hpp"
#include "utils.hpp"
-#include <nlohmann/json.hpp>
-
namespace inja {
/*!
* \brief Class for parsing an inja Template.
*/
class Parser {
- const ParserConfig &config;
+ const ParserConfig& config;
Lexer lexer;
- TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
Token tok, peek_tok;
bool have_peek_tok {false};
@@ -40,21 +35,23 @@ class Parser {
size_t current_bracket_level {0};
size_t current_brace_level {0};
- nonstd::string_view json_literal_start;
+ std::string_view literal_start;
- BlockNode *current_block {nullptr};
- ExpressionListNode *current_expression_list {nullptr};
+ BlockNode* current_block {nullptr};
+ ExpressionListNode* current_expression_list {nullptr};
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
std::stack<IfStatementNode*> if_statement_stack;
std::stack<ForStatementNode*> for_statement_stack;
+ std::stack<BlockStatementNode*> block_statement_stack;
- void throw_parser_error(const std::string &message) {
+ inline void throw_parser_error(const std::string& message) const {
INJA_THROW(ParserError(message, lexer.current_position()));
}
- void get_next_token() {
+ inline void get_next_token() {
if (have_peek_tok) {
tok = peek_tok;
have_peek_tok = false;
@@ -63,49 +60,108 @@ class Parser {
}
}
- void get_peek_token() {
+ inline void get_peek_token() {
if (!have_peek_tok) {
peek_tok = lexer.scan();
have_peek_tok = true;
}
}
- void add_json_literal(const char* content_ptr) {
- nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size());
- current_expression_list->rpn_output.emplace_back(std::make_shared<LiteralNode>(json::parse(json_text), json_text.data() - content_ptr));
+ inline void add_literal(const char* content_ptr) {
+ std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
+ arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
+ }
+
+ inline void add_operator() {
+ auto function = operator_stack.top();
+ operator_stack.pop();
+
+ for (int i = 0; i < function->number_args; ++i) {
+ function->arguments.insert(function->arguments.begin(), arguments.back());
+ arguments.pop_back();
+ }
+ arguments.emplace_back(function);
+ }
+
+ void add_to_template_storage(std::string_view path, std::string& template_name) {
+ if (template_storage.find(template_name) != template_storage.end()) {
+ return;
+ }
+
+ std::string original_path = static_cast<std::string>(path);
+ std::string original_name = template_name;
+
+ if (config.search_included_templates_in_files) {
+ // Build the relative path
+ template_name = original_path + original_name;
+ if (template_name.compare(0, 2, "./") == 0) {
+ template_name.erase(0, 2);
+ }
+
+ if (template_storage.find(template_name) == template_storage.end()) {
+ // Load file
+ std::ifstream file;
+ file.open(template_name);
+ if (!file.fail()) {
+ std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+
+ auto include_template = Template(text);
+ template_storage.emplace(template_name, include_template);
+ parse_into_template(template_storage[template_name], template_name);
+ return;
+ } else if (!config.include_callback) {
+ INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
+ }
+ }
+ }
+
+ // Try include callback
+ if (config.include_callback) {
+ auto include_template = config.include_callback(original_path, original_name);
+ template_storage.emplace(template_name, include_template);
+ }
+ }
+
+ std::string parse_filename(const Token& tok) const {
+ if (tok.kind != Token::Kind::String) {
+ throw_parser_error("expected string, got '" + tok.describe() + "'");
+ }
+
+ if (tok.text.length() < 2) {
+ throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
+ }
+
+ // Remove first and last character ""
+ return std::string {tok.text.substr(1, tok.text.length() - 2)};
}
- bool parse_expression(Template &tmpl, Token::Kind closing) {
+ bool parse_expression(Template& tmpl, Token::Kind closing) {
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
// Literals
switch (tok.kind) {
case Token::Kind::String: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Number: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::LeftBracket: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_bracket_level += 1;
-
} break;
case Token::Kind::LeftBrace: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_brace_level += 1;
-
} break;
case Token::Kind::RightBracket: {
if (current_bracket_level == 0) {
@@ -114,9 +170,8 @@ class Parser {
current_bracket_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::RightBrace: {
if (current_brace_level == 0) {
@@ -125,35 +180,35 @@ class Parser {
current_brace_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Id: {
get_peek_token();
- // Json Literal
- if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") || tok.text == static_cast<decltype(tok.text)>("null")) {
+ // Data Literal
+ if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
+ tok.text == static_cast<decltype(tok.text)>("null")) {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
- // Operator
+ // Operator
} else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
goto parse_operator;
- // Functions
+ // Functions
} else if (peek_tok.kind == Token::Kind::LeftParen) {
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
- function_stack.emplace(operator_stack.top().get(), current_paren_level);
+ function_stack.emplace(operator_stack.top().get(), current_paren_level);
- // Variables
+ // Variables
} else {
- current_expression_list->rpn_output.emplace_back(std::make_shared<JsonNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
+ arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
}
- // Operators
+ // Operators
} break;
case Token::Kind::Equal:
case Token::Kind::NotEqual:
@@ -169,7 +224,7 @@ class Parser {
case Token::Kind::Percent:
case Token::Kind::Dot: {
- parse_operator:
+ parse_operator:
FunctionStorage::Operation operation;
switch (tok.kind) {
case Token::Kind::Id: {
@@ -230,13 +285,14 @@ class Parser {
}
auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
- while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ while (!operator_stack.empty() &&
+ ((operator_stack.top()->precedence > function_node->precedence) ||
+ (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
+ (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
+ add_operator();
}
operator_stack.emplace(function_node);
-
} break;
case Token::Kind::Comma: {
if (current_brace_level == 0 && current_bracket_level == 0) {
@@ -246,13 +302,11 @@ class Parser {
function_stack.top().first->number_args += 1;
}
-
} break;
case Token::Kind::Colon: {
if (current_brace_level == 0 && current_bracket_level == 0) {
throw_parser_error("unexpected ':'");
}
-
} break;
case Token::Kind::LeftParen: {
current_paren_level += 1;
@@ -264,13 +318,11 @@ class Parser {
function_stack.top().first->number_args = 0;
}
}
-
} break;
case Token::Kind::RightParen: {
current_paren_level -= 1;
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
}
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
@@ -292,8 +344,7 @@ class Parser {
throw_parser_error("internal error at function " + func->name);
}
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
function_stack.pop();
}
}
@@ -305,14 +356,20 @@ class Parser {
}
while (!operator_stack.empty()) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
+ }
+
+ if (arguments.size() == 1) {
+ current_expression_list->root = arguments[0];
+ arguments = {};
+ } else if (arguments.size() > 1) {
+ throw_parser_error("malformed expression");
}
return true;
}
- bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) {
+ bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
if (tok.kind != Token::Kind::Id) {
return false;
}
@@ -329,12 +386,11 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("else")) {
if (if_statement_stack.empty()) {
throw_parser_error("else without matching if");
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
if_statement_data->has_false_statement = true;
@@ -354,7 +410,6 @@ class Parser {
return false;
}
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
if (if_statement_stack.empty()) {
throw_parser_error("endif without matching if");
@@ -365,12 +420,40 @@ class Parser {
if_statement_stack.pop();
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
current_block = if_statement_data->parent;
if_statement_stack.pop();
+ } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
+ get_next_token();
+
+ if (tok.kind != Token::Kind::Id) {
+ throw_parser_error("expected block name, got '" + tok.describe() + "'");
+ }
+
+ const std::string block_name = static_cast<std::string>(tok.text);
+
+ auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
+ current_block->nodes.emplace_back(block_statement_node);
+ block_statement_stack.emplace(block_statement_node.get());
+ current_block = &block_statement_node->block;
+ auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
+ if (!success.second) {
+ throw_parser_error("block with the name '" + block_name + "' does already exist");
+ }
+
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
+ if (block_statement_stack.empty()) {
+ throw_parser_error("endblock without matching block");
+ }
+
+ auto& block_statement_data = block_statement_stack.top();
+ get_next_token();
+ current_block = block_statement_data->parent;
+ block_statement_stack.pop();
} else if (tok.text == static_cast<decltype(tok.text)>("for")) {
get_next_token();
@@ -394,11 +477,13 @@ class Parser {
value_token = tok;
get_next_token();
- for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text),
+ current_block, tok.text.data() - tmpl.content.c_str());
- // Array type
+ // Array type
} else {
- for_statement_node = std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node =
+ std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
}
current_block->nodes.emplace_back(for_statement_node);
@@ -414,44 +499,34 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
if (for_statement_stack.empty()) {
throw_parser_error("endfor without matching for");
}
- auto &for_statement_data = for_statement_stack.top();
+ auto& for_statement_data = for_statement_stack.top();
get_next_token();
current_block = for_statement_data->parent;
for_statement_stack.pop();
-
} else if (tok.text == static_cast<decltype(tok.text)>("include")) {
get_next_token();
- if (tok.kind != Token::Kind::String) {
- throw_parser_error("expected string, got '" + tok.describe() + "'");
- }
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- // Build the relative path
- json json_name = json::parse(tok.text);
- std::string pathname = static_cast<std::string>(path);
- pathname += json_name.get_ref<const std::string &>();
- if (pathname.compare(0, 2, "./") == 0) {
- pathname.erase(0, 2);
- }
- // sys::path::remove_dots(pathname, true, sys::path::Style::posix);
+ current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
- if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) {
- auto include_template = Template(load_file(pathname));
- template_storage.emplace(pathname, include_template);
- parse_into_template(template_storage[pathname], pathname);
- }
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
+ get_next_token();
- current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(pathname, tok.text.data() - tmpl.content.c_str()));
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- get_next_token();
+ current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
+ get_next_token();
} else if (tok.text == static_cast<decltype(tok.text)>("set")) {
get_next_token();
@@ -474,14 +549,13 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else {
return false;
}
return true;
}
- void parse_into(Template &tmpl, nonstd::string_view path) {
+ void parse_into(Template& tmpl, std::string_view path) {
lexer.start(tmpl.content);
current_block = &tmpl.root;
@@ -495,7 +569,8 @@ class Parser {
if (!for_statement_stack.empty()) {
throw_parser_error("unmatched for");
}
- } return;
+ }
+ return;
case Token::Kind::Text: {
current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
} break;
@@ -545,33 +620,35 @@ class Parser {
}
}
-
public:
- explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config,
- TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { }
+ explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage,
+ const FunctionStorage& function_storage)
+ : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {}
- Template parse(nonstd::string_view input, nonstd::string_view path) {
+ Template parse(std::string_view input, std::string_view path) {
auto result = Template(static_cast<std::string>(input));
parse_into(result, path);
return result;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
return parse(input, "./");
}
- void parse_into_template(Template& tmpl, nonstd::string_view filename) {
- nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
+ void parse_into_template(Template& tmpl, std::string_view filename) {
+ std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
// StringRef path = sys::path::parent_path(filename);
auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
sub_parser.parse_into(tmpl, path);
}
- std::string load_file(nonstd::string_view filename) {
+ std::string load_file(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(static_cast<std::string>(filename), file);
+ file.open(filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
+ }
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text;
}
diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp
index 7bc518f..f24bb91 100644
--- a/include/inja/renderer.hpp
+++ b/include/inja/renderer.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_RENDERER_HPP_
#define INCLUDE_INJA_RENDERER_HPP_
@@ -9,8 +7,6 @@
#include <utility>
#include <vector>
-#include <nlohmann/json.hpp>
-
#include "config.hpp"
#include "exceptions.hpp"
#include "node.hpp"
@@ -22,25 +18,31 @@ namespace inja {
/*!
* \brief Class for rendering a Template with data.
*/
-class Renderer : public NodeVisitor {
+class Renderer : public NodeVisitor {
using Op = FunctionStorage::Operation;
const RenderConfig config;
- const Template *current_template;
- const TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ const TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
+
+ const Template* current_template;
+ size_t current_level {0};
+ std::vector<const Template*> template_stack;
+ std::vector<const BlockStatementNode*> block_statement_stack;
- const json *json_input;
- std::ostream *output_stream;
+ const json* data_input;
+ std::ostream* output_stream;
- json json_additional_data;
- json* current_loop_data = &json_additional_data["loop"];
+ json additional_data;
+ json* current_loop_data = &additional_data["loop"];
- std::vector<std::shared_ptr<json>> json_tmp_stack;
- std::stack<const json*> json_eval_stack;
- std::stack<const JsonNode*> not_found_stack;
+ std::vector<std::shared_ptr<json>> data_tmp_stack;
+ std::stack<const json*> data_eval_stack;
+ std::stack<const DataNode*> not_found_stack;
- bool truthy(const json* data) const {
+ bool break_rendering {false};
+
+ static bool truthy(const json* data) {
if (data->is_boolean()) {
return data->get<bool>();
} else if (data->is_number()) {
@@ -51,7 +53,7 @@ class Renderer : public NodeVisitor {
return !data->empty();
}
- void print_json(const std::shared_ptr<json> value) {
+ void print_data(const std::shared_ptr<json> value) {
if (value->is_string()) {
*output_stream << value->get_ref<const json::string_t&>();
} else if (value->is_number_integer()) {
@@ -63,18 +65,20 @@ class Renderer : public NodeVisitor {
}
const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
- for (auto& expression : expression_list.rpn_output) {
- expression->accept(*this);
+ if (!expression_list.root) {
+ throw_renderer_error("empty expression", expression_list);
}
- if (json_eval_stack.empty()) {
+ expression_list.root->accept(*this);
+
+ if (data_eval_stack.empty()) {
throw_renderer_error("empty expression", expression_list);
- } else if (json_eval_stack.size() != 1) {
+ } else if (data_eval_stack.size() != 1) {
throw_renderer_error("malformed expression", expression_list);
}
- auto result = json_eval_stack.top();
- json_eval_stack.pop();
+ const auto result = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result) {
if (not_found_stack.empty()) {
@@ -89,51 +93,68 @@ class Renderer : public NodeVisitor {
return std::make_shared<json>(*result);
}
- void throw_renderer_error(const std::string &message, const AstNode& node) {
+ void throw_renderer_error(const std::string& message, const AstNode& node) {
SourceLocation loc = get_source_location(current_template->content, node.pos);
INJA_THROW(RenderError(message, loc));
}
- template<size_t N, bool throw_not_found=true>
- std::array<const json*, N> get_arguments(const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ void make_result(const json&& result) {
+ auto result_ptr = std::make_shared<json>(result);
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
+ }
+
+ template <size_t N, size_t N_start = 0, bool throw_not_found = true> std::array<const json*, N> get_arguments(const FunctionNode& node) {
+ if (node.arguments.size() < N_start + N) {
+ throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
+ }
+
+ for (size_t i = N_start; i < N_start + N; i += 1) {
+ node.arguments[i]->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
std::array<const json*, N> result;
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
return result;
}
- template<bool throw_not_found=true>
- Arguments get_argument_vector(size_t N, const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ template <bool throw_not_found = true> Arguments get_argument_vector(const FunctionNode& node) {
+ const size_t N = node.arguments.size();
+ for (auto a : node.arguments) {
+ a->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
Arguments result {N};
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
@@ -143,6 +164,10 @@ class Renderer : public NodeVisitor {
void visit(const BlockNode& node) {
for (auto& n : node.nodes) {
n->accept(*this);
+
+ if (break_rendering) {
+ break;
+ }
}
}
@@ -150,325 +175,294 @@ class Renderer : public NodeVisitor {
output_stream->write(current_template->content.c_str() + node.pos, node.length);
}
- void visit(const ExpressionNode&) { }
+ void visit(const ExpressionNode&) {}
void visit(const LiteralNode& node) {
- json_eval_stack.push(&node.value);
+ data_eval_stack.push(&node.value);
}
- void visit(const JsonNode& node) {
- if (json_additional_data.contains(node.ptr)) {
- json_eval_stack.push(&(json_additional_data[node.ptr]));
-
- } else if (json_input->contains(node.ptr)) {
- json_eval_stack.push(&(*json_input)[node.ptr]);
-
+ void visit(const DataNode& node) {
+ if (additional_data.contains(node.ptr)) {
+ data_eval_stack.push(&(additional_data[node.ptr]));
+ } else if (data_input->contains(node.ptr)) {
+ data_eval_stack.push(&(*data_input)[node.ptr]);
} else {
// Try to evaluate as a no-argument callback
- auto function_data = function_storage.find_function(node.name, 0);
+ const auto function_data = function_storage.find_function(node.name, 0);
if (function_data.operation == FunctionStorage::Operation::Callback) {
Arguments empty_args {};
- auto value = std::make_shared<json>(function_data.callback(empty_args));
- json_tmp_stack.push_back(value);
- json_eval_stack.push(value.get());
-
+ const auto value = std::make_shared<json>(function_data.callback(empty_args));
+ data_tmp_stack.push_back(value);
+ data_eval_stack.push(value.get());
} else {
- json_eval_stack.push(nullptr);
+ data_eval_stack.push(nullptr);
not_found_stack.emplace(&node);
}
}
}
void visit(const FunctionNode& node) {
- std::shared_ptr<json> result_ptr;
-
switch (node.operation) {
case Op::Not: {
- auto args = get_arguments<1>(node);
- result_ptr = std::make_shared<json>(!truthy(args[0]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<1>(node);
+ make_result(!truthy(args[0]));
} break;
case Op::And: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) && truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::Or: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) || truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::In: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
} break;
case Op::Equal: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] == *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] == *args[1]);
} break;
case Op::NotEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] != *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] != *args[1]);
} break;
case Op::Greater: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] > *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] > *args[1]);
} break;
case Op::GreaterEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] >= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] >= *args[1]);
} break;
case Op::Less: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] < *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] < *args[1]);
} break;
case Op::LessEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] <= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] <= *args[1]);
} break;
case Op::Add: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_string() && args[1]->is_string()) {
- result_ptr = std::make_shared<json>(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
} else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() + args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() + args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() + args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() + args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Subtract: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() - args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() - args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() - args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() - args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Multiplication: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() * args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() * args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() * args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() * args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Division: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[1]->get<double>() == 0) {
throw_renderer_error("division by zero", node);
}
- result_ptr = std::make_shared<json>(args[0]->get<double>() / args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(args[0]->get<double>() / args[1]->get<double>());
} break;
case Op::Power: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
- int result = std::pow(args[0]->get<int>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
+ make_result(result);
} else {
double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ make_result(result);
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Modulo: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(args[0]->get<int>() % args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(args[0]->get<int>() % args[1]->get<int>());
} break;
case Op::AtId: {
- json_eval_stack.pop(); // Pop id nullptr
- auto container = get_arguments<1, false>(node)[0];
+ const auto container = get_arguments<1, 0, false>(node)[0];
+ node.arguments[1]->accept(*this);
if (not_found_stack.empty()) {
throw_renderer_error("could not find element with given name", node);
}
- auto id_node = not_found_stack.top();
+ const auto id_node = not_found_stack.top();
not_found_stack.pop();
- json_eval_stack.push(&container->at(id_node->name));
+ data_eval_stack.pop();
+ data_eval_stack.push(&container->at(id_node->name));
} break;
case Op::At: {
- auto args = get_arguments<2>(node);
- json_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ const auto args = get_arguments<2>(node);
+ if (args[0]->is_object()) {
+ data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
+ } else {
+ data_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ }
} break;
case Op::Default: {
- auto default_arg = get_arguments<1>(node)[0];
- auto test_arg = get_arguments<1, false>(node)[0];
- json_eval_stack.push(test_arg ? test_arg : default_arg);
+ const auto test_arg = get_arguments<1, 0, false>(node)[0];
+ data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
} break;
case Op::DivisibleBy: {
- auto args = get_arguments<2>(node);
- int divisor = args[1]->get<int>();
- result_ptr = std::make_shared<json>((divisor != 0) && (args[0]->get<int>() % divisor == 0));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int divisor = args[1]->get<int>();
+ make_result((divisor != 0) && (args[0]->get<int>() % divisor == 0));
} break;
case Op::Even: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
} break;
case Op::Exists: {
- auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto&& name = get_arguments<1>(node)[0]->get_ref<const std::string&>();
+ make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
} break;
case Op::ExistsInObject: {
- auto args = get_arguments<2>(node);
- auto &&name = args[1]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(args[0]->find(name) != args[0]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ auto&& name = args[1]->get_ref<const std::string&>();
+ make_result(args[0]->find(name) != args[0]->end());
} break;
case Op::First: {
- auto result = &get_arguments<1>(node)[0]->front();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->front();
+ data_eval_stack.push(result);
} break;
case Op::Float: {
- result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Int: {
- result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Last: {
- auto result = &get_arguments<1>(node)[0]->back();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->back();
+ data_eval_stack.push(result);
} break;
case Op::Length: {
- auto val = get_arguments<1>(node)[0];
+ const auto val = get_arguments<1>(node)[0];
if (val->is_string()) {
- result_ptr = std::make_shared<json>(val->get_ref<const std::string &>().length());
+ make_result(val->get_ref<const std::string&>().length());
} else {
- result_ptr = std::make_shared<json>(val->size());
+ make_result(val->size());
}
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Lower: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Max: {
- auto args = get_arguments<1>(node);
- auto result = std::max_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::max_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Min: {
- auto args = get_arguments<1>(node);
- auto result = std::min_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::min_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Odd: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
} break;
case Op::Range: {
std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
std::iota(result.begin(), result.end(), 0);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Round: {
- auto args = get_arguments<2>(node);
- int precision = args[1]->get<int>();
- double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int precision = args[1]->get<int>();
+ const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
+ if (precision == 0) {
+ make_result(int(result));
+ } else {
+ make_result(result);
+ }
} break;
case Op::Sort: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
+ auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
std::sort(result_ptr->begin(), result_ptr->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
} break;
case Op::Upper: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::IsBoolean: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_boolean());
} break;
case Op::IsNumber: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number());
} break;
case Op::IsInteger: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_integer());
} break;
case Op::IsFloat: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_float());
} break;
case Op::IsObject: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_object());
} break;
case Op::IsArray: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_array());
} break;
case Op::IsString: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_string());
} break;
case Op::Callback: {
- auto args = get_argument_vector(node.number_args, node);
- result_ptr = std::make_shared<json>(node.callback(args));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto args = get_argument_vector(node);
+ make_result(node.callback(args));
+ } break;
+ case Op::Super: {
+ const auto args = get_argument_vector(node);
+ const size_t old_level = current_level;
+ const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
+ const size_t level = current_level + level_diff;
+
+ if (block_statement_stack.empty()) {
+ throw_renderer_error("super() call is not within a block", node);
+ }
+
+ if (level < 1 || level > template_stack.size() - 1) {
+ throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
+ }
+
+ const auto current_block_statement = block_statement_stack.back();
+ const Template* new_template = template_stack.at(level);
+ const Template* old_template = current_template;
+ const auto block_it = new_template->block_storage.find(current_block_statement->name);
+ if (block_it != new_template->block_storage.end()) {
+ current_template = new_template;
+ current_level = level;
+ block_it->second->block.accept(*this);
+ current_level = old_level;
+ current_template = old_template;
+ } else {
+ throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
+ }
+ make_result(nullptr);
+ } break;
+ case Op::Join: {
+ const auto args = get_arguments<2>(node);
+ const auto separator = args[1]->get<std::string>();
+ std::ostringstream os;
+ std::string sep;
+ for (const auto& value : *args[0]) {
+ os << sep;
+ if (value.is_string()) {
+ os << value.get<std::string>(); // otherwise the value is surrounded with ""
+ } else {
+ os << value;
+ }
+ sep = separator;
+ }
+ make_result(os.str());
} break;
case Op::ParenLeft:
case Op::ParenRight:
@@ -478,15 +472,15 @@ class Renderer : public NodeVisitor {
}
void visit(const ExpressionListNode& node) {
- print_json(eval_expression_list(node));
+ print_data(eval_expression_list(node));
}
- void visit(const StatementNode&) { }
+ void visit(const StatementNode&) {}
- void visit(const ForStatementNode&) { }
+ void visit(const ForStatementNode&) {}
void visit(const ForArrayStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_array()) {
throw_renderer_error("object must be an array", node);
}
@@ -500,7 +494,7 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.value)] = *it;
+ additional_data[static_cast<std::string>(node.value)] = *it;
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -515,17 +509,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
- auto tmp = (*current_loop_data)["parent"];
+ const auto tmp = (*current_loop_data)["parent"];
*current_loop_data = std::move(tmp);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const ForObjectStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_object()) {
throw_renderer_error("object must be an object", node);
}
@@ -538,8 +532,8 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.key)] = it.key();
- json_additional_data[static_cast<std::string>(node.value)] = it.value();
+ additional_data[static_cast<std::string>(node.key)] = it.key();
+ additional_data[static_cast<std::string>(node.value)] = it.value();
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -554,17 +548,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.key)].clear();
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.key)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
*current_loop_data = std::move((*current_loop_data)["parent"]);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const IfStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (truthy(result.get())) {
node.true_statement.accept(*this);
} else if (node.has_false_statement) {
@@ -574,35 +568,63 @@ class Renderer : public NodeVisitor {
void visit(const IncludeStatementNode& node) {
auto sub_renderer = Renderer(config, template_storage, function_storage);
- auto included_template_it = template_storage.find(node.file);
-
+ const auto included_template_it = template_storage.find(node.file);
if (included_template_it != template_storage.end()) {
- sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
+ sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
} else if (config.throw_at_missing_includes) {
throw_renderer_error("include '" + node.file + "' not found", node);
}
}
+ void visit(const ExtendsStatementNode& node) {
+ const auto included_template_it = template_storage.find(node.file);
+ if (included_template_it != template_storage.end()) {
+ const Template* parent_template = &included_template_it->second;
+ render_to(*output_stream, *parent_template, *data_input, &additional_data);
+ break_rendering = true;
+ } else if (config.throw_at_missing_includes) {
+ throw_renderer_error("extends '" + node.file + "' not found", node);
+ }
+ }
+
+ void visit(const BlockStatementNode& node) {
+ const size_t old_level = current_level;
+ current_level = 0;
+ current_template = template_stack.front();
+ const auto block_it = current_template->block_storage.find(node.name);
+ if (block_it != current_template->block_storage.end()) {
+ block_statement_stack.emplace_back(&node);
+ block_it->second->block.accept(*this);
+ block_statement_stack.pop_back();
+ }
+ current_level = old_level;
+ current_template = template_stack.back();
+ }
+
void visit(const SetStatementNode& node) {
- json_additional_data[node.key] = *eval_expression_list(node.expression);
+ std::string ptr = node.key;
+ replace_substring(ptr, ".", "/");
+ ptr = "/" + ptr;
+ additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
}
public:
- Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(config), template_storage(template_storage), function_storage(function_storage) { }
+ Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
+ : config(config), template_storage(template_storage), function_storage(function_storage) {}
- void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) {
+ void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
output_stream = &os;
current_template = &tmpl;
- json_input = &data;
+ data_input = &data;
if (loop_data) {
- json_additional_data = *loop_data;
- current_loop_data = &json_additional_data["loop"];
+ additional_data = *loop_data;
+ current_loop_data = &additional_data["loop"];
}
+ template_stack.emplace_back(current_template);
current_template->root.accept(*this);
- json_tmp_stack.clear();
+ data_tmp_stack.clear();
}
};
diff --git a/include/inja/statistics.hpp b/include/inja/statistics.hpp
index 71fc719..da33593 100644
--- a/include/inja/statistics.hpp
+++ b/include/inja/statistics.hpp
@@ -1,11 +1,8 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_STATISTICS_HPP_
#define INCLUDE_INJA_STATISTICS_HPP_
#include "node.hpp"
-
namespace inja {
/*!
@@ -18,24 +15,26 @@ class StatisticsVisitor : public NodeVisitor {
}
}
- void visit(const TextNode&) { }
- void visit(const ExpressionNode&) { }
- void visit(const LiteralNode&) { }
+ void visit(const TextNode&) {}
+ void visit(const ExpressionNode&) {}
+ void visit(const LiteralNode&) {}
- void visit(const JsonNode&) {
+ void visit(const DataNode&) {
variable_counter += 1;
}
- void visit(const FunctionNode&) { }
-
- void visit(const ExpressionListNode& node) {
- for (auto& n : node.rpn_output) {
+ void visit(const FunctionNode& node) {
+ for (auto& n : node.arguments) {
n->accept(*this);
}
}
- void visit(const StatementNode&) { }
- void visit(const ForStatementNode&) { }
+ void visit(const ExpressionListNode& node) {
+ node.root->accept(*this);
+ }
+
+ void visit(const StatementNode&) {}
+ void visit(const ForStatementNode&) {}
void visit(const ForArrayStatementNode& node) {
node.condition.accept(*this);
@@ -53,14 +52,20 @@ class StatisticsVisitor : public NodeVisitor {
node.false_statement.accept(*this);
}
- void visit(const IncludeStatementNode&) { }
+ void visit(const IncludeStatementNode&) {}
+
+ void visit(const ExtendsStatementNode&) {}
+
+ void visit(const BlockStatementNode& node) {
+ node.block.accept(*this);
+ }
- void visit(const SetStatementNode&) { }
+ void visit(const SetStatementNode&) {}
public:
unsigned int variable_counter;
- explicit StatisticsVisitor() : variable_counter(0) { }
+ explicit StatisticsVisitor(): variable_counter(0) {}
};
} // namespace inja
diff --git a/include/inja/string_view.hpp b/include/inja/string_view.hpp
deleted file mode 100644
index 2bb50c9..0000000
--- a/include/inja/string_view.hpp
+++ /dev/null
@@ -1,1416 +0,0 @@
-// Copyright 2017-2019 by Martin Moene
-//
-// string-view lite, a C++17-like string_view for C++98 and later.
-// For more information see https://github.com/martinmoene/string-view-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#pragma once
-
-#ifndef NONSTD_SV_LITE_H_INCLUDED
-#define NONSTD_SV_LITE_H_INCLUDED
-
-#define string_view_lite_MAJOR 1
-#define string_view_lite_MINOR 4
-#define string_view_lite_PATCH 0
-
-#define string_view_lite_VERSION \
- nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \
- string_view_lite_PATCH)
-
-#define nssv_STRINGIFY(x) nssv_STRINGIFY_(x)
-#define nssv_STRINGIFY_(x) #x
-
-// string-view lite configuration:
-
-#define nssv_STRING_VIEW_DEFAULT 0
-#define nssv_STRING_VIEW_NONSTD 1
-#define nssv_STRING_VIEW_STD 2
-
-#if !defined(nssv_CONFIG_SELECT_STRING_VIEW)
-#define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
-#endif
-
-#if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW)
-#error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
-#endif
-
-#ifndef nssv_CONFIG_STD_SV_OPERATOR
-#define nssv_CONFIG_STD_SV_OPERATOR 0
-#endif
-
-#ifndef nssv_CONFIG_USR_SV_OPERATOR
-#define nssv_CONFIG_USR_SV_OPERATOR 1
-#endif
-
-#ifdef nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef nssv_CONFIG_NO_EXCEPTIONS
-#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
-#define nssv_CONFIG_NO_EXCEPTIONS 0
-#else
-#define nssv_CONFIG_NO_EXCEPTIONS 1
-#endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef nssv_CPLUSPLUS
-#if defined(_MSVC_LANG) && !defined(__clang__)
-#define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
-#else
-#define nssv_CPLUSPLUS __cplusplus
-#endif
-#endif
-
-#define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L)
-#define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L)
-#define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L)
-#define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L)
-
-// use C++17 std::string_view if available and requested:
-
-#if nssv_CPP17_OR_GREATER && defined(__has_include)
-#if __has_include(<string_view> )
-#define nssv_HAVE_STD_STRING_VIEW 1
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-
-#define nssv_USES_STD_STRING_VIEW \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW))
-
-#define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW)
-#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
-
-//
-// Use C++17 std::string_view:
-//
-
-#if nssv_USES_STD_STRING_VIEW
-
-#include <string_view>
-
-// Extensions for std::string:
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(std::basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return std::basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-// Literal operators sv and _sv:
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-
-using namespace std::literals::string_view_literals;
-
-#endif
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
-inline namespace literals {
-inline namespace string_view_literals {
-
-constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1)
-{
- return std::string_view {str, len};
-}
-
-constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2)
-{
- return std::u16string_view {str, len};
-}
-
-constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3)
-{
- return std::u32string_view {str, len};
-}
-
-constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4)
-{
- return std::wstring_view {str, len};
-}
-
-} // namespace string_view_literals
-} // namespace literals
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
-
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-using std::basic_string_view;
-using std::string_view;
-using std::u16string_view;
-using std::u32string_view;
-using std::wstring_view;
-
-// literal "sv" and "_sv", see above
-
-using std::operator==;
-using std::operator!=;
-using std::operator<;
-using std::operator<=;
-using std::operator>;
-using std::operator>=;
-
-using std::operator<<;
-
-} // namespace nonstd
-
-#else // nssv_HAVE_STD_STRING_VIEW
-
-//
-// Before C++17: use string_view lite:
-//
-
-// Compiler versions:
-//
-// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
-// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
-// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
-// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
-// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
-// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
-// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
-// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
-// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
-// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
-
-#if defined(_MSC_VER) && !defined(__clang__)
-#define nssv_COMPILER_MSVC_VER (_MSC_VER)
-#define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900)))
-#else
-#define nssv_COMPILER_MSVC_VER 0
-#define nssv_COMPILER_MSVC_VERSION 0
-#endif
-
-#define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch))
-
-#if defined(__clang__)
-#define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-#define nssv_COMPILER_CLANG_VERSION 0
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__)
-#define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-#define nssv_COMPILER_GNUC_VERSION 0
-#endif
-
-// half-open range [lo..hi):
-#define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi))
-
-// Presence of language and library features:
-
-#ifdef _HAS_CPP0X
-#define nssv_HAS_CPP0X _HAS_CPP0X
-#else
-#define nssv_HAS_CPP0X 0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
-
-#if nssv_COMPILER_MSVC_VER >= 1900
-#undef nssv_CPP11_OR_GREATER
-#define nssv_CPP11_OR_GREATER 1
-#endif
-
-#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
-#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
-#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
-#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
-#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
-#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
-
-#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
-#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
-
-// Presence of C++11 language features:
-
-#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
-#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
-#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
-#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
-#define nssv_HAVE_NULLPTR nssv_CPP11_100
-#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
-#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
-#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
-#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
-#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
-
-#if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
-#define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
-#else
-#define nssv_HAVE_STD_DEFINED_LITERALS 0
-#endif
-
-// Presence of C++14 language features:
-
-#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
-
-// Presence of C++17 language features:
-
-#define nssv_HAVE_NODISCARD nssv_CPP17_000
-
-// Presence of C++ library features:
-
-#define nssv_HAVE_STD_HASH nssv_CPP11_120
-
-// C++ feature usage:
-
-#if nssv_HAVE_CONSTEXPR_11
-#define nssv_constexpr constexpr
-#else
-#define nssv_constexpr /*constexpr*/
-#endif
-
-#if nssv_HAVE_CONSTEXPR_14
-#define nssv_constexpr14 constexpr
-#else
-#define nssv_constexpr14 /*constexpr*/
-#endif
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-#define nssv_explicit explicit
-#else
-#define nssv_explicit /*explicit*/
-#endif
-
-#if nssv_HAVE_INLINE_NAMESPACE
-#define nssv_inline_ns inline
-#else
-#define nssv_inline_ns /*inline*/
-#endif
-
-#if nssv_HAVE_NOEXCEPT
-#define nssv_noexcept noexcept
-#else
-#define nssv_noexcept /*noexcept*/
-#endif
-
-//#if nssv_HAVE_REF_QUALIFIER
-//# define nssv_ref_qual &
-//# define nssv_refref_qual &&
-//#else
-//# define nssv_ref_qual /*&*/
-//# define nssv_refref_qual /*&&*/
-//#endif
-
-#if nssv_HAVE_NULLPTR
-#define nssv_nullptr nullptr
-#else
-#define nssv_nullptr NULL
-#endif
-
-#if nssv_HAVE_NODISCARD
-#define nssv_nodiscard [[nodiscard]]
-#else
-#define nssv_nodiscard /*[[nodiscard]]*/
-#endif
-
-// Additional includes:
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <limits>
-#include <ostream>
-#include <string> // std::char_traits<>
-
-#if !nssv_CONFIG_NO_EXCEPTIONS
-#include <stdexcept>
-#endif
-
-#if nssv_CPP11_OR_GREATER
-#include <type_traits>
-#endif
-
-// Clang, GNUC, MSVC warning suppression macros:
-
-#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wuser-defined-literals"
-#elif defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wliteral-suffix"
-#endif // __clang__
-
-#if nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code))
-#define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes))
-#else
-#define nssv_SUPPRESS_MSGSL_WARNING(expr)
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr)
-#define nssv_DISABLE_MSVC_WARNINGS(codes)
-#endif
-
-#if defined(__clang__)
-#define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
-#elif defined(__GNUC__)
-#define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
-#elif nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_RESTORE_WARNINGS() __pragma(warning(pop))
-#else
-#define nssv_RESTORE_WARNINGS()
-#endif
-
-// Suppress the following MSVC (GSL) warnings:
-// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
-// start with an underscore are reserved
-// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
-// use brace initialization, gsl::narrow_cast or gsl::narow
-// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
-
-nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472)
- // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
- // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
-
- namespace nonstd {
- namespace sv_lite {
-
-#if nssv_CPP11_OR_GREATER
-
- namespace detail {
-
- // Expect tail call optimization to make length() non-recursive:
-
- template <typename CharT> inline constexpr std::size_t length(CharT *s, std::size_t result = 0) {
- return *s == '\0' ? result : length(s + 1, result + 1);
- }
-
- } // namespace detail
-
-#endif // nssv_CPP11_OR_GREATER
-
- template <class CharT, class Traits = std::char_traits<CharT>> class basic_string_view;
-
- //
- // basic_string_view:
- //
-
- template <class CharT, class Traits /* = std::char_traits<CharT> */
- >
- class basic_string_view {
- public:
- // Member types:
-
- typedef Traits traits_type;
- typedef CharT value_type;
-
- typedef CharT *pointer;
- typedef CharT const *const_pointer;
- typedef CharT &reference;
- typedef CharT const &const_reference;
-
- typedef const_pointer iterator;
- typedef const_pointer const_iterator;
- typedef std::reverse_iterator<const_iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
-
- // 24.4.2.1 Construction and assignment:
-
- nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {}
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_),
- size_(other.size_) {}
-#endif
-
- nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept
- : data_(s),
- size_(count) {}
-
- nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept
- : data_(s)
-#if nssv_CPP17_OR_GREATER
- ,
- size_(Traits::length(s))
-#elif nssv_CPP11_OR_GREATER
- ,
- size_(detail::length(s))
-#else
- ,
- size_(Traits::length(s))
-#endif
- {
- }
-
- // Assignment:
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept {
- data_ = other.data_;
- size_ = other.size_;
- return *this;
- }
-#endif
-
- // 24.4.2.2 Iterator support:
-
- nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
- nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
-
- nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
- nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
-
- nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); }
- nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); }
-
- nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
- nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
-
- // 24.4.2.3 Capacity:
-
- nssv_constexpr size_type size() const nssv_noexcept { return size_; }
- nssv_constexpr size_type length() const nssv_noexcept { return size_; }
- nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits<size_type>::max)(); }
-
- // since C++20
- nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; }
-
- // 24.4.2.4 Element access:
-
- nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); }
-
- nssv_constexpr14 const_reference at(size_type pos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos < size());
-#else
- if (pos >= size()) {
- throw std::out_of_range("nonstd::string_view::at()");
- }
-#endif
- return data_at(pos);
- }
-
- nssv_constexpr const_reference front() const { return data_at(0); }
- nssv_constexpr const_reference back() const { return data_at(size() - 1); }
-
- nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
-
- // 24.4.2.5 Modifiers:
-
- nssv_constexpr14 void remove_prefix(size_type n) {
- assert(n <= size());
- data_ += n;
- size_ -= n;
- }
-
- nssv_constexpr14 void remove_suffix(size_type n) {
- assert(n <= size());
- size_ -= n;
- }
-
- nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept {
- using std::swap;
- swap(data_, other.data_);
- swap(size_, other.size_);
- }
-
- // 24.4.2.6 String operations:
-
- size_type copy(CharT *dest, size_type n, size_type pos = 0) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::copy()");
- }
-#endif
- const size_type rlen = (std::min)(n, size() - pos);
-
- (void)Traits::copy(dest, data() + pos, rlen);
-
- return rlen;
- }
-
- nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::substr()");
- }
-#endif
- return basic_string_view(data() + pos, (std::min)(n, size() - pos));
- }
-
- // compare(), 6x:
-
- nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1)
- {
- if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) {
- return result;
- }
-
- return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2)
- {
- return substr(pos1, n1).compare(other);
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2,
- size_type n2) const // (3)
- {
- return substr(pos1, n1).compare(other.substr(pos2, n2));
- }
-
- nssv_constexpr int compare(CharT const *s) const // (4)
- {
- return compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5)
- {
- return substr(pos1, n1).compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6)
- {
- return substr(pos1, n1).compare(basic_string_view(s, n2));
- }
-
- // 24.4.2.7 Searching:
-
- // starts_with(), 3x, since C++20:
-
- nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(0, v.size(), v) == 0;
- }
-
- nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2)
- {
- return starts_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool starts_with(CharT const *s) const // (3)
- {
- return starts_with(basic_string_view(s));
- }
-
- // ends_with(), 3x, since C++20:
-
- nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(size() - v.size(), npos, v) == 0;
- }
-
- nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2)
- {
- return ends_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool ends_with(CharT const *s) const // (3)
- {
- return ends_with(basic_string_view(s));
- }
-
- // find(), 4x:
-
- nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return assert(v.size() == 0 || v.data() != nssv_nullptr),
- pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4)
- {
- return find(basic_string_view(s), pos);
- }
-
- // rfind(), 4x:
-
- nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- if (size() < v.size()) {
- return npos;
- }
-
- if (v.empty()) {
- return (std::min)(size(), pos);
- }
-
- const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size();
- const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq);
-
- return result != last ? size_type(result - cbegin()) : npos;
- }
-
- nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return rfind(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return rfind(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4)
- {
- return rfind(basic_string_view(s), pos);
- }
-
- // find_first_of(), 4x:
-
- nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos
- : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find_first_of(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_of(basic_string_view(s), pos);
- }
-
- // find_last_of(), 4x:
-
- nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size() ? find_last_of(v, size() - 1)
- : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(),
- v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_of(basic_string_view(s), pos);
- }
-
- // find_first_not_of(), 4x:
-
- nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_first_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_not_of(basic_string_view(s), pos);
- }
-
- // find_last_not_of(), 4x:
-
- nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size()
- ? find_last_not_of(v, size() - 1)
- : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_not_of(basic_string_view(s), pos);
- }
-
- // Constants:
-
-#if nssv_CPP17_OR_GREATER
- static nssv_constexpr size_type npos = size_type(-1);
-#elif nssv_CPP11_OR_GREATER
- enum : size_type { npos = size_type(-1) };
-#else
- enum { npos = size_type(-1) };
-#endif
-
- private:
- struct not_in_view {
- const basic_string_view v;
-
- nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {}
-
- nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); }
- };
-
- nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); }
-
- nssv_constexpr size_type to_pos(const_reverse_iterator it) const {
- return it == crend() ? npos : size_type(crend() - it - 1);
- }
-
- nssv_constexpr const_reference data_at(size_type pos) const {
-#if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500)
- return data_[pos];
-#else
- return assert(pos < size()), data_[pos];
-#endif
- }
-
- private:
- const_pointer data_;
- size_type size_;
-
- public:
-#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-
- template <class Allocator>
- basic_string_view(std::basic_string<CharT, Traits, Allocator> const &s) nssv_noexcept : data_(s.data()),
- size_(s.size()) {}
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-
- template <class Allocator> explicit operator std::basic_string<CharT, Traits, Allocator>() const {
- return to_string(Allocator());
- }
-
-#endif // nssv_HAVE_EXPLICIT_CONVERSION
-
-#if nssv_CPP11_OR_GREATER
-
- template <class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a = Allocator()) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
-
-#else
-
- std::basic_string<CharT, Traits> to_string() const { return std::basic_string<CharT, Traits>(begin(), end()); }
-
- template <class Allocator> std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
-
-#endif // nssv_CPP11_OR_GREATER
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- };
-
- //
- // Non-member functions:
- //
-
- // 24.4.3 Non-member comparison functions:
- // lexicographically compare two string views (function template):
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- // Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
- // Implementations shall provide sufficient additional overloads marked
- // constexpr and noexcept so that an object t with an implicit conversion
- // to S can be compared according to Table 67.
-
-#if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141)
-
- // accomodate for older compilers:
-
- // ==
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- // !=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() != rhs.size() && lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() != rhs.size() || rhs.compare(lhs) != 0;
- }
-
- // <
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
-
- // <=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
-
- // >
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
-
- // >=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
-
-#else // newer compilers:
-
-#define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay<basic_string_view<T, U>>::type
-
-#if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150)
-#define nssv_MSVC_ORDER(x) , int = x
-#else
-#define nssv_MSVC_ORDER(x) /*, int=x*/
-#endif
-
- // ==
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- // !=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.size() != rhs.size() || lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- // <
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- // <=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- // >
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- // >=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
-#undef nssv_MSVC_ORDER
-#undef nssv_BASIC_STRING_VIEW_I
-
-#endif // compiler-dependent approach to comparisons
-
- // 24.4.4 Inserters and extractors:
-
- namespace detail {
-
- template <class Stream> void write_padding(Stream &os, std::streamsize n) {
- for (std::streamsize i = 0; i < n; ++i)
- os.rdbuf()->sputc(os.fill());
- }
-
- template <class Stream, class View> Stream &write_to_stream(Stream &os, View const &sv) {
- typename Stream::sentry sentry(os);
-
- if (!os)
- return os;
-
- const std::streamsize length = static_cast<std::streamsize>(sv.length());
-
- // Whether, and how, to pad:
- const bool pad = (length < os.width());
- const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right;
-
- if (left_pad)
- write_padding(os, os.width() - length);
-
- // Write span characters:
- os.rdbuf()->sputn(sv.begin(), length);
-
- if (pad && !left_pad)
- write_padding(os, os.width() - length);
-
- // Reset output stream width:
- os.width(0);
-
- return os;
- }
-
- } // namespace detail
-
- template <class CharT, class Traits>
- std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
- basic_string_view<CharT, Traits> sv) {
- return detail::write_to_stream(os, sv);
- }
-
- // Several typedefs for common character types are provided:
-
- typedef basic_string_view<char> string_view;
- typedef basic_string_view<wchar_t> wstring_view;
-#if nssv_HAVE_WCHAR16_T
- typedef basic_string_view<char16_t> u16string_view;
- typedef basic_string_view<char32_t> u32string_view;
-#endif
-
- } // namespace sv_lite
-} // namespace nonstd::sv_lite
-
-//
-// 24.4.6 Suffix for basic_string_view literals:
-//
-
-#if nssv_HAVE_USER_DEFINED_LITERALS
-
-namespace nonstd {
-nssv_inline_ns namespace literals {
- nssv_inline_ns namespace string_view_literals {
-
-#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
- nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
-
-#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
- nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
- }
-}
-} // namespace nonstd
-
-#endif
-
-//
-// Extensions for std::string:
-//
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-namespace sv_lite {
-
-// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
-
-#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#else
-
-template <class CharT, class Traits> std::basic_string<CharT, Traits> to_string(basic_string_view<CharT, Traits> v) {
- return std::basic_string<CharT, Traits>(v.begin(), v.end());
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v, Allocator const &a) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#endif // nssv_CPP11_OR_GREATER
-
-template <class CharT, class Traits, class Allocator>
-basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-} // namespace sv_lite
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-//
-// make types and algorithms available in namespace nonstd:
-//
-
-namespace nonstd {
-
-using sv_lite::basic_string_view;
-using sv_lite::string_view;
-using sv_lite::wstring_view;
-
-#if nssv_HAVE_WCHAR16_T
-using sv_lite::u16string_view;
-#endif
-#if nssv_HAVE_WCHAR32_T
-using sv_lite::u32string_view;
-#endif
-
-// literal "sv"
-
-using sv_lite::operator==;
-using sv_lite::operator!=;
-using sv_lite::operator<;
-using sv_lite::operator<=;
-using sv_lite::operator>;
-using sv_lite::operator>=;
-
-using sv_lite::operator<<;
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-using sv_lite::to_string;
-using sv_lite::to_string_view;
-#endif
-
-} // namespace nonstd
-
-// 24.4.5 Hash support (C++11):
-
-// Note: The hash value of a string view object is equal to the hash value of
-// the corresponding string object.
-
-#if nssv_HAVE_STD_HASH
-
-#include <functional>
-
-namespace std {
-
-template <> struct hash<nonstd::string_view> {
-public:
- std::size_t operator()(nonstd::string_view v) const nssv_noexcept {
- return std::hash<std::string>()(std::string(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::wstring_view> {
-public:
- std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept {
- return std::hash<std::wstring>()(std::wstring(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::u16string_view> {
-public:
- std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept {
- return std::hash<std::u16string>()(std::u16string(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::u32string_view> {
-public:
- std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept {
- return std::hash<std::u32string>()(std::u32string(v.data(), v.size()));
- }
-};
-
-} // namespace std
-
-#endif // nssv_HAVE_STD_HASH
-
-nssv_RESTORE_WARNINGS()
-
-#endif // nssv_HAVE_STD_STRING_VIEW
-#endif // NONSTD_SV_LITE_H_INCLUDED
diff --git a/include/inja/template.hpp b/include/inja/template.hpp
index 9de0a96..a396dd5 100644
--- a/include/inja/template.hpp
+++ b/include/inja/template.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_TEMPLATE_HPP_
#define INCLUDE_INJA_TEMPLATE_HPP_
@@ -11,7 +9,6 @@
#include "node.hpp"
#include "statistics.hpp"
-
namespace inja {
/*!
@@ -20,9 +17,10 @@ namespace inja {
struct Template {
BlockNode root;
std::string content;
+ std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
- explicit Template() { }
- explicit Template(const std::string& content): content(content) { }
+ explicit Template() {}
+ explicit Template(const std::string& content): content(content) {}
/// Return number of variables (total number, not distinct ones) in the template
int count_variables() {
diff --git a/include/inja/token.hpp b/include/inja/token.hpp
index c000138..207e78e 100644
--- a/include/inja/token.hpp
+++ b/include/inja/token.hpp
@@ -1,11 +1,8 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_TOKEN_HPP_
#define INCLUDE_INJA_TOKEN_HPP_
#include <string>
-
-#include "string_view.hpp"
+#include <string_view>
namespace inja {
@@ -50,12 +47,12 @@ struct Token {
Unknown,
Eof,
};
-
+
Kind kind {Kind::Unknown};
- nonstd::string_view text;
+ std::string_view text;
explicit constexpr Token() = default;
- explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {}
+ explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
std::string describe() const {
switch (kind) {
diff --git a/include/inja/utils.hpp b/include/inja/utils.hpp
index fb1736c..4de05fd 100644
--- a/include/inja/utils.hpp
+++ b/include/inja/utils.hpp
@@ -1,53 +1,42 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_UTILS_HPP_
#define INCLUDE_INJA_UTILS_HPP_
#include <algorithm>
#include <fstream>
#include <string>
+#include <string_view>
#include <utility>
#include "exceptions.hpp"
-#include "string_view.hpp"
namespace inja {
-inline void open_file_or_throw(const std::string &path, std::ifstream &file) {
- file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- try {
- file.open(path);
- } catch (const std::ios_base::failure & /*e*/) {
- INJA_THROW(FileError("failed accessing file at '" + path + "'"));
- }
-}
-
namespace string_view {
-inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
+inline std::string_view slice(std::string_view view, size_t start, size_t end) {
start = std::min(start, view.size());
end = std::min(std::max(start, end), view.size());
return view.substr(start, end - start);
}
-inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
+inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
size_t idx = view.find(Separator);
- if (idx == nonstd::string_view::npos) {
- return std::make_pair(view, nonstd::string_view());
+ if (idx == std::string_view::npos) {
+ return std::make_pair(view, std::string_view());
}
- return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
+ return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
}
-inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
+inline bool starts_with(std::string_view view, std::string_view prefix) {
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
}
} // namespace string_view
-inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) {
+inline SourceLocation get_source_location(std::string_view content, size_t pos) {
// Get line and offset position (starts at 1:1)
auto sliced = string_view::slice(content, 0, pos);
std::size_t last_newline = sliced.rfind("\n");
- if (last_newline == nonstd::string_view::npos) {
+ if (last_newline == std::string_view::npos) {
return {1, sliced.length() + 1};
}
@@ -65,6 +54,17 @@ inline SourceLocation get_source_location(nonstd::string_view content, size_t po
return {count_lines + 1, sliced.length() - last_newline};
}
+inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
+ if (f.empty()) {
+ return;
+ }
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+}
+
} // namespace inja
#endif // INCLUDE_INJA_UTILS_HPP_