diff --git a/.gitignore b/.gitignore index c0a0221..060ca13 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ dist doc/html doc/latex + +examples diff --git a/include/inja/bytecode.hpp b/include/inja/bytecode.hpp index b1f2252..51f2fa7 100644 --- a/include/inja/bytecode.hpp +++ b/include/inja/bytecode.hpp @@ -90,8 +90,7 @@ struct Bytecode { // start loop // value popped off stack is what is iterated over - // args is index of bytecode after end loop (jumped to if iterable is - // empty) + // args is index of bytecode after end loop (jumped to if iterable is empty) // immediate value is key name (for maps) // str is value name StartLoop, diff --git a/include/inja/environment.hpp b/include/inja/environment.hpp index 6a326b3..79ba7fa 100644 --- a/include/inja/environment.hpp +++ b/include/inja/environment.hpp @@ -13,7 +13,6 @@ #include "config.hpp" #include "function_storage.hpp" #include "parser.hpp" -#include "polyfill.hpp" #include "renderer.hpp" #include "string_view.hpp" #include "template.hpp" diff --git a/include/inja/lexer.hpp b/include/inja/lexer.hpp index 0cf64a8..961fda5 100644 --- a/include/inja/lexer.hpp +++ b/include/inja/lexer.hpp @@ -67,9 +67,10 @@ public: m_tok_start = m_pos; again: - if (m_tok_start >= m_in.size()) + if (m_tok_start >= m_in.size()) { return make_token(Token::Kind::Eof); - + } + switch (m_state) { default: case State::Text: { @@ -159,8 +160,9 @@ private: Token scan_body(nonstd::string_view close, Token::Kind closeKind, bool trim = false) { again: // skip whitespace (except for \n as it might be a close) - if (m_tok_start >= m_in.size()) + if (m_tok_start >= m_in.size()) { return make_token(Token::Kind::Eof); + } char ch = m_in[m_tok_start]; if (ch == ' ' || ch == '\t' || ch == '\r') { m_tok_start += 1; diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp index cf4022f..42b4be9 100644 --- a/include/inja/parser.hpp +++ b/include/inja/parser.hpp @@ -74,25 +74,31 @@ public: m_static(ParserStatic::get_instance()) {} bool parse_expression(Template &tmpl) { - if (!parse_expression_and(tmpl)) + if (!parse_expression_and(tmpl)) { return false; - if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("or")) + } + if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("or")) { return true; + } get_next_token(); - if (!parse_expression_and(tmpl)) + if (!parse_expression_and(tmpl)) { return false; + } append_function(tmpl, Bytecode::Op::Or, 2); return true; } bool parse_expression_and(Template &tmpl) { - if (!parse_expression_not(tmpl)) + if (!parse_expression_not(tmpl)) { return false; - if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("and")) + } + if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("and")) { return true; + } get_next_token(); - if (!parse_expression_not(tmpl)) + if (!parse_expression_not(tmpl)) { return false; + } append_function(tmpl, Bytecode::Op::And, 2); return true; } @@ -100,8 +106,9 @@ public: bool parse_expression_not(Template &tmpl) { if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast("not")) { get_next_token(); - if (!parse_expression_not(tmpl)) + if (!parse_expression_not(tmpl)) { return false; + } append_function(tmpl, Bytecode::Op::Not, 1); return true; } else { @@ -110,8 +117,9 @@ public: } bool parse_expression_comparison(Template &tmpl) { - if (!parse_expression_datum(tmpl)) + if (!parse_expression_datum(tmpl)) { return false; + } Bytecode::Op op; switch (m_tok.kind) { case Token::Kind::Id: @@ -142,8 +150,9 @@ public: return true; } get_next_token(); - if (!parse_expression_datum(tmpl)) + if (!parse_expression_datum(tmpl)) { return false; + } append_function(tmpl, op, 2); return true; } @@ -157,8 +166,9 @@ public: switch (m_tok.kind) { case Token::Kind::LeftParen: { get_next_token(); - if (!parse_expression(tmpl)) + if (!parse_expression(tmpl)) { return false; + } if (m_tok.kind != Token::Kind::RightParen) { throw_parser_error("unmatched '('"); } @@ -197,8 +207,9 @@ public: if (op != Bytecode::Op::Nop) { // swap arguments for default(); see comment in RenderTo() - if (op == Bytecode::Op::Default) + if (op == Bytecode::Op::Default) { std::swap(tmpl.bytecodes.back(), *(tmpl.bytecodes.rbegin() + 1)); + } append_function(tmpl, op, num_args); return true; } else { @@ -252,17 +263,19 @@ public: if (bracket_level == 0) { throw_parser_error("unexpected ']'"); } - --bracket_level; - if (brace_level == 0 && bracket_level == 0) + bracket_level -= 1; + if (brace_level == 0 && bracket_level == 0) { goto returnJson; + } break; case Token::Kind::RightBrace: if (brace_level == 0) { throw_parser_error("unexpected '}'"); } - --brace_level; - if (brace_level == 0 && bracket_level == 0) + brace_level -= 1; + if (brace_level == 0 && bracket_level == 0) { goto returnJson; + } break; default: if (brace_level != 0) { @@ -286,15 +299,17 @@ public: } bool parse_statement(Template &tmpl, nonstd::string_view path) { - if (m_tok.kind != Token::Kind::Id) + if (m_tok.kind != Token::Kind::Id) { return false; + } if (m_tok.text == static_cast("if")) { get_next_token(); // evaluate expression - if (!parse_expression(tmpl)) + if (!parse_expression(tmpl)) { return false; + } // start a new if block on if stack m_if_stack.emplace_back(static_cast(tmpl.bytecodes.size())); @@ -321,8 +336,9 @@ public: // pop if stack m_if_stack.pop_back(); } else if (m_tok.text == static_cast("else")) { - if (m_if_stack.empty()) + if (m_if_stack.empty()) { throw_parser_error("else without matching if"); + } auto &if_data = m_if_stack.back(); get_next_token(); @@ -340,8 +356,9 @@ public: get_next_token(); // evaluate expression - if (!parse_expression(tmpl)) + if (!parse_expression(tmpl)) { return false; + } // update "previous jump" if_data.prev_cond_jump = tmpl.bytecodes.size(); @@ -353,27 +370,31 @@ public: get_next_token(); // options: for a in arr; for a, b in obj - if (m_tok.kind != Token::Kind::Id) + if (m_tok.kind != Token::Kind::Id) { throw_parser_error("expected id, got '" + m_tok.describe() + "'"); + } Token value_token = m_tok; get_next_token(); Token key_token; if (m_tok.kind == Token::Kind::Comma) { get_next_token(); - if (m_tok.kind != Token::Kind::Id) + if (m_tok.kind != Token::Kind::Id) { throw_parser_error("expected id, got '" + m_tok.describe() + "'"); + } key_token = std::move(value_token); value_token = m_tok; get_next_token(); } - if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("in")) + if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("in")) { throw_parser_error("expected 'in', got '" + m_tok.describe() + "'"); + } get_next_token(); - if (!parse_expression(tmpl)) + if (!parse_expression(tmpl)) { return false; + } m_loop_stack.push_back(tmpl.bytecodes.size()); @@ -464,10 +485,12 @@ public: get_next_token(); switch (m_tok.kind) { case Token::Kind::Eof: - if (!m_if_stack.empty()) + if (!m_if_stack.empty()) { throw_parser_error("unmatched if"); - if (!m_loop_stack.empty()) + } + if (!m_loop_stack.empty()) { throw_parser_error("unmatched for"); + } return; case Token::Kind::Text: tmpl.bytecodes.emplace_back(Bytecode::Op::PrintText, m_tok.text, 0u); diff --git a/include/inja/polyfill.hpp b/include/inja/polyfill.hpp deleted file mode 100644 index c0da581..0000000 --- a/include/inja/polyfill.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_POLYFILL_HPP_ -#define INCLUDE_INJA_POLYFILL_HPP_ - -#if __cplusplus < 201402L - -#include -#include -#include -#include - -namespace stdinja { - -template struct _Unique_if { typedef std::unique_ptr _Single_object; }; - -template struct _Unique_if { typedef std::unique_ptr _Unknown_bound; }; - -template struct _Unique_if { typedef void _Known_bound; }; - -template typename _Unique_if::_Single_object make_unique(Args &&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -template typename _Unique_if::_Unknown_bound make_unique(size_t n) { - typedef typename std::remove_extent::type U; - return std::unique_ptr(new U[n]()); -} - -template typename _Unique_if::_Known_bound make_unique(Args &&...) = delete; - -} // namespace stdinja - -#else - -namespace stdinja = std; - -#endif // memory */ - -#endif // INCLUDE_INJA_POLYFILL_HPP_ diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp index 046ab48..82cec1f 100644 --- a/include/inja/renderer.hpp +++ b/include/inja/renderer.hpp @@ -83,6 +83,7 @@ class Renderer { ptr = ptr_buffer; break; } + json::json_pointer json_ptr(ptr.data()); try { // first try to evaluate as a loop variable @@ -98,6 +99,7 @@ class Renderer { m_tmp_val = callback(arguments); return &m_tmp_val; } + throw RenderError("variable '" + static_cast(bc.str) + "' not found"); return nullptr; } diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp index 6e1b633..4ef137e 100644 --- a/single_include/inja/inja.hpp +++ b/single_include/inja/inja.hpp @@ -2849,48 +2849,6 @@ private: #endif // INCLUDE_INJA_PARSER_HPP_ -// #include "polyfill.hpp" -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_POLYFILL_HPP_ -#define INCLUDE_INJA_POLYFILL_HPP_ - -#if __cplusplus < 201402L - -#include -#include -#include -#include - -namespace stdinja { - -template struct _Unique_if { typedef std::unique_ptr _Single_object; }; - -template struct _Unique_if { typedef std::unique_ptr _Unknown_bound; }; - -template struct _Unique_if { typedef void _Known_bound; }; - -template typename _Unique_if::_Single_object make_unique(Args &&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -template typename _Unique_if::_Unknown_bound make_unique(size_t n) { - typedef typename std::remove_extent::type U; - return std::unique_ptr(new U[n]()); -} - -template typename _Unique_if::_Known_bound make_unique(Args &&...) = delete; - -} // namespace stdinja - -#else - -namespace stdinja = std; - -#endif // memory */ - -#endif // INCLUDE_INJA_POLYFILL_HPP_ - // #include "renderer.hpp" // Copyright (c) 2019 Pantor. All rights reserved.