|
1 | | -// Copyright (c) 2020 Pantor. All rights reserved. |
| 1 | +// Copyright (c) 2021 Pantor. All rights reserved. |
2 | 2 |
|
3 | 3 | #ifndef INCLUDE_INJA_PARSER_HPP_ |
4 | 4 | #define INCLUDE_INJA_PARSER_HPP_ |
@@ -50,6 +50,7 @@ class Parser { |
50 | 50 | std::stack<std::shared_ptr<FunctionNode>> operator_stack; |
51 | 51 | std::stack<IfStatementNode*> if_statement_stack; |
52 | 52 | std::stack<ForStatementNode*> for_statement_stack; |
| 53 | + std::stack<BlockStatementNode*> block_statement_stack; |
53 | 54 |
|
54 | 55 | inline void throw_parser_error(const std::string &message) { |
55 | 56 | INJA_THROW(ParserError(message, lexer.current_position())); |
@@ -87,6 +88,22 @@ class Parser { |
87 | 88 | arguments.emplace_back(function); |
88 | 89 | } |
89 | 90 |
|
| 91 | + void add_to_template_storage(nonstd::string_view path, std::string& template_name) { |
| 92 | + if (config.search_included_templates_in_files && template_storage.find(template_name) == template_storage.end()) { |
| 93 | + // Build the relative path |
| 94 | + template_name = static_cast<std::string>(path) + template_name; |
| 95 | + if (template_name.compare(0, 2, "./") == 0) { |
| 96 | + template_name.erase(0, 2); |
| 97 | + } |
| 98 | + |
| 99 | + if (template_storage.find(template_name) == template_storage.end()) { |
| 100 | + auto include_template = Template(load_file(template_name)); |
| 101 | + template_storage.emplace(template_name, include_template); |
| 102 | + parse_into_template(template_storage[template_name], template_name); |
| 103 | + } |
| 104 | + } |
| 105 | + } |
| 106 | + |
90 | 107 | bool parse_expression(Template &tmpl, Token::Kind closing) { |
91 | 108 | while (tok.kind != closing && tok.kind != Token::Kind::Eof) { |
92 | 109 | // Literals |
@@ -387,6 +404,37 @@ class Parser { |
387 | 404 | current_block = if_statement_data->parent; |
388 | 405 | if_statement_stack.pop(); |
389 | 406 |
|
| 407 | + } else if (tok.text == static_cast<decltype(tok.text)>("block")) { |
| 408 | + get_next_token(); |
| 409 | + |
| 410 | + if (tok.kind != Token::Kind::Id) { |
| 411 | + throw_parser_error("expected block name, got '" + tok.describe() + "'"); |
| 412 | + } |
| 413 | + |
| 414 | + const std::string block_name = static_cast<std::string>(tok.text); |
| 415 | + |
| 416 | + auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str()); |
| 417 | + current_block->nodes.emplace_back(block_statement_node); |
| 418 | + block_statement_stack.emplace(block_statement_node.get()); |
| 419 | + current_block = &block_statement_node->block; |
| 420 | + auto success = tmpl.block_storage.emplace(block_name, block_statement_node); |
| 421 | + if (!success.second) { |
| 422 | + throw_parser_error("block with the name '" + block_name + "' does already exist"); |
| 423 | + } |
| 424 | + |
| 425 | + get_next_token(); |
| 426 | + |
| 427 | + } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) { |
| 428 | + if (block_statement_stack.empty()) { |
| 429 | + throw_parser_error("endblock without matching block"); |
| 430 | + } |
| 431 | + |
| 432 | + auto &block_statement_data = block_statement_stack.top(); |
| 433 | + get_next_token(); |
| 434 | + |
| 435 | + current_block = block_statement_data->parent; |
| 436 | + block_statement_stack.pop(); |
| 437 | + |
390 | 438 | } else if (tok.text == static_cast<decltype(tok.text)>("for")) { |
391 | 439 | get_next_token(); |
392 | 440 |
|
@@ -450,21 +498,23 @@ class Parser { |
450 | 498 | } |
451 | 499 |
|
452 | 500 | std::string template_name = json::parse(tok.text).get_ref<const std::string &>(); |
453 | | - if (config.search_included_templates_in_files && template_storage.find(template_name) == template_storage.end()) { |
454 | | - // Build the relative path |
455 | | - template_name = static_cast<std::string>(path) + template_name; |
456 | | - if (template_name.compare(0, 2, "./") == 0) { |
457 | | - template_name.erase(0, 2); |
458 | | - } |
| 501 | + add_to_template_storage(path, template_name); |
459 | 502 |
|
460 | | - if (template_storage.find(template_name) == template_storage.end()) { |
461 | | - auto include_template = Template(load_file(template_name)); |
462 | | - template_storage.emplace(template_name, include_template); |
463 | | - parse_into_template(template_storage[template_name], template_name); |
464 | | - } |
| 503 | + current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str())); |
| 504 | + |
| 505 | + get_next_token(); |
| 506 | + |
| 507 | + } else if (tok.text == static_cast<decltype(tok.text)>("extends")) { |
| 508 | + get_next_token(); |
| 509 | + |
| 510 | + if (tok.kind != Token::Kind::String) { |
| 511 | + throw_parser_error("expected string, got '" + tok.describe() + "'"); |
465 | 512 | } |
466 | 513 |
|
467 | | - current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str())); |
| 514 | + std::string template_name = json::parse(tok.text).get_ref<const std::string &>(); |
| 515 | + add_to_template_storage(path, template_name); |
| 516 | + |
| 517 | + current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str())); |
468 | 518 |
|
469 | 519 | get_next_token(); |
470 | 520 |
|
|
0 commit comments