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, const std::filesystem::path &path)
void parse_into_template (Template &tmpl, const std::filesystem::path &filename)

Static Public Member Functions

static std::string load_file (const std::filesystem::path &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 (const std::filesystem::path &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, const std::filesystem::path &path)
void parse_into (Template &tmpl, const std::filesystem::path &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 1579 of file inja.hpp.

Member Typedef Documentation

◆ Arguments

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

Definition at line 1580 of file inja.hpp.

◆ OperatorStack

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

Definition at line 1581 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 2224 of file inja.hpp.

2228 : config(parser_config)
2229 , lexer(lexer_config)
2232 }
TemplateStorage & template_storage
Definition inja.hpp:1586
const FunctionStorage & function_storage
Definition inja.hpp:1587
const ParserConfig & config
Definition inja.hpp:1583
Lexer lexer
Definition inja.hpp:1585

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

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

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

Member Function Documentation

◆ add_literal()

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

Definition at line 1621 of file inja.hpp.

1621 {
1622 const char* begin = literal_start.data();
1623 const char* end = tok.text.data() + tok.text.size();
1624
1625 const std::string_view data_text(begin, static_cast<std::size_t>(end - begin));
1626 arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
1627 }
Token tok
Definition inja.hpp:1589
std::string_view literal_start
Definition inja.hpp:1592

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

Referenced by parse_expression().

Here is the caller graph for this function:

◆ add_operator()

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

Definition at line 1629 of file inja.hpp.

1629 {
1630 auto function = operator_stack.top();
1631 operator_stack.pop();
1632
1633 if (static_cast<int>(arguments.size()) < function->number_args) {
1634 throw_parser_error("too few arguments");
1635 }
1636
1637 for (int i = 0; i < function->number_args; ++i) {
1638 function->arguments.insert(function->arguments.begin(), arguments.back());
1639 arguments.pop_back();
1640 }
1641 arguments.emplace_back(function);
1642 }
void throw_parser_error(const std::string &message) const
Definition inja.hpp:1601

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

Referenced by parse_expression().

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

◆ add_to_template_storage()

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

Definition at line 1644 of file inja.hpp.

1644 {
1645 if (template_storage.find(template_name) != template_storage.end()) {
1646 return;
1647 }
1648
1649 const std::string original_name = template_name;
1650
1651 if (config.search_included_templates_in_files) {
1652 // Build the relative path
1653 template_name = (path / original_name).string();
1654 if (template_name.compare(0, 2, "./") == 0) {
1655 template_name.erase(0, 2);
1656 }
1657
1658 if (template_storage.find(template_name) == template_storage.end()) {
1659 // Load file
1660 std::ifstream file;
1661 file.open(template_name);
1662 if (!file.fail()) {
1663 const std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1664
1665 auto include_template = Template(text);
1666 template_storage.emplace(template_name, include_template);
1667 parse_into_template(template_storage[template_name], template_name);
1668 return;
1669 } else if (!config.include_callback) {
1670 INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
1671 }
1672 }
1673 }
1674
1675 // Try include callback
1676 if (config.include_callback) {
1677 auto include_template = config.include_callback(path, original_name);
1678 template_storage.emplace(template_name, include_template);
1679 }
1680 }
void parse_into_template(Template &tmpl, const std::filesystem::path &filename)
Definition inja.hpp:2240
#define INJA_THROW(exception)
Definition inja.hpp:55

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 1605 of file inja.hpp.

1605 {
1606 if (have_peek_tok) {
1607 tok = peek_tok;
1608 have_peek_tok = false;
1609 } else {
1610 tok = lexer.scan();
1611 }
1612 }
Token peek_tok
Definition inja.hpp:1589
bool have_peek_tok
Definition inja.hpp:1590

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 1614 of file inja.hpp.

1614 {
1615 if (!have_peek_tok) {
1616 peek_tok = lexer.scan();
1617 have_peek_tok = true;
1618 }
1619 }

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::filesystem::path & filename)
inlinestatic

Definition at line 2245 of file inja.hpp.

2245 {
2246 std::ifstream file;
2247 file.open(filename);
2248 if (file.fail()) {
2249 INJA_THROW(FileError("failed accessing file at '" + filename.string() + "'"));
2250 }
2251 std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2252 return text;
2253 }

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,
const std::filesystem::path & path )
inline

Definition at line 2234 of file inja.hpp.

2234 {
2235 auto result = Template(std::string(input));
2236 parse_into(result, path);
2237 return result;
2238 }
void parse_into(Template &tmpl, const std::filesystem::path &path)
Definition inja.hpp:2160

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 1700 of file inja.hpp.

