MQTTSuite
Loading...
Searching...
No Matches
inja::Parser Class Reference

Class for parsing an inja Template. More...

#include <inja.hpp>

Collaboration diagram for inja::Parser:

Public Member Functions

 Parser (const ParserConfig &parser_config, const LexerConfig &lexer_config, TemplateStorage &template_storage, const FunctionStorage &function_storage)
Template parse (std::string_view input, std::string_view path)
void parse_into_template (Template &tmpl, std::string_view filename)
std::string load_file (const std::string &filename)

Private Types

using Arguments = std::vector<std::shared_ptr<ExpressionNode>>
using OperatorStack = std::stack<std::shared_ptr<FunctionNode>>

Private Member Functions

void throw_parser_error (const std::string &message) const
void get_next_token ()
void get_peek_token ()
void add_literal (Arguments &arguments, const char *content_ptr)
void add_operator (Arguments &arguments, OperatorStack &operator_stack)
void add_to_template_storage (std::string_view path, std::string &template_name)
std::string parse_filename () const
bool parse_expression (Template &tmpl, Token::Kind closing)
std::shared_ptr< ExpressionNodeparse_expression (Template &tmpl)
bool parse_statement (Template &tmpl, Token::Kind closing, std::string_view path)
void parse_into (Template &tmpl, std::string_view path)

Private Attributes

const ParserConfigconfig
Lexer lexer
TemplateStoragetemplate_storage
const FunctionStoragefunction_storage
Token tok
Token peek_tok
bool have_peek_tok {false}
std::string_view literal_start
BlockNodecurrent_block {nullptr}
ExpressionListNodecurrent_expression_list {nullptr}
std::stack< IfStatementNode * > if_statement_stack
std::stack< ForStatementNode * > for_statement_stack
std::stack< BlockStatementNode * > block_statement_stack

Detailed Description

Class for parsing an inja Template.

Definition at line 1539 of file inja.hpp.

Member Typedef Documentation

◆ Arguments

using inja::Parser::Arguments = std::vector<std::shared_ptr<ExpressionNode>>
private

Definition at line 1540 of file inja.hpp.

◆ OperatorStack

using inja::Parser::OperatorStack = std::stack<std::shared_ptr<FunctionNode>>
private

Definition at line 1541 of file inja.hpp.

Constructor & Destructor Documentation

◆ Parser()

inja::Parser::Parser ( const ParserConfig & parser_config,
const LexerConfig & lexer_config,
TemplateStorage & template_storage,
const FunctionStorage & function_storage )
inlineexplicit

Definition at line 2141 of file inja.hpp.

2145 : config(parser_config)
2146 , lexer(lexer_config)
2149 }
TemplateStorage & template_storage
Definition inja.hpp:1546
const FunctionStorage & function_storage
Definition inja.hpp:1547
const ParserConfig & config
Definition inja.hpp:1543
Lexer lexer
Definition inja.hpp:1545

References config, function_storage, inja::Lexer::Lexer(), lexer, and template_storage.

Here is the call graph for this function:

Member Function Documentation

◆ add_literal()

void inja::Parser::add_literal ( Arguments & arguments,
const char * content_ptr )
inlineprivate

Definition at line 1581 of file inja.hpp.

1581 {
1582 std::string_view data_text(literal_start.data(),
1583 static_cast<std::string_view::size_type>(tok.text.data() - literal_start.data()) + tok.text.size());
1584 arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
1585 }
Token tok
Definition inja.hpp:1549
std::string_view literal_start
Definition inja.hpp:1552

References literal_start, inja::Token::text, and tok.

◆ add_operator()

void inja::Parser::add_operator ( Arguments & arguments,
OperatorStack & operator_stack )
inlineprivate

Definition at line 1587 of file inja.hpp.

