Skip to content

Commit d462b9f

Browse files
authored
Fix issue 246 (pantor#247)
* Add `parse_expression` with one argument in preparation to fix pantor#246. * Fix issue pantor#246: `func(n1 + n2, n3)` runs as if `func(n1, n2 + n3)` * Simplify parser for an expression enclosed by parentheses.
1 parent edc1779 commit d462b9f

File tree

6 files changed

+128
-152
lines changed

6 files changed

+128
-152
lines changed

include/inja/function_storage.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ class FunctionStorage {
6262
Super,
6363
Join,
6464
Callback,
65-
ParenLeft,
66-
ParenRight,
6765
None,
6866
};
6967

include/inja/node.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class FunctionNode : public ExpressionNode {
148148
CallbackFunction callback;
149149

150150
explicit FunctionNode(std::string_view name, size_t pos)
151-
: ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
151+
: ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(0) {}
152152
explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
153153
switch (operation) {
154154
case Op::Not: {

include/inja/parser.hpp

Lines changed: 62 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ namespace inja {
2222
* \brief Class for parsing an inja Template.
2323
*/
2424
class Parser {
25+
using Arguments = std::vector<std::shared_ptr<ExpressionNode>>;
26+
using OperatorStack = std::stack<std::shared_ptr<FunctionNode>>;
27+
2528
const ParserConfig& config;
2629

2730
Lexer lexer;
@@ -31,18 +34,11 @@ class Parser {
3134
Token tok, peek_tok;
3235
bool have_peek_tok {false};
3336

34-
size_t current_paren_level {0};
35-
size_t current_bracket_level {0};
36-
size_t current_brace_level {0};
37-
3837
std::string_view literal_start;
3938

4039
BlockNode* current_block {nullptr};
4140
ExpressionListNode* current_expression_list {nullptr};
42-
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
43-
std::vector<std::shared_ptr<ExpressionNode>> arguments;
4441

45-
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
4642
std::stack<IfStatementNode*> if_statement_stack;
4743
std::stack<ForStatementNode*> for_statement_stack;
4844
std::stack<BlockStatementNode*> block_statement_stack;
@@ -67,12 +63,12 @@ class Parser {
6763
}
6864
}
6965

70-
inline void add_literal(const char* content_ptr) {
66+
inline void add_literal(Arguments &arguments, const char* content_ptr) {
7167
std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
7268
arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
7369
}
7470

75-
inline void add_operator() {
71+
inline void add_operator(Arguments &arguments, OperatorStack &operator_stack) {
7672
auto function = operator_stack.top();
7773
operator_stack.pop();
7874

@@ -140,19 +136,29 @@ class Parser {
140136
}
141137

142138
bool parse_expression(Template& tmpl, Token::Kind closing) {
143-
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
139+
current_expression_list->root = parse_expression(tmpl);
140+
return tok.kind == closing;
141+
}
142+
143+
std::shared_ptr<ExpressionNode> parse_expression(Template& tmpl) {
144+
size_t current_bracket_level {0};
145+
size_t current_brace_level {0};
146+
Arguments arguments;
147+
OperatorStack operator_stack;
148+
149+
while (tok.kind != Token::Kind::Eof) {
144150
// Literals
145151
switch (tok.kind) {
146152
case Token::Kind::String: {
147153
if (current_brace_level == 0 && current_bracket_level == 0) {
148154
literal_start = tok.text;
149-
add_literal(tmpl.content.c_str());
155+
add_literal(arguments, tmpl.content.c_str());
150156
}
151157
} break;
152158
case Token::Kind::Number: {
153159
if (current_brace_level == 0 && current_bracket_level == 0) {
154160
literal_start = tok.text;
155-
add_literal(tmpl.content.c_str());
161+
add_literal(arguments, tmpl.content.c_str());
156162
}
157163
} break;
158164
case Token::Kind::LeftBracket: {
@@ -174,7 +180,7 @@ class Parser {
174180

175181
current_bracket_level -= 1;
176182
if (current_brace_level == 0 && current_bracket_level == 0) {
177-
add_literal(tmpl.content.c_str());
183+
add_literal(arguments, tmpl.content.c_str());
178184
}
179185
} break;
180186
case Token::Kind::RightBrace: {
@@ -184,7 +190,7 @@ class Parser {
184190

185191
current_brace_level -= 1;
186192
if (current_brace_level == 0 && current_bracket_level == 0) {
187-
add_literal(tmpl.content.c_str());
193+
add_literal(arguments, tmpl.content.c_str());
188194
}
189195
} break;
190196
case Token::Kind::Id: {
@@ -195,7 +201,7 @@ class Parser {
195201
tok.text == static_cast<decltype(tok.text)>("null")) {
196202
if (current_brace_level == 0 && current_bracket_level == 0) {
197203
literal_start = tok.text;
198-
add_literal(tmpl.content.c_str());
204+
add_literal(arguments, tmpl.content.c_str());
199205
}
200206

201207
// Operator
@@ -204,8 +210,30 @@ class Parser {
204210

205211
// Functions
206212
} else if (peek_tok.kind == Token::Kind::LeftParen) {
207-
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
208-
function_stack.emplace(operator_stack.top().get(), current_paren_level);
213+
auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
214+
get_next_token();
215+
do {
216+
get_next_token();
217+
auto expr = parse_expression(tmpl);
218+
if (!expr) {
219+
break;
220+
}
221+
func->number_args += 1;
222+
func->arguments.emplace_back(expr);
223+
} while (tok.kind == Token::Kind::Comma);
224+
if (tok.kind != Token::Kind::RightParen) {
225+
throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
226+
}
227+
228+
auto function_data = function_storage.find_function(func->name, func->number_args);
229+
if (function_data.operation == FunctionStorage::Operation::None) {
230+
throw_parser_error("unknown function " + func->name);
231+
}
232+
func->operation = function_data.operation;
233+
if (function_data.operation == FunctionStorage::Operation::Callback) {
234+
func->callback = function_data.callback;
235+
}
236+
arguments.emplace_back(func);
209237

210238
// Variables
211239
} else {
@@ -291,20 +319,15 @@ class Parser {
291319

292320
while (!operator_stack.empty() &&
293321
((operator_stack.top()->precedence > function_node->precedence) ||
294-
(operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
295-
(operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
296-
add_operator();
322+
(operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left))) {
323+
add_operator(arguments, operator_stack);
297324
}
298325

299326
operator_stack.emplace(function_node);
300327
} break;
301328
case Token::Kind::Comma: {
302329
if (current_brace_level == 0 && current_bracket_level == 0) {
303-
if (function_stack.empty()) {
304-
throw_parser_error("unexpected ','");
305-
}
306-
307-
function_stack.top().first->number_args += 1;
330+
goto break_loop;
308331
}
309332
} break;
310333
case Token::Kind::Colon: {
@@ -313,64 +336,36 @@ class Parser {
313336
}
314337
} break;
315338
case Token::Kind::LeftParen: {
316-
current_paren_level += 1;
317-
operator_stack.emplace(std::make_shared<FunctionNode>(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str()));
318-
319-
get_peek_token();
320-
if (peek_tok.kind == Token::Kind::RightParen) {
321-
if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) {
322-
function_stack.top().first->number_args = 0;
323-
}
324-
}
325-
} break;
326-
case Token::Kind::RightParen: {
327-
current_paren_level -= 1;
328-
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
329-
add_operator();
339+
get_next_token();
340+
auto expr = parse_expression(tmpl);
341+
if (tok.kind != Token::Kind::RightParen) {
342+
throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
330343
}
331-
332-
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
333-
operator_stack.pop();
344+
if (!expr) {
345+
throw_parser_error("empty expression in parentheses");
334346
}
335-
336-
if (!function_stack.empty() && function_stack.top().second == current_paren_level) {
337-
auto func = function_stack.top().first;
338-
auto function_data = function_storage.find_function(func->name, func->number_args);
339-
if (function_data.operation == FunctionStorage::Operation::None) {
340-
throw_parser_error("unknown function " + func->name);
341-
}
342-
func->operation = function_data.operation;
343-
if (function_data.operation == FunctionStorage::Operation::Callback) {
344-
func->callback = function_data.callback;
345-
}
346-
347-
if (operator_stack.empty()) {
348-
throw_parser_error("internal error at function " + func->name);
349-
}
350-
351-
add_operator();
352-
function_stack.pop();
353-
}
354-
}
347+
arguments.emplace_back(expr);
348+
} break;
355349
default:
356-
break;
350+
goto break_loop;
357351
}
358352

359353
get_next_token();
360354
}
361355

356+
break_loop:
362357
while (!operator_stack.empty()) {
363-
add_operator();
358+
add_operator(arguments, operator_stack);
364359
}
365360

361+
std::shared_ptr<ExpressionNode> expr;
366362
if (arguments.size() == 1) {
367-
current_expression_list->root = arguments[0];
363+
expr = arguments[0];
368364
arguments = {};
369365
} else if (arguments.size() > 1) {
370366
throw_parser_error("malformed expression");
371367
}
372-
373-
return true;
368+
return expr;
374369
}
375370

376371
bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
@@ -604,10 +599,6 @@ class Parser {
604599
current_expression_list = expression_list_node.get();
605600

606601
if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) {
607-
throw_parser_error("expected expression, got '" + tok.describe() + "'");
608-
}
609-
610-
if (tok.kind != Token::Kind::ExpressionClose) {
611602
throw_parser_error("expected expression close, got '" + tok.describe() + "'");
612603
}
613604
} break;

include/inja/renderer.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,6 @@ class Renderer : public NodeVisitor {
464464
}
465465
make_result(os.str());
466466
} break;
467-
case Op::ParenLeft:
468-
case Op::ParenRight:
469467
case Op::None:
470468
break;
471469
}

0 commit comments

Comments
 (0)