1700 {
1701 size_t current_bracket_level{0};
1702 size_t current_brace_level{0};
1703 Arguments arguments;
1704 OperatorStack operator_stack;
1705
1706 while (tok.kind != Token::Kind::Eof) {
1707 // Literals
1708 switch (tok.kind) {
1709 case Token::Kind::String: {
1710 if (current_brace_level == 0 && current_bracket_level == 0) {
1711 literal_start = tok.text;
1712 add_literal(arguments, tmpl.content.c_str());
1713 }
1714 } break;
1715 case Token::Kind::Number: {
1716 if (current_brace_level == 0 && current_bracket_level == 0) {
1717 literal_start = tok.text;
1718 add_literal(arguments, tmpl.content.c_str());
1719 }
1720 } break;
1722 if (current_brace_level == 0 && current_bracket_level == 0) {
1723 literal_start = tok.text;
1724 }
1725 current_bracket_level += 1;
1726 } break;
1728 if (current_brace_level == 0 && current_bracket_level == 0) {
1729 literal_start = tok.text;
1730 }
1731 current_brace_level += 1;
1732 } break;
1734 if (current_bracket_level == 0) {
1735 throw_parser_error("unexpected ']'");
1736 }
1737
1738 current_bracket_level -= 1;
1739 if (current_brace_level == 0 && current_bracket_level == 0) {
1740 add_literal(arguments, tmpl.content.c_str());
1741 }
1742 } break;
1744 if (current_brace_level == 0) {
1745 throw_parser_error("unexpected '}'");
1746 }
1747
1748 current_brace_level -= 1;
1749 if (current_brace_level == 0 && current_bracket_level == 0) {
1750 add_literal(arguments, tmpl.content.c_str());
1751 }
1752 } break;
1753 case Token::Kind::Id: {
1755
1756 // Data Literal
1757 if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
1758 tok.text == static_cast<decltype(tok.text)>("null")) {
1759 if (current_brace_level == 0 && current_bracket_level == 0) {
1760 literal_start = tok.text;
1761 add_literal(arguments, tmpl.content.c_str());
1762 }
1763
1764 // Operator
1765 } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
1766 goto parse_operator;
1767
1768 // Functions
1769 } else if (peek_tok.kind == Token::Kind::LeftParen) {
1770 auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
1772 do {
1774 auto expr = parse_expression(tmpl);
1775 if (!expr) {
1776 break;
1777 }
1778 func->number_args += 1;
1779 func->arguments.emplace_back(expr);
1780 } while (tok.kind == Token::Kind::Comma);
1781 if (tok.kind != Token::Kind::RightParen) {
1782 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1783 }
1784
1785 auto function_data = function_storage.find_function(func->name, func->number_args);
1786 if (function_data.operation == FunctionStorage::Operation::None) {
1787 throw_parser_error("unknown function " + func->name);
1788 }
1789 func->operation = function_data.operation;
1790 if (function_data.operation == FunctionStorage::Operation::Callback) {
1791 func->callback = function_data.callback;
1792 }
1793 arguments.emplace_back(func);
1794
1795 // Variables
1796 } else {
1797 arguments.emplace_back(
1798 std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
1799 }
1800
1801 // Operators
1802 } break;
1803 case Token::Kind::Equal:
1809 case Token::Kind::Plus:
1810 case Token::Kind::Minus:
1811 case Token::Kind::Times:
1812 case Token::Kind::Slash:
1813 case Token::Kind::Power:
1815 case Token::Kind::Dot: {
1816 parse_operator:
1818 switch (tok.kind) {
1819 case Token::Kind::Id: {
1820 if (tok.text == "and") {
1822 } else if (tok.text == "or") {
1824 } else if (tok.text == "in") {
1826 } else if (tok.text == "not") {
1828 } else {
1829 throw_parser_error("unknown operator in parser.");
1830 }
1831 } break;
1832 case Token::Kind::Equal: {
1834 } break;
1835 case Token::Kind::NotEqual: {
1837 } break;
1840 } break;
1843 } break;
1844 case Token::Kind::LessThan: {
1846 } break;
1849 } break;
1850 case Token::Kind::Plus: {
1852 } break;
1853 case Token::Kind::Minus: {
1855 } break;
1856 case Token::Kind::Times: {
1858 } break;
1859 case Token::Kind::Slash: {
1861 } break;
1862 case Token::Kind::Power: {
1864 } break;
1865 case Token::Kind::Percent: {
1867 } break;
1868 case Token::Kind::Dot: {
1870 } break;
1871 default: {
1872 throw_parser_error("unknown operator in parser.");
1873 }
1874 }
1875 auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
1876
1877 while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) ||
1878 (operator_stack.top()->precedence == function_node->precedence &&
1879 function_node->associativity == FunctionNode::Associativity::Left))) {
1880 add_operator(arguments, operator_stack);
1881 }
1882
1883 operator_stack.emplace(function_node);
1884 } break;
1885 case Token::Kind::Comma: {
1886 if (current_brace_level == 0 && current_bracket_level == 0) {
1887 goto break_loop;
1888 }
1889 } break;
1890 case Token::Kind::Colon: {
1891 if (current_brace_level == 0 && current_bracket_level == 0) {
1892 throw_parser_error("unexpected ':'");
1893 }
1894 } break;
1897 auto expr = parse_expression(tmpl);
1898 if (tok.kind != Token::Kind::RightParen) {
1899 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1900 }
1901 if (!expr) {
1902 throw_parser_error("empty expression in parentheses");
1903 }
1904 arguments.emplace_back(expr);
1905 } break;
1906
1907 // parse function call pipe syntax
1908 case Token::Kind::Pipe: {
1909 // get function name
1911 if (tok.kind != Token::Kind::Id) {
1912 throw_parser_error("expected function name, got '" + tok.describe() + "'");
1913 }
1914 auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
1915 // add first parameter as last value from arguments
1916 func->number_args += 1;
1917 func->arguments.emplace_back(arguments.back());
1918 arguments.pop_back();
1920 if (peek_tok.kind == Token::Kind::LeftParen) {
1922 // parse additional parameters
1923 do {
1925 auto expr = parse_expression(tmpl);
1926 if (!expr) {
1927 break;
1928 }
1929 func->number_args += 1;
1930 func->arguments.emplace_back(expr);
1931 } while (tok.kind == Token::Kind::Comma);
1932 if (tok.kind != Token::Kind::RightParen) {
1933 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1934 }
1935 }
1936 // search store for defined function with such name and number of args
1937 auto function_data = function_storage.find_function(func->name, func->number_args);
1938 if (function_data.operation == FunctionStorage::Operation::None) {
1939 throw_parser_error("unknown function " + func->name);
1940 }
1941 func->operation = function_data.operation;
1942 if (function_data.operation == FunctionStorage::Operation::Callback) {
1943 func->callback = function_data.callback;
1944 }
1945 arguments.emplace_back(func);
1946 } break;
1947 default:
1948 goto break_loop;
1949 }
1950
1952 }
1953
1954 break_loop:
1955 while (!operator_stack.empty()) {
1956 add_operator(arguments, operator_stack);
1957 }
1958
1959 std::shared_ptr<ExpressionNode> expr;
1960 if (arguments.size() == 1) {
1961 expr = arguments[0];
1962 arguments = {};
1963 } else if (arguments.size() > 1) {
1964 throw_parser_error("malformed expression");
1965 }
1966 return expr;
1967 }
void get_peek_token()
Definition inja.hpp:1614
void add_operator(Arguments &arguments, OperatorStack &operator_stack)
Definition inja.hpp:1629
std::vector< std::shared_ptr< ExpressionNode > > Arguments
Definition inja.hpp:1580
std::stack< std::shared_ptr< FunctionNode > > OperatorStack
Definition inja.hpp:1581
void get_next_token()
Definition inja.hpp:1605
void add_literal(Arguments &arguments, const char *content_ptr)
Definition inja.hpp:1621
bool parse_expression(Template &tmpl, Token::Kind closing)
Definition inja.hpp:1695