1587 {
1588 auto function = operator_stack.top();
1589 operator_stack.pop();
1590
1591 if (static_cast<int>(arguments.size()) < function->number_args) {
1592 throw_parser_error("too few arguments");
1593 }
1594
1595 for (int i = 0; i < function->number_args; ++i) {
1596 function->arguments.insert(function->arguments.begin(), arguments.back());
1597 arguments.pop_back();
1598 }
1599 arguments.emplace_back(function);
1600 }
void throw_parser_error(const std::string &message) const
Definition inja.hpp:1561

References inja::FunctionNode::arguments, inja::FunctionNode::number_args, and throw_parser_error().

Here is the call graph for this function:

◆ add_to_template_storage()

void inja::Parser::add_to_template_storage ( std::string_view path,
std::string & template_name )
inlineprivate

Definition at line 1602 of file inja.hpp.

1602 {
1603 if (template_storage.find(template_name) != template_storage.end()) {
1604 return;
1605 }
1606
1607 std::string original_path = static_cast<std::string>(path);
1608 std::string original_name = template_name;
1609
1610 if (config.search_included_templates_in_files) {
1611 // Build the relative path
1612 template_name = original_path + original_name;
1613 if (template_name.compare(0, 2, "./") == 0) {
1614 template_name.erase(0, 2);
1615 }
1616
1617 if (template_storage.find(template_name) == template_storage.end()) {
1618 // Load file
1619 std::ifstream file;
1620 file.open(template_name);
1621 if (!file.fail()) {
1622 std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1623
1624 auto include_template = Template(text);
1625 template_storage.emplace(template_name, include_template);
1626 parse_into_template(template_storage[template_name], template_name);
1627 return;
1628 } else if (!config.include_callback) {
1629 INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
1630 }
1631 }
1632 }
1633
1634 // Try include callback
1635 if (config.include_callback) {
1636 auto include_template = config.include_callback(original_path, original_name);
1637 template_storage.emplace(template_name, include_template);
1638 }
1639 }
void parse_into_template(Template &tmpl, std::string_view filename)
Definition inja.hpp:2157
#define INJA_THROW(exception)
Definition inja.hpp:45

References config, inja::FileError::FileError(), inja::ParserConfig::include_callback, parse_into_template(), inja::ParserConfig::search_included_templates_in_files, inja::Template::Template(), and template_storage.

Referenced by parse_statement().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_next_token()

void inja::Parser::get_next_token ( )
inlineprivate

Definition at line 1565 of file inja.hpp.

1565 {
1566 if (have_peek_tok) {
1567 tok = peek_tok;
1568 have_peek_tok = false;
1569 } else {
1570 tok = lexer.scan();
1571 }
1572 }
Token peek_tok
Definition inja.hpp:1549
bool have_peek_tok
Definition inja.hpp:1550

References have_peek_tok, lexer, peek_tok, inja::Lexer::scan(), and tok.

Referenced by parse_expression(), parse_into(), and parse_statement().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_peek_token()

void inja::Parser::get_peek_token ( )
inlineprivate

Definition at line 1574 of file inja.hpp.

1574 {
1575 if (!have_peek_tok) {
1576 peek_tok = lexer.scan();
1577 have_peek_tok = true;
1578 }
1579 }

References have_peek_tok, lexer, peek_tok, and inja::Lexer::scan().

Referenced by parse_expression().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_file()

std::string inja::Parser::load_file ( const std::string & filename)
inline

Definition at line 2165 of file inja.hpp.

2165 {
2166 std::ifstream file;
2167 file.open(filename);
2168 if (file.fail()) {
2169 INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
2170 }
2171 std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2172 return text;
2173 }

References inja::FileError::FileError().

Referenced by inja::Environment::load_file(), and inja::Environment::parse_template().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse()

Template inja::Parser::parse ( std::string_view input,
std::string_view path )
inline

Definition at line 2151 of file inja.hpp.

