Add variable assignment
This commit is contained in:
		
							
								
								
									
										10
									
								
								ast/ast.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								ast/ast.go
									
									
									
									
									
								
							| @@ -90,6 +90,16 @@ func (e Expr) Pos() Position { | ||||
| 	return e.Position | ||||
| } | ||||
|  | ||||
| type Assignment struct { | ||||
| 	Name     Ident | ||||
| 	Value    Node | ||||
| 	Position Position | ||||
| } | ||||
|  | ||||
| func (a Assignment) Pos() Position { | ||||
| 	return a.Position | ||||
| } | ||||
|  | ||||
| type FuncCall struct { | ||||
| 	Name     Ident | ||||
| 	Params   []Node | ||||
|   | ||||
| @@ -33,7 +33,7 @@ var ( | ||||
| type includeTag struct{} | ||||
|  | ||||
| func (it includeTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
| 	if len(args) != 1 { | ||||
| 	if len(args) < 1 { | ||||
| 		return ErrIncludeInvalidArgs | ||||
| 	} | ||||
|  | ||||
| @@ -52,5 +52,22 @@ func (it includeTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
| 		return ErrNoSuchTemplate | ||||
| 	} | ||||
|  | ||||
| 	return tc.Execute(tmpl.ast, nil) | ||||
| 	local := map[string]any{} | ||||
|  | ||||
| 	// Use the variable assignments after the first argument | ||||
| 	// to set the local variables of the execution | ||||
| 	for _, arg := range args[1:] { | ||||
| 		if a, ok := arg.(ast.Assignment); ok { | ||||
| 			val, err := tc.GetValue(a.Value, local) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			local[a.Name.Value] = val | ||||
| 		} else { | ||||
| 			// If the argument isn't an assigment, return invalid args | ||||
| 			return ErrIncludeInvalidArgs | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return tc.Execute(tmpl.ast, local) | ||||
| } | ||||
|   | ||||
							
								
								
									
										23
									
								
								macro_tag.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								macro_tag.go
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrMacroInvalidArgs = errors.New("macro expects one string argument") | ||||
| 	ErrMacroInvalidArgs = errors.New("macro expects one string argument followed by variable assignments") | ||||
| 	ErrNoSuchMacro      = errors.New("no such template") | ||||
| ) | ||||
|  | ||||
| @@ -15,7 +15,7 @@ var ( | ||||
| type macroTag struct{} | ||||
|  | ||||
| func (mt macroTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
| 	if len(args) != 1 { | ||||
| 	if len(args) < 1 { | ||||
| 		return ErrMacroInvalidArgs | ||||
| 	} | ||||
|  | ||||
| @@ -30,11 +30,28 @@ func (mt macroTag) Run(tc *TagContext, block, args []ast.Node) error { | ||||
| 	} | ||||
|  | ||||
| 	if len(block) == 0 { | ||||
| 		local := map[string]any{} | ||||
|  | ||||
| 		// Use the variable assignments after the first argument | ||||
| 		// to set the local variables of the execution | ||||
| 		for _, arg := range args[1:] { | ||||
| 			if a, ok := arg.(ast.Assignment); ok { | ||||
| 				val, err := tc.GetValue(a.Value, local) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				local[a.Name.Value] = val | ||||
| 			} else { | ||||
| 				// If the argument isn't an assigment, return invalid args | ||||
| 				return ErrMacroInvalidArgs | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		macro, ok := tc.t.macros[name] | ||||
| 		if !ok { | ||||
| 			return ErrNoSuchMacro | ||||
| 		} | ||||
| 		return tc.Execute(macro, nil) | ||||
| 		return tc.Execute(macro, local) | ||||
| 	} else { | ||||
| 		tc.t.macros[name] = block | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										633
									
								
								parser/parser.go
									
									
									
									
									
								
							
							
						
						
									
										633
									
								
								parser/parser.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -99,7 +99,8 @@ ExprTag = "#(" item:Expr ')' { | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| Expr = Ternary / LogicalExpr | ||||
| Expr = Ternary / Assignment / LogicalExpr | ||||
| Assignable = Ternary / LogicalExpr | ||||
|  | ||||
| LogicalExpr = _ first:ComparisonExpr rest:(_ LogicalOp _ ComparisonExpr)* _ { | ||||
|     return toExpr(c, first, rest), nil | ||||
| @@ -138,14 +139,22 @@ Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / F | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| VariableOr = variable:Ident _ '|' _ or:Expr { | ||||
| VariableOr = variable:Ident _ '|' _ or:Assignable { | ||||
|     return ast.VariableOr{ | ||||
|         Variable: variable.(ast.Ident), | ||||
|         Or:       or.(ast.Node), | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| Ternary = cond:Expr _ '?' _ ifTrue:Value _ ':' _ elseVal:Value { | ||||
| Assignment = name:Ident _ '=' _ value:Assignable { | ||||
|     return ast.Assignment{ | ||||
|         Name:     name.(ast.Ident), | ||||
|         Value:    value.(ast.Node), | ||||
|         Position: getPos(c), | ||||
|     }, nil | ||||
| } | ||||
|  | ||||
| Ternary = cond:Assignable _ '?' _ ifTrue:Value _ ':' _ elseVal:Value { | ||||
|     return ast.Ternary{ | ||||
|         Condition: cond.(ast.Node), | ||||
|         IfTrue:    ifTrue.(ast.Node), | ||||
|   | ||||
							
								
								
									
										18
									
								
								salix.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								salix.go
									
									
									
									
									
								
							| @@ -24,7 +24,6 @@ import ( | ||||
| 	"html" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
|  | ||||
| 	"go.elara.ws/salix/ast" | ||||
| ) | ||||
| @@ -126,6 +125,9 @@ func (t *Template) execute(w io.Writer, nodes []ast.Node, local map[string]any) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if _, ok := v.(ast.Assignment); ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			_, err = io.WriteString(w, t.toString(v)) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| @@ -210,6 +212,8 @@ func (t *Template) getValue(node ast.Node, local map[string]any) (any, error) { | ||||
| 		return t.evalTernary(node, local) | ||||
| 	case ast.VariableOr: | ||||
| 		return t.evalVariableOr(node, local) | ||||
| 	case ast.Assignment: | ||||
| 		return node, t.handleAssignment(node, local) | ||||
| 	default: | ||||
| 		return nil, nil | ||||
| 	} | ||||
| @@ -398,6 +402,9 @@ func (t *Template) execFunc(fn reflect.Value, node ast.Node, args []ast.Node, lo | ||||
|  | ||||
| 	params := make([]reflect.Value, fnType.NumIn()) | ||||
| 	for i, arg := range args { | ||||
| 		if _, ok := arg.(ast.Assignment); ok { | ||||
| 			return nil, t.posError(arg, "assignment cannot be used as a function argument") | ||||
| 		} | ||||
| 		paramVal, err := t.getValue(arg, local) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| @@ -448,6 +455,15 @@ func (t *Template) evalVariableOr(vo ast.VariableOr, local map[string]any) (any, | ||||
| 	return val.Interface(), nil | ||||
| } | ||||
|  | ||||
| func (t *Template) handleAssignment(a ast.Assignment, local map[string]any) error { | ||||
| 	val, err := t.getValue(a.Value, local) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	local[a.Name.Value] = val | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *Template) posError(n ast.Node, format string, v ...any) error { | ||||
| 	return ast.PosError(n, t.name, format, v...) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user