mirror of
https://github.com/semihalev/twig.git
synced 2026-03-14 13:55:46 +01:00
104 lines
3.3 KiB
Go
104 lines
3.3 KiB
Go
package twig
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
func (p *Parser) parseDo(parser *Parser) (Node, error) {
|
|
// Get the line number for error reporting
|
|
doLine := parser.tokens[parser.tokenIndex-2].Line
|
|
|
|
// Check if we have an empty do tag ({% do %})
|
|
if parser.tokenIndex < len(parser.tokens) && parser.tokens[parser.tokenIndex].Type == TOKEN_BLOCK_END {
|
|
// Empty do tag is not valid
|
|
return nil, fmt.Errorf("do tag cannot be empty at line %d", doLine)
|
|
}
|
|
|
|
// Check for special case: assignment expressions
|
|
// These need to be handled specially since they're not normal expressions
|
|
if parser.tokenIndex < len(parser.tokens) {
|
|
// Look ahead to find possible assignment patterns
|
|
// We need to check for NUMBER = EXPR which is invalid
|
|
// as well as NAME = EXPR which is valid
|
|
|
|
// Check if we have an equals sign in the next few tokens
|
|
hasAssignment := false
|
|
equalsPosition := -1
|
|
|
|
// Scan ahead a bit to find possible equals sign
|
|
for i := 0; i < 3 && parser.tokenIndex+i < len(parser.tokens); i++ {
|
|
token := parser.tokens[parser.tokenIndex+i]
|
|
if token.Type == TOKEN_OPERATOR && token.Value == "=" {
|
|
hasAssignment = true
|
|
equalsPosition = i
|
|
break
|
|
}
|
|
|
|
// Stop scanning if we hit the end of the block
|
|
if token.Type == TOKEN_BLOCK_END {
|
|
break
|
|
}
|
|
}
|
|
|
|
// If we found an equals sign, analyze the left-hand side
|
|
if hasAssignment && equalsPosition > 0 {
|
|
firstToken := parser.tokens[parser.tokenIndex]
|
|
|
|
// Check if the left-hand side is a valid variable name
|
|
isValidVariableName := firstToken.Type == TOKEN_NAME
|
|
|
|
// If the left-hand side is a number or literal, that's an error
|
|
if !isValidVariableName {
|
|
return nil, fmt.Errorf("invalid variable name %q in do tag assignment at line %d", firstToken.Value, doLine)
|
|
}
|
|
|
|
// Handle assignment case
|
|
if isValidVariableName && hasAssignment {
|
|
varName := parser.tokens[parser.tokenIndex].Value
|
|
|
|
// Skip tokens up to and including the equals sign
|
|
parser.tokenIndex += equalsPosition + 1
|
|
|
|
// Parse the right side expression
|
|
expr, err := parser.parseExpression()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing expression in do assignment at line %d: %w", doLine, err)
|
|
}
|
|
|
|
// Make sure we have the closing tag
|
|
if parser.tokenIndex >= len(parser.tokens) || parser.tokens[parser.tokenIndex].Type != TOKEN_BLOCK_END {
|
|
return nil, fmt.Errorf("expecting end of do tag at line %d", doLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// Additional validation for variable name
|
|
if _, err := strconv.Atoi(varName); err == nil {
|
|
return nil, fmt.Errorf("invalid variable name %q in do tag assignment at line %d", varName, doLine)
|
|
}
|
|
|
|
// Create a SetNode instead of DoNode for assignments
|
|
return &SetNode{
|
|
name: varName,
|
|
value: expr,
|
|
line: doLine,
|
|
}, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse the expression to be executed
|
|
expr, err := parser.parseExpression()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing expression in do tag at line %d: %w", doLine, err)
|
|
}
|
|
|
|
// Make sure we have the closing tag
|
|
if parser.tokenIndex >= len(parser.tokens) || parser.tokens[parser.tokenIndex].Type != TOKEN_BLOCK_END {
|
|
return nil, fmt.Errorf("expecting end of do tag at line %d", doLine)
|
|
}
|
|
parser.tokenIndex++
|
|
|
|
// Create and return the DoNode
|
|
return NewDoNode(expr, doLine), nil
|
|
}
|