2151 {
2152 auto result = Template(static_cast<std::string>(input));
2153 parse_into(result, path);
2154 return result;
2155 }
void parse_into(Template &tmpl, std::string_view path)
Definition inja.hpp:2078

References parse_into(), and inja::Template::Template().

Referenced by inja::Environment::parse().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_expression() [1/2]

std::shared_ptr< ExpressionNode > inja::Parser::parse_expression ( Template & tmpl)
inlineprivate

Definition at line 1659 of file inja.hpp.

1659 {
1660 size_t current_bracket_level{0};
1661 size_t current_brace_level{0};
1662 Arguments arguments;
1663 OperatorStack operator_stack;
1664
1665 while (tok.kind != Token::Kind::Eof) {
1666 // Literals
1667 switch (tok.kind) {
1668 case Token::Kind::String: {
1669 if (current_brace_level == 0 && current_bracket_level == 0) {
1670 literal_start = tok.text;
1671 add_literal(arguments, tmpl.content.c_str());
1672 }
1673 } break;
1674 case Token::Kind::Number: {
1675 if (current_brace_level == 0 && current_bracket_level == 0) {
1676 literal_start = tok.text;
1677 add_literal(arguments, tmpl.content.c_str());
1678 }
1679 } break;
1681 if (current_brace_level == 0 && current_bracket_level == 0) {
1682 literal_start = tok.text;
1683 }
1684 current_bracket_level += 1;
1685 } break;
1687 if (current_brace_level == 0 && current_bracket_level == 0) {
1688 literal_start = tok.text;
1689 }
1690 current_brace_level += 1;
1691 } break;
1693 if (current_bracket_level == 0) {
1694 throw_parser_error("unexpected ']'");
1695 }
1696
1697 current_bracket_level -= 1;
1698 if (current_brace_level == 0 && current_bracket_level == 0) {
1699 add_literal(arguments, tmpl.content.c_str());
1700 }
1701 } break;
1703 if (current_brace_level == 0) {
1704 throw_parser_error("unexpected '}'");
1705 }
1706
1707 current_brace_level -= 1;
1708 if (current_brace_level == 0 && current_bracket_level == 0) {
1709 add_literal(arguments, tmpl.content.c_str());
1710 }
1711 } break;
1712 case Token::Kind::Id: {
1714
1715 // Data Literal
1716 if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
1717 tok.text == static_cast<decltype(tok.text)>("null")) {
1718 if (current_brace_level == 0 && current_bracket_level == 0) {
1719 literal_start = tok.text;
1720 add_literal(arguments, tmpl.content.c_str());
1721 }
1722
1723 // Operator
1724 } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
1725 goto parse_operator;
1726
1727 // Functions
1728 } else if (peek_tok.kind == Token::Kind::LeftParen) {
1729 auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
1731 do {
1733 auto expr = parse_expression(tmpl);
1734 if (!expr) {
1735 break;
1736 }
1737 func->number_args += 1;
1738 func->arguments.emplace_back(expr);
1739 } while (tok.kind == Token::Kind::Comma);
1740 if (tok.kind != Token::Kind::RightParen) {
1741 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1742 }
1743
1744 auto function_data = function_storage.find_function(func->name, func->number_args);
1745 if (function_data.operation == FunctionStorage::Operation::None) {
1746 throw_parser_error("unknown function " + func->name);
1747 }
1748 func->operation = function_data.operation;
1749 if (function_data.operation == FunctionStorage::Operation::Callback) {
1750 func->callback = function_data.callback;
1751 }
1752 arguments.emplace_back(func);
1753
1754 // Variables
1755 } else {
1756 arguments.emplace_back(
1757 std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
1758 }
1759
1760 // Operators
1761 } break;
1762 case Token::Kind::Equal:
1768 case Token::Kind::Plus:
1769 case Token::Kind::Minus:
1770 case Token::Kind::Times:
1771 case Token::Kind::Slash:
1772 case Token::Kind::Power:
1774 case Token::Kind::Dot: {
1775 parse_operator:
1777 switch (tok.kind) {
1778 case Token::Kind::Id: {
1779 if (tok.text == "and") {
1781 } else if (tok.text == "or") {
1783 } else if (tok.text == "in") {
1785 } else if (tok.text == "not") {
1787 } else {
1788 throw_parser_error("unknown operator in parser.");
1789 }
1790 } break;
1791 case Token::Kind::Equal: {
1793 } break;
1794 case Token::Kind::NotEqual: {
1796 } break;
1799 } break;
1802 } break;
1803 case Token::Kind::LessThan: {
1805 } break;
1808 } break;
1809 case Token::Kind::Plus: {
1811 } break;
1812 case Token::Kind::Minus: {
1814 } break;
1815 case Token::Kind::Times: {
1817 } break;
1818 case Token::Kind::Slash: {
1820 } break;
1821 case Token::Kind::Power: {
1823 } break;
1824 case Token::Kind::Percent: {
1826 } break;
1827 case Token::Kind::Dot: {
1829 } break;
1830 default: {
1831 throw_parser_error("unknown operator in parser.");
1832 }
1833 }
1834 auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
1835
1836 while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) ||
1837 (operator_stack.top()->precedence == function_node->precedence &&
1838 function_node->associativity == FunctionNode::Associativity::Left))) {
1839 add_operator(arguments, operator_stack);
1840 }
1841
1842 operator_stack.emplace(function_node);
1843 } break;
1844 case Token::Kind::Comma: {
1845 if (current_brace_level == 0 && current_bracket_level == 0) {
1846 goto break_loop;
1847 }
1848 } break;
1849 case Token::Kind::Colon: {
1850 if (current_brace_level == 0 && current_bracket_level == 0) {
1851 throw_parser_error("unexpected ':'");
1852 }
1853 } break;
1856 auto expr = parse_expression(tmpl);
1857 if (tok.kind != Token::Kind::RightParen) {
1858 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1859 }
1860 if (!expr) {
1861 throw_parser_error("empty expression in parentheses");
1862 }
1863 arguments.emplace_back(expr);
1864 } break;
1865 default:
1866 goto break_loop;
1867 }
1868
1870 }
1871
1872 break_loop:
1873 while (!operator_stack.empty()) {
1874 add_operator(arguments, operator_stack);
1875 }
1876
1877 std::shared_ptr<ExpressionNode> expr;
1878 if (arguments.size() == 1) {
1879 expr = arguments[0];
1880 arguments = {};
1881 } else if (arguments.size() > 1) {
1882 throw_parser_error("malformed expression");
1883 }
1884 return expr;
1885 }
void get_peek_token()
Definition inja.hpp:1574
void add_operator(Arguments &arguments, OperatorStack &operator_stack)
Definition inja.hpp:1587
std::vector< std::shared_ptr< ExpressionNode > > Arguments
Definition inja.hpp:1540
std::stack< std::shared_ptr< FunctionNode > > OperatorStack
Definition inja.hpp:1541
void get_next_token()
Definition inja.hpp:1565
void add_literal(Arguments &arguments, const char *content_ptr)
Definition inja.hpp:1581
bool parse_expression(Template &tmpl, Token::Kind closing)
Definition inja.hpp:1654

