Add variable assignment

This commit is contained in:
Elara 2023-10-31 13:45:00 -07:00
parent ee4aa95dc3
commit 236e178cea
6 changed files with 460 additions and 260 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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),

View File

@ -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...)
}