fix string view reference movement in bytecode
diff --git a/README.md b/README.md
index 746909a..181d710 100644
--- a/README.md
+++ b/README.md
@@ -82,10 +82,10 @@
Environment env_default;
// With global path to template files and where files will be saved
-Environment env_1 = Environment("../path/templates/");
+Environment env_1 {"../path/templates/"};
// With separate input and output path
-Environment env_2 = Environment("../path/templates/", "../path/results/");
+Environment env_2 {"../path/templates/", "../path/results/"};
// Choose between dot notation (like Jinja2) and JSON pointer to access elements
env.set_element_notation(ElementNotation::Dot); // (default) e.g. time.start
diff --git a/include/inja/bytecode.hpp b/include/inja/bytecode.hpp
index 724f0b7..8bb2663 100644
--- a/include/inja/bytecode.hpp
+++ b/include/inja/bytecode.hpp
@@ -116,7 +116,7 @@
uint32_t flags: 2;
json value;
- std::string_view str;
+ std::string str;
Bytecode(): args(0), flags(0) {}
explicit Bytecode(Op op, unsigned int args = 0): op(op), args(args), flags(0) {}
diff --git a/include/inja/lexer.hpp b/include/inja/lexer.hpp
index fee3e9a..cd65738 100644
--- a/include/inja/lexer.hpp
+++ b/include/inja/lexer.hpp
@@ -208,9 +208,13 @@
Token scan_id() {
for (;;) {
- if (m_pos >= m_in.size()) break;
+ if (m_pos >= m_in.size()) {
+ break;
+ }
char ch = m_in[m_pos];
- if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') break;
+ if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
+ break;
+ }
m_pos += 1;
}
return make_token(Token::Kind::Id);
@@ -218,11 +222,14 @@
Token scan_number() {
for (;;) {
- if (m_pos >= m_in.size()) break;
+ if (m_pos >= m_in.size()) {
+ break;
+ }
char ch = m_in[m_pos];
// be very permissive in lexer (we'll catch errors when conversion happens)
- if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-')
+ if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
break;
+ }
m_pos += 1;
}
return make_token(Token::Kind::Number);
@@ -233,12 +240,13 @@
for (;;) {
if (m_pos >= m_in.size()) break;
char ch = m_in[m_pos++];
- if (ch == '\\')
+ if (ch == '\\') {
escape = true;
- else if (!escape && ch == m_in[m_tok_start])
+ } else if (!escape && ch == m_in[m_tok_start]) {
break;
- else
+ } else {
escape = false;
+ }
}
return make_token(Token::Kind::String);
}
diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp
index f4696fa..0ca3e29 100644
--- a/include/inja/parser.hpp
+++ b/include/inja/parser.hpp
@@ -381,12 +381,6 @@
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);
- // parse it only if it's new
- // TemplateStorage::iterator included;
- // bool is_new {true};
- // std::tie(included, is_new) = m_included_templates.emplace(pathname);
- // if (is_new) included->second = parse_template(pathname);
-
Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp
index f3b984d..8f868f7 100644
--- a/include/inja/renderer.hpp
+++ b/include/inja/renderer.hpp
@@ -28,18 +28,20 @@
std::vector<const json*>& get_args(const Bytecode& bc) {
m_tmp_args.clear();
- bool hasImm = ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop);
+ bool has_imm = ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop);
// get args from stack
unsigned int pop_args = bc.args;
- if (hasImm) --pop_args;
+ if (has_imm) {
+ pop_args -= 1;
+ }
for (auto i = std::prev(m_stack.end(), pop_args); i != m_stack.end(); i++) {
m_tmp_args.push_back(&(*i));
}
// get immediate arg
- if (hasImm) {
+ if (has_imm) {
m_tmp_args.push_back(get_imm(bc));
}
@@ -48,9 +50,12 @@
void pop_args(const Bytecode& bc) {
unsigned int popArgs = bc.args;
- if ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop)
- --popArgs;
- for (unsigned int i = 0; i < popArgs; ++i) m_stack.pop_back();
+ if ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop) {
+ popArgs -= 1;
+ }
+ for (unsigned int i = 0; i < popArgs; ++i) {
+ m_stack.pop_back();
+ }
}
const json* get_imm(const Bytecode& bc) {
@@ -169,11 +174,13 @@
const auto& bc = tmpl.bytecodes[i];
switch (bc.op) {
- case Bytecode::Op::Nop:
+ case Bytecode::Op::Nop: {
break;
- case Bytecode::Op::PrintText:
+ }
+ case Bytecode::Op::PrintText: {
os << bc.str;
break;
+ }
case Bytecode::Op::PrintValue: {
const json& val = *get_args(bc)[0];
if (val.is_string())
@@ -184,9 +191,10 @@
pop_args(bc);
break;
}
- case Bytecode::Op::Push:
+ case Bytecode::Op::Push: {
m_stack.emplace_back(*get_imm(bc));
break;
+ }
case Bytecode::Op::Upper: {
auto result = get_args(bc)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
@@ -443,9 +451,10 @@
m_stack.emplace_back(std::move(result));
break;
}
- case Bytecode::Op::Jump:
+ case Bytecode::Op::Jump: {
i = bc.args - 1; // -1 due to ++i in loop
break;
+ }
case Bytecode::Op::ConditionalJump: {
if (!truthy(m_stack.back())) {
i = bc.args - 1; // -1 due to ++i in loop
diff --git a/include/inja/template.hpp b/include/inja/template.hpp
index c40cda3..2e171e1 100644
--- a/include/inja/template.hpp
+++ b/include/inja/template.hpp
@@ -9,29 +9,9 @@
namespace inja {
-class Template {
- friend class Parser;
- friend class Renderer;
-
+struct Template {
std::vector<Bytecode> bytecodes;
std::string content;
-
- public:
- Template() {}
- Template(const Template& oth): bytecodes(oth.bytecodes), content(oth.content) {}
- Template(Template&& oth): bytecodes(std::move(oth.bytecodes)), content(std::move(oth.content)) {}
-
- Template& operator=(const Template& oth) {
- bytecodes = oth.bytecodes;
- content = oth.content;
- return *this;
- }
-
- Template& operator=(Template&& oth) {
- bytecodes = std::move(oth.bytecodes);
- content = std::move(oth.content);
- return *this;
- }
};
using TemplateStorage = std::map<std::string, Template>;
diff --git a/test/unit-files.cpp b/test/unit-files.cpp
index dab55fc..25d3ca4 100644
--- a/test/unit-files.cpp
+++ b/test/unit-files.cpp
@@ -5,26 +5,28 @@
using json = nlohmann::json;
+const std::string test_file_directory {"../test/data/"};
+
TEST_CASE("loading") {
- inja::Environment env = inja::Environment();
+ inja::Environment env;
json data;
data["name"] = "Jeff";
SECTION("Files should be loaded") {
- CHECK( env.load_file("../test/data/simple.txt") == "Hello {{ name }}." );
+ CHECK( env.load_file(test_file_directory + "simple.txt") == "Hello {{ name }}." );
}
SECTION("Files should be rendered") {
- CHECK( env.render_file("../test/data/simple.txt", data) == "Hello Jeff." );
+ CHECK( env.render_file(test_file_directory + "simple.txt", data) == "Hello Jeff." );
}
SECTION("File includes should be rendered") {
- CHECK( env.render_file("../test/data/include.txt", data) == "Answer: Hello Jeff." );
+ CHECK( env.render_file(test_file_directory + "include.txt", data) == "Answer: Hello Jeff." );
}
}
TEST_CASE("complete-files") {
- inja::Environment env = inja::Environment("../test/data/");
+ inja::Environment env {test_file_directory};
for (std::string test_name : {"simple-file", "nested", "nested-line", "html"}) {
SECTION(test_name) {
@@ -34,8 +36,8 @@
}
TEST_CASE("global-path") {
- inja::Environment env = inja::Environment("../test/data/", "./");
- inja::Environment env_result = inja::Environment("./");
+ inja::Environment env {test_file_directory, "./"};
+ inja::Environment env_result {"./"};
json data;
data["name"] = "Jeff";
diff --git a/test/unit-renderer.cpp b/test/unit-renderer.cpp
index 12c223a..e78127a 100644
--- a/test/unit-renderer.cpp
+++ b/test/unit-renderer.cpp
@@ -43,6 +43,7 @@
CHECK( env.render("Hello {{ names.1 }}!", data) == "Hello Seb!" );
CHECK( env.render("Hello {{ brother.name }}!", data) == "Hello Chris!" );
CHECK( env.render("Hello {{ brother.daughter0.name }}!", data) == "Hello Maria!" );
+ CHECK( env.render("{{ \"{{ no_value }}\" }}", data) == "{{ no_value }}" );
CHECK_THROWS_WITH( env.render("{{unknown}}", data), "[inja.exception.render_error] variable 'unknown' not found" );
}
@@ -215,6 +216,7 @@
CHECK( env.render("{{ default(nothing, 0) }}", data) == "0" );
CHECK( env.render("{{ default(name, \"nobody\") }}", data) == "Peter" );
CHECK( env.render("{{ default(surname, \"nobody\") }}", data) == "nobody" );
+ CHECK( env.render("{{ default(surname, \"{{ surname }}\") }}", data) == "{{ surname }}" );
CHECK_THROWS_WITH( env.render("{{ default(surname, lastname) }}", data), "[inja.exception.render_error] variable 'lastname' not found" );
}