References inja::FunctionStorage::Add, inja::FunctionStorage::And, inja::FunctionNode::arguments, inja::FunctionNode::associativity, inja::FunctionStorage::AtId, inja::FunctionStorage::Callback, inja::FunctionNode::callback, inja::FunctionStorage::FunctionData::callback, inja::Token::Colon, inja::Token::Comma, inja::Template::content, inja::Token::describe(), inja::FunctionStorage::Division, inja::Token::Dot, inja::Token::Eof, inja::FunctionStorage::Equal, inja::Token::Equal, inja::FunctionStorage::find_function(), function_storage, get_next_token(), get_peek_token(), inja::FunctionStorage::Greater, inja::FunctionStorage::GreaterEqual, inja::Token::GreaterEqual, inja::Token::GreaterThan, inja::Token::Id, inja::FunctionStorage::In, inja::Token::kind, inja::FunctionNode::Left, inja::Token::LeftBrace, inja::Token::LeftBracket, inja::Token::LeftParen, inja::FunctionStorage::Less, inja::FunctionStorage::LessEqual, inja::Token::LessEqual, inja::Token::LessThan, literal_start, inja::Token::Minus, inja::FunctionStorage::Modulo, inja::FunctionStorage::Multiplication, inja::FunctionNode::name, inja::FunctionStorage::None, inja::FunctionStorage::Not, inja::FunctionStorage::NotEqual, inja::Token::NotEqual, inja::Token::Number, inja::FunctionNode::number_args, inja::FunctionNode::operation, inja::FunctionStorage::FunctionData::operation, inja::FunctionStorage::Or, parse_expression(), peek_tok, inja::Token::Percent, inja::Token::Plus, inja::FunctionStorage::Power, inja::Token::Power, inja::FunctionNode::precedence, inja::Token::RightBrace, inja::Token::RightBracket, inja::Token::RightParen, inja::Token::Slash, inja::Token::String, inja::FunctionStorage::Subtract, inja::Token::text, throw_parser_error(), inja::Token::Times, and tok.

