| 2546 | } |
| 2547 | |
| 2548 | std::shared_ptr<TemplateNode> parseTemplate( |
| 2549 | const TemplateTokenIterator & begin, |
| 2550 | TemplateTokenIterator & it, |
| 2551 | const TemplateTokenIterator & end, |
| 2552 | bool fully = false) const { |
| 2553 | std::vector<std::shared_ptr<TemplateNode>> children; |
| 2554 | while (it != end) { |
| 2555 | const auto start = it; |
| 2556 | const auto & token = *(it++); |
| 2557 | if (auto if_token = dynamic_cast<IfTemplateToken*>(token.get())) { |
| 2558 | std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<TemplateNode>>> cascade; |
| 2559 | cascade.emplace_back(std::move(if_token->condition), parseTemplate(begin, it, end)); |
| 2560 | |
| 2561 | while (it != end && (*it)->type == TemplateToken::Type::Elif) { |
| 2562 | auto elif_token = dynamic_cast<ElifTemplateToken*>((*(it++)).get()); |
| 2563 | cascade.emplace_back(std::move(elif_token->condition), parseTemplate(begin, it, end)); |
| 2564 | } |
| 2565 | |
| 2566 | if (it != end && (*it)->type == TemplateToken::Type::Else) { |
| 2567 | cascade.emplace_back(nullptr, parseTemplate(begin, ++it, end)); |
| 2568 | } |
| 2569 | if (it == end || (*(it++))->type != TemplateToken::Type::EndIf) { |
| 2570 | throw unterminated(**start); |
| 2571 | } |
| 2572 | children.emplace_back(std::make_shared<IfNode>(token->location, std::move(cascade))); |
| 2573 | } else if (auto for_token = dynamic_cast<ForTemplateToken*>(token.get())) { |
| 2574 | auto body = parseTemplate(begin, it, end); |
| 2575 | auto else_body = std::shared_ptr<TemplateNode>(); |
| 2576 | if (it != end && (*it)->type == TemplateToken::Type::Else) { |
| 2577 | else_body = parseTemplate(begin, ++it, end); |
| 2578 | } |
| 2579 | if (it == end || (*(it++))->type != TemplateToken::Type::EndFor) { |
| 2580 | throw unterminated(**start); |
| 2581 | } |
| 2582 | children.emplace_back(std::make_shared<ForNode>(token->location, std::move(for_token->var_names), std::move(for_token->iterable), std::move(for_token->condition), std::move(body), for_token->recursive, std::move(else_body))); |
| 2583 | } else if (dynamic_cast<GenerationTemplateToken*>(token.get())) { |
| 2584 | auto body = parseTemplate(begin, it, end); |
| 2585 | if (it == end || (*(it++))->type != TemplateToken::Type::EndGeneration) { |
| 2586 | throw unterminated(**start); |
| 2587 | } |
| 2588 | // Treat as a no-op, as our scope is templates for inference, not training (`{% generation %}` wraps generated tokens for masking). |
| 2589 | children.emplace_back(std::move(body)); |
| 2590 | } else if (auto text_token = dynamic_cast<TextTemplateToken*>(token.get())) { |
| 2591 | SpaceHandling pre_space = (it - 1) != begin ? (*(it - 2))->post_space : SpaceHandling::Keep; |
| 2592 | SpaceHandling post_space = it != end ? (*it)->pre_space : SpaceHandling::Keep; |
| 2593 | |
| 2594 | auto text = text_token->text; |
| 2595 | if (post_space == SpaceHandling::Strip) { |
| 2596 | static std::regex trailing_space_regex(R"(\s+$)"); |
| 2597 | text = std::regex_replace(text, trailing_space_regex, ""); |
| 2598 | } else if (options.lstrip_blocks && it != end) { |
| 2599 | auto i = text.size(); |
| 2600 | while (i > 0 && (text[i - 1] == ' ' || text[i - 1] == '\t')) i--; |
| 2601 | if ((i == 0 && (it - 1) == begin) || (i > 0 && text[i - 1] == '\n')) { |
| 2602 | text.resize(i); |
| 2603 | } |
| 2604 | } |
| 2605 | if (pre_space == SpaceHandling::Strip) { |