Improve expression handling and add parentheses support
This commit is contained in:
		
							
								
								
									
										23
									
								
								expr.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								expr.go
									
									
									
									
									
								
							| @@ -8,33 +8,14 @@ import ( | ||||
| ) | ||||
|  | ||||
| func (t *Template) evalExpr(expr ast.Expr, local map[string]any) (any, error) { | ||||
| 	val, err := t.getValue(expr.Segment, local) | ||||
| 	val, err := t.getValue(expr.First, local) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	a := reflect.ValueOf(val) | ||||
|  | ||||
| 	for _, exprB := range expr.Rest { | ||||
| 		val, err := t.getValue(exprB.Segment, local) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		b := reflect.ValueOf(val) | ||||
| 		a = reflect.ValueOf(t.performOp(a, b, exprB.Logical)) | ||||
| 	} | ||||
|  | ||||
| 	return a.Interface(), nil | ||||
| } | ||||
|  | ||||
| func (t *Template) evalExprSegment(expr ast.ExprSegment, local map[string]any) (any, error) { | ||||
| 	val, err := t.getValue(expr.Value, local) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	a := reflect.ValueOf(val) | ||||
|  | ||||
| 	for _, exprB := range expr.Rest { | ||||
| 		val, err := t.getValue(exprB.Value, local) | ||||
| 		val, err := t.getValue(exprB.First, local) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|   | ||||
| @@ -17,15 +17,15 @@ func (ft forTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
| 		return ErrForTagInvalidArgs | ||||
| 	} | ||||
|  | ||||
| 	var expr ast.ExprSegment | ||||
| 	var expr ast.Expr | ||||
| 	if len(args) == 1 { | ||||
| 		expr2, ok := args[0].(ast.ExprSegment) | ||||
| 		expr2, ok := args[0].(ast.Expr) | ||||
| 		if !ok { | ||||
| 			return ErrForTagInvalidArgs | ||||
| 		} | ||||
| 		expr = expr2 | ||||
| 	} else if len(args) == 2 { | ||||
| 		expr2, ok := args[1].(ast.ExprSegment) | ||||
| 		expr2, ok := args[1].(ast.Expr) | ||||
| 		if !ok { | ||||
| 			return ErrForTagInvalidArgs | ||||
| 		} | ||||
| @@ -44,7 +44,7 @@ func (ft forTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	varName, ok := unwrap(expr.Value).(ast.Ident) | ||||
| 	varName, ok := unwrap(expr.First).(ast.Ident) | ||||
| 	if !ok { | ||||
| 		return ErrForTagInvalidArgs | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,3 +1,5 @@ | ||||
| module go.elara.ws/salix | ||||
|  | ||||
| go 1.21.2 | ||||
|  | ||||
| require github.com/davecgh/go-spew v1.1.1 | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -0,0 +1,2 @@ | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
|   | ||||
| @@ -63,8 +63,8 @@ type Value struct { | ||||
| } | ||||
|  | ||||
| type Expr struct { | ||||
| 	Segment  Node | ||||
| 	Logical  Logical | ||||
| 	First    Node | ||||
| 	Operator Operator | ||||
| 	Rest     []Expr | ||||
| 	Position Position | ||||
| } | ||||
| @@ -73,17 +73,6 @@ func (e Expr) Pos() Position { | ||||
| 	return e.Position | ||||
| } | ||||
|  | ||||
| type ExprSegment struct { | ||||
| 	Value    Node | ||||
| 	Operator Operator | ||||
| 	Rest     []ExprSegment | ||||
| 	Position Position | ||||
| } | ||||
|  | ||||
| func (es ExprSegment) Pos() Position { | ||||
| 	return es.Position | ||||
| } | ||||
|  | ||||
| type FuncCall struct { | ||||
| 	Name     Ident | ||||
| 	Params   []Node | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,6 +28,25 @@ func getPos(c *current) ast.Position { | ||||
|     } | ||||
| } | ||||
|  | ||||
| func toExpr(c *current, first, rest any) ast.Node { | ||||
|     restSlice := toAnySlice(rest) | ||||
|     if len(restSlice) == 0 { | ||||
|         return first.(ast.Node) | ||||
|     } | ||||
|      | ||||
|     out := ast.Expr{First: first.(ast.Node), Position: getPos(c)} | ||||
|     for _, restValue := range restSlice { | ||||
|         valueSlice := toAnySlice(restValue) | ||||
|         out.Rest = append(out.Rest, ast.Expr{ | ||||
|             Operator: valueSlice[1].(ast.Operator), | ||||
|             First:  valueSlice[3].(ast.Node), | ||||
|             Position: valueSlice[3].(ast.Node).Pos(), | ||||
|         }) | ||||
|     } | ||||
|      | ||||
|     return out | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| Root = items:(Tag / ExprTag / EndTag / Text)* { | ||||
| @@ -55,54 +74,32 @@ EndTag = "#!" name:Ident { | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| ExprTag = "#(" item:Item ')' { | ||||
| ExprTag = "#(" item:Expr ')' { | ||||
|     return ast.ExprTag{ | ||||
|         Value:    item.(ast.Node), | ||||
|         Position: getPos(c), | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| Item = Ternary / Expr | ||||
| Expr = Ternary / LogicalExpr | ||||
|  | ||||
| Expr = first:ExprSegment rest:(_ Logical _ ExprSegment)* { | ||||
|     restSlice := toAnySlice(rest) | ||||
|     if len(restSlice) == 0 { | ||||
|         return first, nil | ||||
|     } | ||||
|      | ||||
|     out := ast.Expr{Segment: first.(ast.Node), Position: getPos(c)} | ||||
|     for _, restValue := range restSlice { | ||||
|         valueSlice := toAnySlice(restValue) | ||||
|         out.Rest = append(out.Rest, ast.Expr{ | ||||
|             Logical:  valueSlice[1].(ast.Logical), | ||||
|             Segment:  valueSlice[3].(ast.Node), | ||||
|             Position: valueSlice[3].(ast.Node).Pos(), | ||||
|         }) | ||||
|     } | ||||
|      | ||||
|     return out, nil | ||||
| LogicalExpr = _ first:ComparisonExpr rest:(_ LogicalOp _ ComparisonExpr)* _ { | ||||
|     return toExpr(c, first, rest), nil | ||||
| } | ||||
|  | ||||
| ExprSegment = first:Value rest:(_ Operator _ Value)* { | ||||
|     restSlice := toAnySlice(rest) | ||||
|     if len(restSlice) == 0 { | ||||
|         return first, nil | ||||
|     } | ||||
|      | ||||
|     out := ast.ExprSegment{Value: first.(ast.Node), Position: getPos(c)} | ||||
|     for _, restValue := range restSlice { | ||||
|         valueSlice := toAnySlice(restValue) | ||||
|         out.Rest = append(out.Rest, ast.ExprSegment{ | ||||
|             Operator: valueSlice[1].(ast.Operator), | ||||
|             Value:    valueSlice[3].(ast.Node), | ||||
|             Position: valueSlice[3].(ast.Node).Pos(), | ||||
|         }) | ||||
|     } | ||||
|      | ||||
|     return out, nil | ||||
| ComparisonExpr = _ first:ArithmeticExpr rest:(_ ComparisonOp _ ArithmeticExpr)* _ { | ||||
|     return toExpr(c, first, rest), nil | ||||
| } | ||||
|  | ||||
| ParamList = '(' params:(Item ( ',' _ Item )* )? ')' { | ||||
| ArithmeticExpr = _ first:Value rest:(_ ArithmeticOp _ Value)* _ { | ||||
|     return toExpr(c, first, rest), nil | ||||
| } | ||||
|  | ||||
| ParenExpr = '(' expr:Expr ')' { | ||||
|     return expr, nil | ||||
| } | ||||
|  | ||||
| ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' { | ||||
|     paramSlice := toAnySlice(params) | ||||
|     if len(paramSlice) == 0 { | ||||
|         return []ast.Node{}, nil | ||||
| @@ -116,7 +113,7 @@ ParamList = '(' params:(Item ( ',' _ Item )* )? ')' { | ||||
|     return out, nil | ||||
| } | ||||
|  | ||||
| Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / VariableOr / Ident) { | ||||
| Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / VariableOr / Ident / ParenExpr) { | ||||
|     return ast.Value{ | ||||
|         Node: node.(ast.Node), | ||||
|         Not:  not != nil, | ||||
| @@ -218,15 +215,22 @@ Bool = ("true"i / "false"i) { | ||||
|     }, err | ||||
| } | ||||
|  | ||||
| Operator = ("==" / "<=" / ">=" / "in"i / '<' / '>' / '+' / '-' / '/' / '*') { | ||||
| LogicalOp = ("||" / "&&") { | ||||
|     return ast.Operator{ | ||||
|         Value:    string(c.text), | ||||
|         Position: getPos(c), | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| Logical = ("&&" / "||") { | ||||
|     return ast.Logical{ | ||||
| ComparisonOp = ("==" / "<=" / ">=" / '<' / '>' / "in"i) { | ||||
|     return ast.Operator{ | ||||
|         Value:    string(c.text), | ||||
|         Position: getPos(c), | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| ArithmeticOp = ('+' / '-' / '/' / '*' / '%') { | ||||
|     return ast.Operator{ | ||||
|         Value:    string(c.text), | ||||
|         Position: getPos(c), | ||||
|     }, nil | ||||
|   | ||||
							
								
								
									
										2
									
								
								salix.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								salix.go
									
									
									
									
									
								
							| @@ -206,8 +206,6 @@ func (t *Template) getValue(node ast.Node, local map[string]any) (any, error) { | ||||
| 		return node.Value, nil | ||||
| 	case ast.Expr: | ||||
| 		return t.evalExpr(node, local) | ||||
| 	case ast.ExprSegment: | ||||
| 		return t.evalExprSegment(node, local) | ||||
| 	case ast.FuncCall: | ||||
| 		return t.execFuncCall(node, local) | ||||
| 	case ast.Index: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user