Referenced by parse_expression(), and parse_expression().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_expression() [2/2]

bool inja::Parser::parse_expression ( Template & tmpl,
Token::Kind closing )
inlineprivate

Definition at line 1654 of file inja.hpp.

1654 {
1656 return tok.kind == closing;
1657 }
ExpressionListNode * current_expression_list
Definition inja.hpp:1555

References current_expression_list, inja::Token::kind, parse_expression(), inja::ExpressionListNode::root, and tok.

Referenced by parse_into(), and parse_statement().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_filename()

std::string inja::Parser::parse_filename ( ) const
inlineprivate

Definition at line 1641 of file inja.hpp.

1641 {
1642 if (tok.kind != Token::Kind::String) {
1643 throw_parser_error("expected string, got '" + tok.describe() + "'");
1644 }
1645
1646 if (tok.text.length() < 2) {
1647 throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
1648 }
1649
1650 // Remove first and last character ""
1651 return std::string{tok.text.substr(1, tok.text.length() - 2)};
1652 }

References inja::Token::describe(), inja::Token::kind, inja::Token::String, inja::Token::text, throw_parser_error(), and tok.

Referenced by parse_statement().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_into()

void inja::Parser::parse_into ( Template & tmpl,
std::string_view path )
inlineprivate

Definition at line 2078 of file inja.hpp.

2078 {
2079 lexer.start(tmpl.content);
2080 current_block = &tmpl.root;
2081
2082 for (;;) {
2084 switch (tok.kind) {
2085 case Token::Kind::Eof: {
2086 if (!if_statement_stack.empty()) {
2087 throw_parser_error("unmatched if");
2088 }
2089 if (!for_statement_stack.empty()) {
2090 throw_parser_error("unmatched for");
2091 }
2092 }
2093 return;
2094 case Token::Kind::Text: {
2095 current_block->nodes.emplace_back(
2096 std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
2097 } break;
2100 if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) {
2101 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2102 }
2103 if (tok.kind != Token::Kind::StatementClose) {
2104 throw_parser_error("expected statement close, got '" + tok.describe() + "'");
2105 }
2106 } break;
2110 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2111 }
2113 throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
2114 }
2115 } break;
2118
2119 auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
2120 current_block->nodes.emplace_back(expression_list_node);
2121 current_expression_list = expression_list_node.get();
2122
2124 throw_parser_error("expected expression close, got '" + tok.describe() + "'");
2125 }
2126 } break;
2129 if (tok.kind != Token::Kind::CommentClose) {
2130 throw_parser_error("expected comment close, got '" + tok.describe() + "'");
2131 }
2132 } break;
2133 default: {
2134 throw_parser_error("unexpected token '" + tok.describe() + "'");
2135 } break;
2136 }
2137 }
2138 }
std::stack< ForStatementNode * > for_statement_stack
Definition inja.hpp:1558
bool parse_statement(Template &tmpl, Token::Kind closing, std::string_view path)
Definition inja.hpp:1887
std::stack< IfStatementNode * > if_statement_stack
Definition inja.hpp:1557
BlockNode * current_block
Definition inja.hpp:1554

References inja::Token::CommentClose, inja::Token::CommentOpen, inja::Template::content, current_block, current_expression_list, inja::Token::describe(), inja::Token::Eof, inja::Token::ExpressionClose, inja::Token::ExpressionOpen, for_statement_stack, get_next_token(), if_statement_stack, inja::Token::kind, lexer, inja::Token::LineStatementClose, inja::Token::LineStatementOpen, inja::BlockNode::nodes, parse_expression(), parse_statement(), inja::Template::root, inja::Lexer::start(), inja::Token::StatementClose, inja::Token::StatementOpen, inja::Token::Text, inja::Token::text, throw_parser_error(), and tok.

Referenced by parse(), and parse_into_template().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_into_template()

void inja::Parser::parse_into_template ( Template & tmpl,
std::string_view filename )
inline

Definition at line 2157 of file inja.hpp.

2157 {
2158 std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
2159
2160 // StringRef path = sys::path::parent_path(filename);
2161 auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
2162 sub_parser.parse_into(tmpl, path);
2163 }
Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config, TemplateStorage &template_storage, const FunctionStorage &function_storage)
Definition inja.hpp:2141

References config, function_storage, inja::Lexer::get_config(), lexer, parse_into(), and template_storage.

Referenced by add_to_template_storage(), and inja::Environment::parse_template().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_statement()

bool inja::Parser::parse_statement ( Template & tmpl,
Token::Kind closing,
std::string_view path )
inlineprivate

Definition at line 1887 of file inja.hpp.

1887 {
1888 if (tok.kind != Token::Kind::Id) {
1889 return false;
1890 }
1891
1892 if (tok.text == static_cast<decltype(tok.text)>("if")) {
1894
1895 auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
1896 current_block->nodes.emplace_back(if_statement_node);
1897 if_statement_stack.emplace(if_statement_node.get());
1898 current_block = &if_statement_node->true_statement;
1899 current_expression_list = &if_statement_node->condition;
1900
1901 if (!parse_expression(tmpl, closing)) {
1902 return false;
1903 }
1904 } else if (tok.text == static_cast<decltype(tok.text)>("else")) {
1905 if (if_statement_stack.empty()) {
1906 throw_parser_error("else without matching if");
1907 }
1908 auto& if_statement_data = if_statement_stack.top();
1910
1911 if_statement_data->has_false_statement = true;
1912 current_block = &if_statement_data->false_statement;
1913
1914 // Chained else if
1915 if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
1917
1918 auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
1919 current_block->nodes.emplace_back(if_statement_node);
1920 if_statement_stack.emplace(if_statement_node.get());
1921 current_block = &if_statement_node->true_statement;
1922 current_expression_list = &if_statement_node->condition;
1923
1924 if (!parse_expression(tmpl, closing)) {
1925 return false;
1926 }
1927 }
1928 } else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
1929 if (if_statement_stack.empty()) {
1930 throw_parser_error("endif without matching if");
1931 }
1932
1933 // Nested if statements
1934 while (if_statement_stack.top()->is_nested) {
1935 if_statement_stack.pop();
1936 }
1937
1938 auto& if_statement_data = if_statement_stack.top();
1940
1941 current_block = if_statement_data->parent;
1942 if_statement_stack.pop();
1943 } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
1945
1946 if (tok.kind != Token::Kind::Id) {
1947 throw_parser_error("expected block name, got '" + tok.describe() + "'");
1948 }
1949
1950 const std::string block_name = static_cast<std::string>(tok.text);
1951
1952 auto block_statement_node =
1953 std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
1954 current_block->nodes.emplace_back(block_statement_node);
1955 block_statement_stack.emplace(block_statement_node.get());
1956 current_block = &block_statement_node->block;
1957 auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
1958 if (!success.second) {
1959 throw_parser_error("block with the name '" + block_name + "' does already exist");
1960 }
1961
1963 } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
1964 if (block_statement_stack.empty()) {
1965 throw_parser_error("endblock without matching block");
1966 }
1967
1968 auto& block_statement_data = block_statement_stack.top();
1970
1971 current_block = block_statement_data->parent;
1973 } else if (tok.text == static_cast<decltype(tok.text)>("for")) {
1975
1976 // options: for a in arr; for a, b in obj
1977 if (tok.kind != Token::Kind::Id) {
1978 throw_parser_error("expected id, got '" + tok.describe() + "'");
1979 }
1980
1981 Token value_token = tok;
1983
1984 // Object type
1985 std::shared_ptr<ForStatementNode> for_statement_node;
1986 if (tok.kind == Token::Kind::Comma) {
1988 if (tok.kind != Token::Kind::Id) {
1989 throw_parser_error("expected id, got '" + tok.describe() + "'");
1990 }
1991
1992 Token key_token = std::move(value_token);
1993 value_token = tok;
1995
1996 for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text),
1997 static_cast<std::string>(value_token.text),
1999 tok.text.data() - tmpl.content.c_str());
2000
2001 // Array type
2002 } else {
2003 for_statement_node = std::make_shared<ForArrayStatementNode>(
2004 static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
2005 }
2006
2007 current_block->nodes.emplace_back(for_statement_node);
2008 for_statement_stack.emplace(for_statement_node.get());
2009 current_block = &for_statement_node->body;
2010 current_expression_list = &for_statement_node->condition;
2011
2012 if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
2013 throw_parser_error("expected 'in', got '" + tok.describe() + "'");
2014 }
2016
2017 if (!parse_expression(tmpl, closing)) {
2018 return false;
2019 }
2020 } else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
2021 if (for_statement_stack.empty()) {
2022 throw_parser_error("endfor without matching for");
2023 }
2024
2025 auto& for_statement_data = for_statement_stack.top();
2027
2028 current_block = for_statement_data->parent;
2029 for_statement_stack.pop();
2030 } else if (tok.text == static_cast<decltype(tok.text)>("include")) {
2032
2033 std::string template_name = parse_filename();
2034 add_to_template_storage(path, template_name);
2035
2036 current_block->nodes.emplace_back(
2037 std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2038
2040 } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
2042
2043 std::string template_name = parse_filename();
2044 add_to_template_storage(path, template_name);
2045
2046 current_block->nodes.emplace_back(
2047 std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2048
2050 } else if (tok.text == static_cast<decltype(tok.text)>("set")) {
2052
2053 if (tok.kind != Token::Kind::Id) {
2054 throw_parser_error("expected variable name, got '" + tok.describe() + "'");
2055 }
2056
2057 std::string key = static_cast<std::string>(tok.text);
2059
2060 auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
2061 current_block->nodes.emplace_back(set_statement_node);
2062 current_expression_list = &set_statement_node->expression;
2063
2064 if (tok.text != static_cast<decltype(tok.text)>("=")) {
2065 throw_parser_error("expected '=', got '" + tok.describe() + "'");
2066 }
2068
2069 if (!parse_expression(tmpl, closing)) {
2070 return false;
2071 }
2072 } else {
2073 return false;
2074 }
2075 return true;
2076 }
std::string parse_filename() const
Definition inja.hpp:1641
std::stack< BlockStatementNode * > block_statement_stack
Definition inja.hpp:1559
void add_to_template_storage(std::string_view path, std::string &template_name)
Definition inja.hpp:1602

References add_to_template_storage(), inja::BlockStatementNode::block, block_statement_stack, inja::Template::block_storage, inja::ForStatementNode::body, inja::Token::Comma, inja::ForStatementNode::condition, inja::IfStatementNode::condition, inja::Template::content, current_block, current_expression_list, inja::Token::describe(), inja::SetStatementNode::expression, inja::IfStatementNode::false_statement, for_statement_stack, get_next_token(), inja::IfStatementNode::has_false_statement, inja::Token::Id, if_statement_stack, inja::IfStatementNode::is_nested, inja::Token::kind, inja::BlockNode::nodes, inja::BlockStatementNode::parent, inja::ForStatementNode::parent, inja::IfStatementNode::parent, parse_expression(), parse_filename(), inja::Token::text, throw_parser_error(), tok, and inja::IfStatementNode::true_statement.

Referenced by parse_into().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ throw_parser_error()

void inja::Parser::throw_parser_error ( const std::string & message) const
inlineprivate

Definition at line 1561 of file inja.hpp.

1561 {
1562 INJA_THROW(ParserError(message, lexer.current_position()));
1563 }

References inja::Lexer::current_position(), lexer, and inja::ParserError::ParserError().

Referenced by add_operator(), parse_expression(), parse_filename(), parse_into(), and parse_statement().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ block_statement_stack

std::stack<BlockStatementNode*> inja::Parser::block_statement_stack
private

Definition at line 1559 of file inja.hpp.

Referenced by parse_statement().

◆ config

const ParserConfig& inja::Parser::config
private

Definition at line 1543 of file inja.hpp.

Referenced by add_to_template_storage(), parse_into_template(), and Parser().

◆ current_block

BlockNode* inja::Parser::current_block {nullptr}
private

Definition at line 1554 of file inja.hpp.

1554{nullptr};

Referenced by parse_into(), and parse_statement().

◆ current_expression_list

ExpressionListNode* inja::Parser::current_expression_list {nullptr}
private

Definition at line 1555 of file inja.hpp.

1555{nullptr};

Referenced by parse_expression(), parse_into(), and parse_statement().

◆ for_statement_stack

std::stack<ForStatementNode*> inja::Parser::for_statement_stack
private

Definition at line 1558 of file inja.hpp.

Referenced by parse_into(), and parse_statement().

◆ function_storage

const FunctionStorage& inja::Parser::function_storage
private

Definition at line 1547 of file inja.hpp.

Referenced by parse_expression(), parse_into_template(), and Parser().

◆ have_peek_tok

bool inja::Parser::have_peek_tok {false}
private

Definition at line 1550 of file inja.hpp.

1550{false};

Referenced by get_next_token(), and get_peek_token().

◆ if_statement_stack

std::stack<IfStatementNode*> inja::Parser::if_statement_stack
private

Definition at line 1557 of file inja.hpp.

Referenced by parse_into(), and parse_statement().

◆ lexer

Lexer inja::Parser::lexer
private

◆ literal_start

std::string_view inja::Parser::literal_start
private

Definition at line 1552 of file inja.hpp.

Referenced by add_literal(), and parse_expression().

◆ peek_tok

Token inja::Parser::peek_tok
private

Definition at line 1549 of file inja.hpp.

Referenced by get_next_token(), get_peek_token(), and parse_expression().

◆ template_storage

TemplateStorage& inja::Parser::template_storage
private

Definition at line 1546 of file inja.hpp.

Referenced by add_to_template_storage(), parse_into_template(), and Parser().

◆ tok

Token inja::Parser::tok
private

The documentation for this class was generated from the following file: