mirror of
https://github.com/semihalev/twig.git
synced 2026-03-14 13:55:46 +01:00
- Move parsing functions for tags into dedicated files - Add comprehensive tests for the from tag - Fix the implementation of parseFrom to correctly handle imports - Improve test coverage for macros and imports 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
package twig
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// parseIf parses if/elseif/else/endif block structure
|
|
// Examples:
|
|
// {% if condition %}...{% endif %}
|
|
// {% if condition %}...{% else %}...{% endif %}
|
|
// {% if condition %}...{% elseif condition2 %}...{% else %}...{% endif %}
|
|
func (p *Parser) parseIf(parser *Parser) (Node, error) {
|
|
// Get the line number of the if token
|
|
ifLine := parser.tokens[parser.tokenIndex-2].Line
|
|
|
|
// Parse the condition expression
|
|
condition, err := parser.parseExpression()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Expect the block end token (either regular or whitespace-trimming variant)
|
|
if parser.tokenIndex >= len(parser.tokens) ||
|
|
(parser.tokens[parser.tokenIndex].Type != TOKEN_BLOCK_END &&
|
|
parser.tokens[parser.tokenIndex].Type != TOKEN_BLOCK_END_TRIM) {
|
|
return nil, fmt.Errorf("expected block end after if condition at line %d", ifLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// Parse the if body (statements between if and endif/else/elseif)
|
|
ifBody, err := parser.parseOuterTemplate()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Initialize conditions and bodies arrays with the initial if condition and body
|
|
conditions := []Node{condition}
|
|
bodies := [][]Node{ifBody}
|
|
var elseBody []Node
|
|
|
|
// Keep track of whether we've seen an else block
|
|
var hasElseBlock bool
|
|
|
|
// Process subsequent tags (elseif, else, endif)
|
|
for {
|
|
// We expect a block start token for elseif, else, or endif
|
|
if parser.tokenIndex >= len(parser.tokens) || parser.tokens[parser.tokenIndex].Type != TOKEN_BLOCK_START {
|
|
return nil, fmt.Errorf("unexpected end of template, expected endif at line %d", ifLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// We expect a name token (elseif, else, or endif)
|
|
if parser.tokenIndex >= len(parser.tokens) || parser.tokens[parser.tokenIndex].Type != TOKEN_NAME {
|
|
return nil, fmt.Errorf("expected block name at line %d", parser.tokens[parser.tokenIndex-1].Line)
|
|
}
|
|
|
|
// Get the tag name
|
|
blockName := parser.tokens[parser.tokenIndex].Value
|
|
blockLine := parser.tokens[parser.tokenIndex].Line
|
|
parser.tokenIndex++
|
|
|
|
// Process based on the tag type
|
|
if blockName == "elseif" {
|
|
// Check if we've already seen an else block - elseif can't come after else
|
|
if hasElseBlock {
|
|
return nil, fmt.Errorf("unexpected elseif after else at line %d", blockLine)
|
|
}
|
|
|
|
// Handle elseif condition
|
|
elseifCondition, err := parser.parseExpression()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Expect block end token
|
|
if parser.tokenIndex >= len(parser.tokens) || !isBlockEndToken(parser.tokens[parser.tokenIndex].Type) {
|
|
return nil, fmt.Errorf("expected block end after elseif condition at line %d", blockLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// Parse the elseif body
|
|
elseifBody, err := parser.parseOuterTemplate()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add condition and body to our arrays
|
|
conditions = append(conditions, elseifCondition)
|
|
bodies = append(bodies, elseifBody)
|
|
|
|
// Continue checking for more elseif/else/endif tags
|
|
} else if blockName == "else" {
|
|
// Check if we've already seen an else block - can't have multiple else blocks
|
|
if hasElseBlock {
|
|
return nil, fmt.Errorf("multiple else blocks found at line %d", blockLine)
|
|
}
|
|
|
|
// Mark that we've seen an else block
|
|
hasElseBlock = true
|
|
|
|
// Expect block end token
|
|
if parser.tokenIndex >= len(parser.tokens) || !isBlockEndToken(parser.tokens[parser.tokenIndex].Type) {
|
|
return nil, fmt.Errorf("expected block end after else tag at line %d", blockLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// Parse the else body
|
|
elseBody, err = parser.parseOuterTemplate()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// After else, we need to find endif next (handled in next iteration)
|
|
} else if blockName == "endif" {
|
|
// Expect block end token
|
|
if parser.tokenIndex >= len(parser.tokens) || !isBlockEndToken(parser.tokens[parser.tokenIndex].Type) {
|
|
return nil, fmt.Errorf("expected block end after endif at line %d", blockLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// We found the endif, we're done
|
|
break
|
|
} else {
|
|
return nil, fmt.Errorf("expected elseif, else, or endif, got %s at line %d", blockName, blockLine)
|
|
}
|
|
}
|
|
|
|
// Create and return the if node
|
|
ifNode := &IfNode{
|
|
conditions: conditions,
|
|
bodies: bodies,
|
|
elseBranch: elseBody,
|
|
line: ifLine,
|
|
}
|
|
|
|
return ifNode, nil
|
|
}
|
|
|
|
// Helper function to check if a token type is a block end token
|
|
func isBlockEndToken(tokenType int) bool {
|
|
return tokenType == TOKEN_BLOCK_END || tokenType == TOKEN_BLOCK_END_TRIM
|
|
}
|
|
|
|
// Helper function to check if a token is any kind of variable end token (regular or trim variant)
|
|
func isVarEndToken(tokenType int) bool {
|
|
return tokenType == TOKEN_VAR_END || tokenType == TOKEN_VAR_END_TRIM
|
|
}
|