References inja::FunctionStorage::Add, add_literal(), add_operator(), 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::Pipe, 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 1695 of file inja.hpp.

1695 {
1697 return tok.kind == closing;
1698 }
ExpressionListNode * current_expression_list
Definition inja.hpp:1595

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 1682 of file inja.hpp.

1682 {
1683 if (tok.kind != Token::Kind::String) {
1684 throw_parser_error("expected string, got '" + tok.describe() + "'");
1685 }
1686
1687 if (tok.text.length() < 2) {
1688 throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
1689 }
1690
1691 // Remove first and last character ""
1692 return std::string{tok.text.substr(1, tok.text.length() - 2)};
1693 }

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,
const std::filesystem::path & path )
inlineprivate

Definition at line 2160 of file inja.hpp.

2160 {
2161 lexer.start(tmpl.content);
2162 current_block = &tmpl.root;
2163
2164 for (;;) {
2166 switch (tok.kind) {
2167 case Token::Kind::Eof: {
2168 if (!if_statement_stack.empty()) {
2169 throw_parser_error("unmatched if");
2170 }
2171 if (!for_statement_stack.empty()) {
2172 throw_parser_error("unmatched for");
2173 }
2174 }
2175 current_block = nullptr;
2176 return;
2177 case Token::Kind::Text: {
2178 current_block->nodes.emplace_back(
2179 std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
2180 } break;
2183 if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) {
2184 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2185 }
2186 if (tok.kind != Token::Kind::StatementClose) {
2187 throw_parser_error("expected statement close, got '" + tok.describe() + "'");
2188 }
2189 } break;
2193 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2194 }
2196 throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
2197 }
2198 } break;
2201
2202 auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
2203 current_block->nodes.emplace_back(expression_list_node);
2204 current_expression_list = expression_list_node.get();
2205
2207 throw_parser_error("expected expression close, got '" + tok.describe() + "'");
2208 }
2209 } break;
2212 if (tok.kind != Token::Kind::CommentClose) {
2213 throw_parser_error("expected comment close, got '" + tok.describe() + "'");
2214 }
2215 } break;
2216 default: {
2217 throw_parser_error("unexpected token '" + tok.describe() + "'");
2218 } break;
2219 }
2220 }
2221 }
std::stack< ForStatementNode * > for_statement_stack
Definition inja.hpp:1598
std::stack< IfStatementNode * > if_statement_stack
Definition inja.hpp:1597
BlockNode * current_block
Definition inja.hpp:1594
bool parse_statement(Template &tmpl, Token::Kind closing, const std::filesystem::path &path)
Definition inja.hpp:1969

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,
const std::filesystem::path & filename )
inline

Definition at line 2240 of file inja.hpp.

2240 {
2241 auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
2242 sub_parser.parse_into(tmpl, filename.parent_path());
2243 }
Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config, TemplateStorage &template_storage, const FunctionStorage &function_storage)
Definition inja.hpp:2224

References config, function_storage, inja::Lexer::get_config(), lexer, parse_into(), Parser(), 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,
const std::filesystem::path & path )
inlineprivate

Definition at line 1969 of file inja.hpp.

1969 {
1970 if (tok.kind != Token::Kind::Id) {
1971 return false;
1972 }
1973
1974 if (tok.text == static_cast<decltype(tok.text)>("if")) {
1976
1977 auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
1978 current_block->nodes.emplace_back(if_statement_node);
1979 if_statement_stack.emplace(if_statement_node.get());
1980 current_block = &if_statement_node->true_statement;
1981 current_expression_list = &if_statement_node->condition;
1982
1983 if (!parse_expression(tmpl, closing)) {
1984 return false;
1985 }
1986 } else if (tok.text == static_cast<decltype(tok.text)>("else")) {
1987 if (if_statement_stack.empty()) {
1988 throw_parser_error("else without matching if");
1989 }
1990 auto& if_statement_data = if_statement_stack.top();
1992
1993 if_statement_data->has_false_statement = true;
1994 current_block = &if_statement_data->false_statement;
1995
1996 // Chained else if
1997 if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
1999
2000 auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
2001 current_block->nodes.emplace_back(if_statement_node);
2002 if_statement_stack.emplace(if_statement_node.get());
2003 current_block = &if_statement_node->true_statement;
2004 current_expression_list = &if_statement_node->condition;
2005
2006 if (!parse_expression(tmpl, closing)) {
2007 return false;
2008 }
2009 }
2010 } else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
2011 if (if_statement_stack.empty()) {
2012 throw_parser_error("endif without matching if");
2013 }
2014
2015 // Nested if statements
2016 while (if_statement_stack.top()->is_nested) {
2017 if_statement_stack.pop();
2018 }
2019
2020 auto& if_statement_data = if_statement_stack.top();
2022
2023 current_block = if_statement_data->parent;
2024 if_statement_stack.pop();
2025 } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
2027
2028 if (tok.kind != Token::Kind::Id) {
2029 throw_parser_error("expected block name, got '" + tok.describe() + "'");
2030 }
2031
2032 const std::string block_name = static_cast<std::string>(tok.text);
2033
2034 auto block_statement_node =
2035 std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
2036 current_block->nodes.emplace_back(block_statement_node);
2037 block_statement_stack.emplace(block_statement_node.get());
2038 current_block = &block_statement_node->block;
2039 auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
2040 if (!success.second) {
2041 throw_parser_error("block with the name '" + block_name + "' does already exist");
2042 }
2043
2045 } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
2046 if (block_statement_stack.empty()) {
2047 throw_parser_error("endblock without matching block");
2048 }
2049
2050 auto& block_statement_data = block_statement_stack.top();
2052
2053 current_block = block_statement_data->parent;
2055 } else if (tok.text == static_cast<decltype(tok.text)>("for")) {
2057
2058 // options: for a in arr; for a, b in obj
2059 if (tok.kind != Token::Kind::Id) {
2060 throw_parser_error("expected id, got '" + tok.describe() + "'");
2061 }
2062
2063 Token value_token = tok;
2065
2066 // Object type
2067 std::shared_ptr<ForStatementNode> for_statement_node;
2068 if (tok.kind == Token::Kind::Comma) {
2070 if (tok.kind != Token::Kind::Id) {
2071 throw_parser_error("expected id, got '" + tok.describe() + "'");
2072 }
2073
2074 const Token key_token = value_token;
2075 value_token = tok;
2077
2078 for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text),
2079 static_cast<std::string>(value_token.text),
2081 tok.text.data() - tmpl.content.c_str());
2082
2083 // Array type
2084 } else {
2085 for_statement_node = std::make_shared<ForArrayStatementNode>(
2086 static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
2087 }
2088
2089 current_block->nodes.emplace_back(for_statement_node);
2090 for_statement_stack.emplace(for_statement_node.get());
2091 current_block = &for_statement_node->body;
2092 current_expression_list = &for_statement_node->condition;
2093
2094 if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
2095 throw_parser_error("expected 'in', got '" + tok.describe() + "'");
2096 }
2098
2099 if (!parse_expression(tmpl, closing)) {
2100 return false;
2101 }
2102 } else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
2103 if (for_statement_stack.empty()) {
2104 throw_parser_error("endfor without matching for");
2105 }
2106
2107 auto& for_statement_data = for_statement_stack.top();
2109
2110 current_block = for_statement_data->parent;
2111 for_statement_stack.pop();
2112 } else if (tok.text == static_cast<decltype(tok.text)>("include")) {
2114
2115 std::string template_name = parse_filename();
2116 add_to_template_storage(path, template_name);
2117
2118 current_block->nodes.emplace_back(
2119 std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2120
2122 } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
2124
2125 std::string template_name = parse_filename();
2126 add_to_template_storage(path, template_name);
2127
2128 current_block->nodes.emplace_back(
2129 std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2130
2132 } else if (tok.text == static_cast<decltype(tok.text)>("set")) {
2134
2135 if (tok.kind != Token::Kind::Id) {
2136 throw_parser_error("expected variable name, got '" + tok.describe() + "'");
2137 }
2138
2139 const std::string key = static_cast<std::string>(tok.text);
2141
2142 auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
2143 current_block->nodes.emplace_back(set_statement_node);
2144 current_expression_list = &set_statement_node->expression;
2145
2146 if (tok.text != static_cast<decltype(tok.text)>("=")) {
2147 throw_parser_error("expected '=', got '" + tok.describe() + "'");
2148 }
2150
2151 if (!parse_expression(tmpl, closing)) {
2152 return false;
2153 }
2154 } else {
2155 return false;
2156 }
2157 return true;
2158 }
std::string parse_filename() const
Definition inja.hpp:1682
std::stack< BlockStatementNode * > block_statement_stack
Definition inja.hpp:1599
void add_to_template_storage(const std::filesystem::path &path, std::string &template_name)
Definition inja.hpp:1644

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 1601 of file inja.hpp.

1601 {
1602 INJA_THROW(ParserError(message, lexer.current_position()));
1603 }

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 1599 of file inja.hpp.

Referenced by parse_statement().

◆ config

const ParserConfig& inja::Parser::config
private

Definition at line 1583 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 1594 of file inja.hpp.

1594{nullptr};

Referenced by parse_into(), and parse_statement().

◆ current_expression_list

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

Definition at line 1595 of file inja.hpp.

1595{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 1598 of file inja.hpp.

Referenced by parse_into(), and parse_statement().

◆ function_storage

const FunctionStorage& inja::Parser::function_storage
private

Definition at line 1587 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 1590 of file inja.hpp.

1590{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 1597 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 1592 of file inja.hpp.

Referenced by add_literal(), and parse_expression().

◆ peek_tok

Token inja::Parser::peek_tok
private

Definition at line 1589 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 1586 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: