From 01047706b64e918203a3495c880457a056328301 Mon Sep 17 00:00:00 2001 From: Elara6331 Date: Sun, 17 Dec 2023 16:55:13 -0800 Subject: [PATCH] Rewrite error handling logic --- ast/ast.go | 7 +- expr.go | 24 +- for_tag.go | 18 +- if_tag.go | 26 +- include_tag.go | 10 +- macro_tag.go | 19 +- parse.go | 2 +- parser/parser.go | 611 ++++++++++++++++++++++++----------------------- parser/salix.peg | 1 + salix.go | 161 ++++++++----- tags.go | 13 + vars.go | 7 +- 12 files changed, 468 insertions(+), 431 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 01e1b09..4175836 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -19,8 +19,8 @@ package ast import "fmt" -func PosError(n Node, filename, format string, v ...any) error { - return fmt.Errorf(filename+":"+n.Pos().String()+": "+format, v...) +func PosError(n Node, format string, v ...any) error { + return fmt.Errorf(n.Pos().String()+": "+format, v...) } type Node interface { @@ -28,12 +28,13 @@ type Node interface { } type Position struct { + Name string Line int Col int } func (p Position) String() string { - return fmt.Sprintf("%d:%d", p.Line, p.Col) + return fmt.Sprintf("%s: line %d, col %d", p.Name, p.Line, p.Col) } type Tag struct { diff --git a/expr.go b/expr.go index d5b68ed..4a110af 100644 --- a/expr.go +++ b/expr.go @@ -19,20 +19,12 @@ package salix import ( - "errors" "reflect" "strings" "go.elara.ws/salix/ast" ) -var ( - ErrModulusFloat = errors.New("modulo operation cannot be performed on floats") - ErrTypeMismatch = errors.New("mismatched types") - ErrLogicalNonBool = errors.New("logical operations may only be performed on boolean values") - ErrInOpInvalidTypes = errors.New("the in operator can only be used on strings, arrays, and slices") -) - func (t *Template) evalExpr(expr ast.Expr, local map[string]any) (any, error) { val, err := t.getValue(expr.First, local) if err != nil { @@ -65,25 +57,25 @@ func (t *Template) performOp(a, b reflect.Value, op ast.Operator) (any, error) { if a.CanConvert(b.Type().Elem()) { a = a.Convert(b.Type().Elem()) } else { - return nil, t.posError(op, "%w (%s and %s)", ErrTypeMismatch, a.Type(), b.Type()) + return nil, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type()) } case reflect.Map: if a.CanConvert(b.Type().Key()) { a = a.Convert(b.Type().Key()) } else { - return nil, t.posError(op, "%w (%s and %s)", ErrTypeMismatch, a.Type(), b.Type()) + return nil, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type()) } case reflect.String: if a.Kind() != reflect.String { - return nil, t.posError(op, "%w (%s and %s)", ErrTypeMismatch, a.Type(), b.Type()) + return nil, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type()) } default: - return nil, t.posError(op, "%w (got %s and %s)", ErrInOpInvalidTypes, a.Type(), b.Type()) + return nil, ast.PosError(op, "the in operator can only be used on strings, arrays, and slices (got %s and %s)", a.Type(), b.Type()) } } else if b.CanConvert(a.Type()) { b = b.Convert(a.Type()) } else { - return nil, t.posError(op, "%w (%s and %s)", ErrTypeMismatch, a.Type(), b.Type()) + return nil, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type()) } switch op.Value { @@ -91,12 +83,12 @@ func (t *Template) performOp(a, b reflect.Value, op ast.Operator) (any, error) { return a.Equal(b), nil case "&&": if a.Kind() != reflect.Bool || b.Kind() != reflect.Bool { - return nil, t.posError(op, "%w", ErrLogicalNonBool) + return nil, ast.PosError(op, "logical operations may only be performed on boolean values") } return a.Bool() && b.Bool(), nil case "||": if a.Kind() != reflect.Bool || b.Kind() != reflect.Bool { - return nil, t.posError(op, "%w", ErrLogicalNonBool) + return nil, ast.PosError(op, "logical operations may only be performed on boolean values") } return a.Bool() || b.Bool(), nil case ">=": @@ -180,7 +172,7 @@ func (t *Template) performOp(a, b reflect.Value, op ast.Operator) (any, error) { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return a.Uint() % b.Uint(), nil case reflect.Float64, reflect.Float32: - return nil, t.posError(op, "%w", ErrModulusFloat) + return nil, ast.PosError(op, "modulus operation cannot be performed on floats") } case "in": if a.Kind() == reflect.String && b.Kind() == reflect.String { diff --git a/for_tag.go b/for_tag.go index 49665ae..2215f4c 100644 --- a/for_tag.go +++ b/for_tag.go @@ -19,33 +19,30 @@ package salix import ( - "errors" "reflect" "go.elara.ws/salix/ast" ) -var ErrForTagInvalidArgs = errors.New("invalid arguments in for tag") - // forTag represents a #for tag within a Salix template type forTag struct{} func (ft forTag) Run(tc *TagContext, block, args []ast.Node) error { if len(args) == 0 || len(args) > 2 { - return ErrForTagInvalidArgs + return tc.PosError(tc.Tag, "invalid argument amount") } var expr ast.Expr if len(args) == 1 { expr2, ok := args[0].(ast.Expr) if !ok { - return ErrForTagInvalidArgs + return tc.PosError(args[0], "invalid argument type: %T (expected ast.Expr)", args[0]) } expr = expr2 } else if len(args) == 2 { expr2, ok := args[1].(ast.Expr) if !ok { - return ErrForTagInvalidArgs + return tc.PosError(args[1], "invalid argument type: %T (expected ast.Expr)", args[1]) } expr = expr2 } @@ -56,25 +53,24 @@ func (ft forTag) Run(tc *TagContext, block, args []ast.Node) error { if len(args) == 2 { varName, ok := unwrap(args[0]).(ast.Ident) if !ok { - return ErrForTagInvalidArgs + return tc.PosError(args[0], "invalid argument type: %T (expected ast.Ident)", expr.First) } vars = append(vars, varName.Value) - } varName, ok := unwrap(expr.First).(ast.Ident) if !ok { - return ErrForTagInvalidArgs + return tc.PosError(expr.First, "invalid argument type: %T (expected ast.Ident)", args[0]) } vars = append(vars, varName.Value) if len(expr.Rest) != 1 { - return ErrForTagInvalidArgs + return tc.PosError(expr.First, "invalid expression (expected 1 element, got %d)", len(expr.Rest)) } rest := expr.Rest[0] if rest.Operator.Value != "in" { - return ErrForTagInvalidArgs + return tc.PosError(expr.First, `invalid operator in expression (expected "in", got %q)`, rest.Operator.Value) } val, err := tc.GetValue(rest, nil) diff --git a/if_tag.go b/if_tag.go index 87525fe..acf85c8 100644 --- a/if_tag.go +++ b/if_tag.go @@ -18,27 +18,17 @@ package salix -import ( - "errors" - - "go.elara.ws/salix/ast" -) - -var ( - ErrIfExpectsOneArg = errors.New("if tag expects one argument") - ErrIfExpectsBool = errors.New("if tag expects a bool value") - ErrIfTwoElse = errors.New("if tags can only have one else tag inside") -) +import "go.elara.ws/salix/ast" // ifTag represents a #if tag within a Salix template type ifTag struct{} func (it ifTag) Run(tc *TagContext, block, args []ast.Node) error { if len(args) != 1 { - return ErrIfExpectsOneArg + return tc.PosError(tc.Tag, "expected one argument, got %d", len(args)) } - inner, err := it.findInner(block) + inner, err := it.findInner(tc, block) if err != nil { return err } @@ -50,7 +40,7 @@ func (it ifTag) Run(tc *TagContext, block, args []ast.Node) error { cond, ok := val.(bool) if !ok { - return ErrIfExpectsBool + return tc.PosError(args[0], "expected boolean argument, got %T", val) } if cond { @@ -65,7 +55,7 @@ func (it ifTag) Run(tc *TagContext, block, args []ast.Node) error { cond, ok := val.(bool) if !ok { - return ErrIfExpectsBool + return tc.PosError(elifTag.value, "expected boolean argument, got %T", val) } nextIndex := len(block) @@ -102,7 +92,7 @@ type elif struct { // findInner finds the inner elif and else tags in a block // passed to the if tag. -func (it ifTag) findInner(block []ast.Node) (innerTags, error) { +func (it ifTag) findInner(tc *TagContext, block []ast.Node) (innerTags, error) { var out innerTags for i, node := range block { if tag, ok := node.(ast.Tag); ok { @@ -112,7 +102,7 @@ func (it ifTag) findInner(block []ast.Node) (innerTags, error) { out.endRoot = i } if len(tag.Params) > 1 { - return innerTags{}, ErrIfExpectsOneArg + return innerTags{}, tc.PosError(tag.Params[1], "expected one argument, got %d", len(tag.Params)) } out.elifTags = append(out.elifTags, elif{ index: i, @@ -120,7 +110,7 @@ func (it ifTag) findInner(block []ast.Node) (innerTags, error) { }) case "else": if out.elseIndex != 0 { - return innerTags{}, ErrIfTwoElse + return innerTags{}, tc.PosError(tag, "cannot have more than one else tag in an if tag") } if out.endRoot == 0 { out.endRoot = i diff --git a/include_tag.go b/include_tag.go index 1a49fd3..8ccc8b7 100644 --- a/include_tag.go +++ b/include_tag.go @@ -34,7 +34,7 @@ type includeTag struct{} func (it includeTag) Run(tc *TagContext, block, args []ast.Node) error { if len(args) < 1 { - return ErrIncludeInvalidArgs + return tc.PosError(tc.Tag, "expected at least one argument, got %d", len(args)) } val, err := tc.GetValue(args[0], nil) @@ -44,12 +44,12 @@ func (it includeTag) Run(tc *TagContext, block, args []ast.Node) error { name, ok := val.(string) if !ok { - return ErrIncludeInvalidArgs + return tc.PosError(args[0], "invalid first argument type: %T (expected string)", val) } tmpl, ok := tc.t.ns.GetTemplate(name) if !ok { - return ErrNoSuchTemplate + return tc.PosError(args[0], "no such template: %q", name) } local := map[string]any{} @@ -64,8 +64,8 @@ func (it includeTag) Run(tc *TagContext, block, args []ast.Node) error { } local[a.Name.Value] = val } else { - // If the argument isn't an assigment, return invalid args - return ErrIncludeInvalidArgs + // If the argument isn't an assigment, return an error + return tc.PosError(tc.Tag, "invalid argument type: %T (expected ast.Assignment)", val) } } diff --git a/macro_tag.go b/macro_tag.go index 5d6a138..edc6f99 100644 --- a/macro_tag.go +++ b/macro_tag.go @@ -1,22 +1,13 @@ package salix -import ( - "errors" - - "go.elara.ws/salix/ast" -) - -var ( - ErrMacroInvalidArgs = errors.New("macro expects one string argument followed by variable assignments") - ErrNoSuchMacro = errors.New("no such template") -) +import "go.elara.ws/salix/ast" // macroTag represents an #macro tag within a Salix template type macroTag struct{} func (mt macroTag) Run(tc *TagContext, block, args []ast.Node) error { if len(args) < 1 { - return ErrMacroInvalidArgs + return tc.PosError(tc.Tag, "expected at least one argument, got %d", len(args)) } nameVal, err := tc.GetValue(args[0], nil) @@ -26,7 +17,7 @@ func (mt macroTag) Run(tc *TagContext, block, args []ast.Node) error { name, ok := nameVal.(string) if !ok { - return ErrMacroInvalidArgs + return tc.PosError(args[0], "invalid first argument type: %T (expected string)", nameVal) } if len(block) == 0 { @@ -43,13 +34,13 @@ func (mt macroTag) Run(tc *TagContext, block, args []ast.Node) error { local[a.Name.Value] = val } else { // If the argument isn't an assigment, return invalid args - return ErrMacroInvalidArgs + return tc.PosError(arg, "%s: invalid argument type: %T (expected ast.Assignment)", tc.NodeToString(arg), arg) } } macro, ok := tc.t.macros[name] if !ok { - return ErrNoSuchMacro + return tc.PosError(tc.Tag, "no such macro: %q", name) } return tc.Execute(macro, local) } else { diff --git a/parse.go b/parse.go index 41a687a..30ecaec 100644 --- a/parse.go +++ b/parse.go @@ -44,7 +44,7 @@ func (n *Namespace) Parse(r NamedReader) (Template, error) { // ParseWithFilename parses a salix template from r, using the given name. func (n *Namespace) ParseWithName(name string, r io.Reader) (Template, error) { - astVal, err := parser.ParseReader(name, r) + astVal, err := parser.ParseReader(name, r, parser.GlobalStore("name", name)) if err != nil { return Template{}, err } diff --git a/parser/parser.go b/parser/parser.go index 2fc96fe..17126b8 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -34,6 +34,7 @@ func toNodeSlice(v any) []ast.Node { func getPos(c *current) ast.Position { return ast.Position{ + Name: c.globalStore["name"].(string), Line: c.pos.line, Col: c.pos.col, } @@ -62,58 +63,58 @@ var g = &grammar{ rules: []*rule{ { name: "Root", - pos: position{line: 70, col: 1, offset: 1683}, + pos: position{line: 71, col: 1, offset: 1729}, expr: &actionExpr{ - pos: position{line: 70, col: 8, offset: 1690}, + pos: position{line: 71, col: 8, offset: 1736}, run: (*parser).callonRoot1, expr: &labeledExpr{ - pos: position{line: 70, col: 8, offset: 1690}, + pos: position{line: 71, col: 8, offset: 1736}, label: "items", expr: &zeroOrMoreExpr{ - pos: position{line: 70, col: 14, offset: 1696}, + pos: position{line: 71, col: 14, offset: 1742}, expr: &choiceExpr{ - pos: position{line: 70, col: 15, offset: 1697}, + pos: position{line: 71, col: 15, offset: 1743}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 70, col: 15, offset: 1697}, + pos: position{line: 71, col: 15, offset: 1743}, name: "Tag", }, &ruleRefExpr{ - pos: position{line: 70, col: 21, offset: 1703}, + pos: position{line: 71, col: 21, offset: 1749}, name: "ExprTag", }, &actionExpr{ - pos: position{line: 88, col: 10, offset: 2137}, + pos: position{line: 89, col: 10, offset: 2183}, run: (*parser).callonRoot7, expr: &seqExpr{ - pos: position{line: 88, col: 10, offset: 2137}, + pos: position{line: 89, col: 10, offset: 2183}, exprs: []any{ &litMatcher{ - pos: position{line: 88, col: 10, offset: 2137}, + pos: position{line: 89, col: 10, offset: 2183}, val: "#!", ignoreCase: false, want: "\"#!\"", }, &labeledExpr{ - pos: position{line: 88, col: 15, offset: 2142}, + pos: position{line: 89, col: 15, offset: 2188}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonRoot11, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -129,18 +130,18 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 266, col: 8, offset: 6362}, + pos: position{line: 267, col: 8, offset: 6408}, run: (*parser).callonRoot16, expr: &seqExpr{ - pos: position{line: 266, col: 8, offset: 6362}, + pos: position{line: 267, col: 8, offset: 6408}, exprs: []any{ &anyMatcher{ - line: 266, col: 8, offset: 6362, + line: 267, col: 8, offset: 6408, }, &zeroOrMoreExpr{ - pos: position{line: 266, col: 10, offset: 6364}, + pos: position{line: 267, col: 10, offset: 6410}, expr: &charClassMatcher{ - pos: position{line: 266, col: 10, offset: 6364}, + pos: position{line: 267, col: 10, offset: 6410}, val: "[^#]", chars: []rune{'#'}, ignoreCase: false, @@ -160,39 +161,39 @@ var g = &grammar{ }, { name: "Tag", - pos: position{line: 79, col: 1, offset: 1908}, + pos: position{line: 80, col: 1, offset: 1954}, expr: &actionExpr{ - pos: position{line: 79, col: 7, offset: 1914}, + pos: position{line: 80, col: 7, offset: 1960}, run: (*parser).callonTag1, expr: &seqExpr{ - pos: position{line: 79, col: 7, offset: 1914}, + pos: position{line: 80, col: 7, offset: 1960}, exprs: []any{ &litMatcher{ - pos: position{line: 79, col: 7, offset: 1914}, + pos: position{line: 80, col: 7, offset: 1960}, val: "#", ignoreCase: false, want: "\"#\"", }, &labeledExpr{ - pos: position{line: 79, col: 11, offset: 1918}, + pos: position{line: 80, col: 11, offset: 1964}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonTag5, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -205,23 +206,23 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 79, col: 22, offset: 1929}, + pos: position{line: 80, col: 22, offset: 1975}, label: "params", expr: &zeroOrOneExpr{ - pos: position{line: 79, col: 29, offset: 1936}, + pos: position{line: 80, col: 29, offset: 1982}, expr: &ruleRefExpr{ - pos: position{line: 79, col: 29, offset: 1936}, + pos: position{line: 80, col: 29, offset: 1982}, name: "ParamList", }, }, }, &labeledExpr{ - pos: position{line: 79, col: 40, offset: 1947}, + pos: position{line: 80, col: 40, offset: 1993}, label: "body", expr: &zeroOrOneExpr{ - pos: position{line: 79, col: 45, offset: 1952}, + pos: position{line: 80, col: 45, offset: 1998}, expr: &litMatcher{ - pos: position{line: 79, col: 45, offset: 1952}, + pos: position{line: 80, col: 45, offset: 1998}, val: ":", ignoreCase: false, want: "\":\"", @@ -236,29 +237,29 @@ var g = &grammar{ }, { name: "ExprTag", - pos: position{line: 95, col: 1, offset: 2257}, + pos: position{line: 96, col: 1, offset: 2303}, expr: &actionExpr{ - pos: position{line: 95, col: 11, offset: 2267}, + pos: position{line: 96, col: 11, offset: 2313}, run: (*parser).callonExprTag1, expr: &seqExpr{ - pos: position{line: 95, col: 11, offset: 2267}, + pos: position{line: 96, col: 11, offset: 2313}, exprs: []any{ &litMatcher{ - pos: position{line: 95, col: 11, offset: 2267}, + pos: position{line: 96, col: 11, offset: 2313}, val: "#(", ignoreCase: false, want: "\"#(\"", }, &labeledExpr{ - pos: position{line: 95, col: 16, offset: 2272}, + pos: position{line: 96, col: 16, offset: 2318}, label: "item", expr: &ruleRefExpr{ - pos: position{line: 95, col: 21, offset: 2277}, + pos: position{line: 96, col: 21, offset: 2323}, name: "Expr", }, }, &litMatcher{ - pos: position{line: 95, col: 26, offset: 2282}, + pos: position{line: 96, col: 26, offset: 2328}, val: ")", ignoreCase: false, want: "\")\"", @@ -271,20 +272,20 @@ var g = &grammar{ }, { name: "Expr", - pos: position{line: 102, col: 1, offset: 2390}, + pos: position{line: 103, col: 1, offset: 2436}, expr: &choiceExpr{ - pos: position{line: 102, col: 8, offset: 2397}, + pos: position{line: 103, col: 8, offset: 2443}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 102, col: 8, offset: 2397}, + pos: position{line: 103, col: 8, offset: 2443}, name: "Ternary", }, &ruleRefExpr{ - pos: position{line: 102, col: 18, offset: 2407}, + pos: position{line: 103, col: 18, offset: 2453}, name: "Assignment", }, &ruleRefExpr{ - pos: position{line: 102, col: 31, offset: 2420}, + pos: position{line: 103, col: 31, offset: 2466}, name: "LogicalExpr", }, }, @@ -294,36 +295,36 @@ var g = &grammar{ }, { name: "Assignable", - pos: position{line: 103, col: 1, offset: 2432}, + pos: position{line: 104, col: 1, offset: 2478}, expr: &choiceExpr{ - pos: position{line: 103, col: 14, offset: 2445}, + pos: position{line: 104, col: 14, offset: 2491}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 103, col: 14, offset: 2445}, + pos: position{line: 104, col: 14, offset: 2491}, name: "Ternary", }, &ruleRefExpr{ - pos: position{line: 103, col: 24, offset: 2455}, + pos: position{line: 104, col: 24, offset: 2501}, name: "LogicalExpr", }, }, }, - leader: false, + leader: true, leftRecursive: true, }, { name: "LogicalExpr", - pos: position{line: 105, col: 1, offset: 2468}, + pos: position{line: 106, col: 1, offset: 2514}, expr: &actionExpr{ - pos: position{line: 105, col: 15, offset: 2482}, + pos: position{line: 106, col: 15, offset: 2528}, run: (*parser).callonLogicalExpr1, expr: &seqExpr{ - pos: position{line: 105, col: 15, offset: 2482}, + pos: position{line: 106, col: 15, offset: 2528}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -331,25 +332,25 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 105, col: 17, offset: 2484}, + pos: position{line: 106, col: 17, offset: 2530}, label: "first", expr: &ruleRefExpr{ - pos: position{line: 105, col: 23, offset: 2490}, + pos: position{line: 106, col: 23, offset: 2536}, name: "ComparisonExpr", }, }, &labeledExpr{ - pos: position{line: 105, col: 38, offset: 2505}, + pos: position{line: 106, col: 38, offset: 2551}, label: "rest", expr: &zeroOrMoreExpr{ - pos: position{line: 105, col: 43, offset: 2510}, + pos: position{line: 106, col: 43, offset: 2556}, expr: &seqExpr{ - pos: position{line: 105, col: 44, offset: 2511}, + pos: position{line: 106, col: 44, offset: 2557}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -357,19 +358,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 245, col: 13, offset: 5928}, + pos: position{line: 246, col: 13, offset: 5974}, run: (*parser).callonLogicalExpr12, expr: &choiceExpr{ - pos: position{line: 245, col: 14, offset: 5929}, + pos: position{line: 246, col: 14, offset: 5975}, alternatives: []any{ &litMatcher{ - pos: position{line: 245, col: 14, offset: 5929}, + pos: position{line: 246, col: 14, offset: 5975}, val: "||", ignoreCase: false, want: "\"||\"", }, &litMatcher{ - pos: position{line: 245, col: 21, offset: 5936}, + pos: position{line: 246, col: 21, offset: 5982}, val: "&&", ignoreCase: false, want: "\"&&\"", @@ -378,9 +379,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -388,7 +389,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 105, col: 58, offset: 2525}, + pos: position{line: 106, col: 58, offset: 2571}, name: "ComparisonExpr", }, }, @@ -396,9 +397,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -413,17 +414,17 @@ var g = &grammar{ }, { name: "ComparisonExpr", - pos: position{line: 109, col: 1, offset: 2588}, + pos: position{line: 110, col: 1, offset: 2634}, expr: &actionExpr{ - pos: position{line: 109, col: 18, offset: 2605}, + pos: position{line: 110, col: 18, offset: 2651}, run: (*parser).callonComparisonExpr1, expr: &seqExpr{ - pos: position{line: 109, col: 18, offset: 2605}, + pos: position{line: 110, col: 18, offset: 2651}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -431,25 +432,25 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 109, col: 20, offset: 2607}, + pos: position{line: 110, col: 20, offset: 2653}, label: "first", expr: &ruleRefExpr{ - pos: position{line: 109, col: 26, offset: 2613}, + pos: position{line: 110, col: 26, offset: 2659}, name: "ArithmeticExpr", }, }, &labeledExpr{ - pos: position{line: 109, col: 41, offset: 2628}, + pos: position{line: 110, col: 41, offset: 2674}, label: "rest", expr: &zeroOrMoreExpr{ - pos: position{line: 109, col: 46, offset: 2633}, + pos: position{line: 110, col: 46, offset: 2679}, expr: &seqExpr{ - pos: position{line: 109, col: 47, offset: 2634}, + pos: position{line: 110, col: 47, offset: 2680}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -457,38 +458,38 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 16, offset: 6061}, + pos: position{line: 253, col: 16, offset: 6107}, run: (*parser).callonComparisonExpr12, expr: &choiceExpr{ - pos: position{line: 252, col: 17, offset: 6062}, + pos: position{line: 253, col: 17, offset: 6108}, alternatives: []any{ &litMatcher{ - pos: position{line: 252, col: 17, offset: 6062}, + pos: position{line: 253, col: 17, offset: 6108}, val: "==", ignoreCase: false, want: "\"==\"", }, &litMatcher{ - pos: position{line: 252, col: 24, offset: 6069}, + pos: position{line: 253, col: 24, offset: 6115}, val: "<=", ignoreCase: false, want: "\"<=\"", }, &litMatcher{ - pos: position{line: 252, col: 31, offset: 6076}, + pos: position{line: 253, col: 31, offset: 6122}, val: ">=", ignoreCase: false, want: "\">=\"", }, &charClassMatcher{ - pos: position{line: 252, col: 38, offset: 6083}, + pos: position{line: 253, col: 38, offset: 6129}, val: "[<>]", chars: []rune{'<', '>'}, ignoreCase: false, inverted: false, }, &litMatcher{ - pos: position{line: 252, col: 50, offset: 6095}, + pos: position{line: 253, col: 50, offset: 6141}, val: "in", ignoreCase: true, want: "\"in\"i", @@ -497,9 +498,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -507,7 +508,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 109, col: 64, offset: 2651}, + pos: position{line: 110, col: 64, offset: 2697}, name: "ArithmeticExpr", }, }, @@ -515,9 +516,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -532,17 +533,17 @@ var g = &grammar{ }, { name: "ArithmeticExpr", - pos: position{line: 113, col: 1, offset: 2714}, + pos: position{line: 114, col: 1, offset: 2760}, expr: &actionExpr{ - pos: position{line: 113, col: 18, offset: 2731}, + pos: position{line: 114, col: 18, offset: 2777}, run: (*parser).callonArithmeticExpr1, expr: &seqExpr{ - pos: position{line: 113, col: 18, offset: 2731}, + pos: position{line: 114, col: 18, offset: 2777}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -550,25 +551,25 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 113, col: 20, offset: 2733}, + pos: position{line: 114, col: 20, offset: 2779}, label: "first", expr: &ruleRefExpr{ - pos: position{line: 113, col: 26, offset: 2739}, + pos: position{line: 114, col: 26, offset: 2785}, name: "Value", }, }, &labeledExpr{ - pos: position{line: 113, col: 32, offset: 2745}, + pos: position{line: 114, col: 32, offset: 2791}, label: "rest", expr: &zeroOrMoreExpr{ - pos: position{line: 113, col: 37, offset: 2750}, + pos: position{line: 114, col: 37, offset: 2796}, expr: &seqExpr{ - pos: position{line: 113, col: 38, offset: 2751}, + pos: position{line: 114, col: 38, offset: 2797}, exprs: []any{ &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -576,10 +577,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 259, col: 16, offset: 6221}, + pos: position{line: 260, col: 16, offset: 6267}, run: (*parser).callonArithmeticExpr12, expr: &charClassMatcher{ - pos: position{line: 259, col: 17, offset: 6222}, + pos: position{line: 260, col: 17, offset: 6268}, val: "[+-/*%]", chars: []rune{'+', '-', '/', '*', '%'}, ignoreCase: false, @@ -587,9 +588,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -597,7 +598,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 113, col: 55, offset: 2768}, + pos: position{line: 114, col: 55, offset: 2814}, name: "Value", }, }, @@ -605,9 +606,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -622,29 +623,29 @@ var g = &grammar{ }, { name: "ParenExpr", - pos: position{line: 117, col: 1, offset: 2822}, + pos: position{line: 118, col: 1, offset: 2868}, expr: &actionExpr{ - pos: position{line: 117, col: 13, offset: 2834}, + pos: position{line: 118, col: 13, offset: 2880}, run: (*parser).callonParenExpr1, expr: &seqExpr{ - pos: position{line: 117, col: 13, offset: 2834}, + pos: position{line: 118, col: 13, offset: 2880}, exprs: []any{ &litMatcher{ - pos: position{line: 117, col: 13, offset: 2834}, + pos: position{line: 118, col: 13, offset: 2880}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 117, col: 17, offset: 2838}, + pos: position{line: 118, col: 17, offset: 2884}, label: "expr", expr: &ruleRefExpr{ - pos: position{line: 117, col: 22, offset: 2843}, + pos: position{line: 118, col: 22, offset: 2889}, name: "Expr", }, }, &litMatcher{ - pos: position{line: 117, col: 27, offset: 2848}, + pos: position{line: 118, col: 27, offset: 2894}, val: ")", ignoreCase: false, want: "\")\"", @@ -657,46 +658,46 @@ var g = &grammar{ }, { name: "ParamList", - pos: position{line: 121, col: 1, offset: 2878}, + pos: position{line: 122, col: 1, offset: 2924}, expr: &actionExpr{ - pos: position{line: 121, col: 13, offset: 2890}, + pos: position{line: 122, col: 13, offset: 2936}, run: (*parser).callonParamList1, expr: &seqExpr{ - pos: position{line: 121, col: 13, offset: 2890}, + pos: position{line: 122, col: 13, offset: 2936}, exprs: []any{ &litMatcher{ - pos: position{line: 121, col: 13, offset: 2890}, + pos: position{line: 122, col: 13, offset: 2936}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 121, col: 17, offset: 2894}, + pos: position{line: 122, col: 17, offset: 2940}, label: "params", expr: &zeroOrOneExpr{ - pos: position{line: 121, col: 24, offset: 2901}, + pos: position{line: 122, col: 24, offset: 2947}, expr: &seqExpr{ - pos: position{line: 121, col: 25, offset: 2902}, + pos: position{line: 122, col: 25, offset: 2948}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 121, col: 25, offset: 2902}, + pos: position{line: 122, col: 25, offset: 2948}, name: "Expr", }, &zeroOrMoreExpr{ - pos: position{line: 121, col: 30, offset: 2907}, + pos: position{line: 122, col: 30, offset: 2953}, expr: &seqExpr{ - pos: position{line: 121, col: 32, offset: 2909}, + pos: position{line: 122, col: 32, offset: 2955}, exprs: []any{ &litMatcher{ - pos: position{line: 121, col: 32, offset: 2909}, + pos: position{line: 122, col: 32, offset: 2955}, val: ",", ignoreCase: false, want: "\",\"", }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -704,7 +705,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 121, col: 38, offset: 2915}, + pos: position{line: 122, col: 38, offset: 2961}, name: "Expr", }, }, @@ -715,7 +716,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 121, col: 49, offset: 2926}, + pos: position{line: 122, col: 49, offset: 2972}, val: ")", ignoreCase: false, want: "\")\"", @@ -728,20 +729,20 @@ var g = &grammar{ }, { name: "Value", - pos: position{line: 135, col: 1, offset: 3288}, + pos: position{line: 136, col: 1, offset: 3334}, expr: &actionExpr{ - pos: position{line: 135, col: 9, offset: 3296}, + pos: position{line: 136, col: 9, offset: 3342}, run: (*parser).callonValue1, expr: &seqExpr{ - pos: position{line: 135, col: 9, offset: 3296}, + pos: position{line: 136, col: 9, offset: 3342}, exprs: []any{ &labeledExpr{ - pos: position{line: 135, col: 9, offset: 3296}, + pos: position{line: 136, col: 9, offset: 3342}, label: "not", expr: &zeroOrOneExpr{ - pos: position{line: 135, col: 13, offset: 3300}, + pos: position{line: 136, col: 13, offset: 3346}, expr: &litMatcher{ - pos: position{line: 135, col: 13, offset: 3300}, + pos: position{line: 136, col: 13, offset: 3346}, val: "!", ignoreCase: false, want: "\"!\"", @@ -749,42 +750,42 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 135, col: 18, offset: 3305}, + pos: position{line: 136, col: 18, offset: 3351}, label: "node", expr: &choiceExpr{ - pos: position{line: 135, col: 24, offset: 3311}, + pos: position{line: 136, col: 24, offset: 3357}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 135, col: 24, offset: 3311}, + pos: position{line: 136, col: 24, offset: 3357}, name: "MethodCall", }, &ruleRefExpr{ - pos: position{line: 135, col: 37, offset: 3324}, + pos: position{line: 136, col: 37, offset: 3370}, name: "FieldAccess", }, &ruleRefExpr{ - pos: position{line: 135, col: 51, offset: 3338}, + pos: position{line: 136, col: 51, offset: 3384}, name: "Index", }, &actionExpr{ - pos: position{line: 221, col: 10, offset: 5431}, + pos: position{line: 222, col: 10, offset: 5477}, run: (*parser).callonValue11, expr: &seqExpr{ - pos: position{line: 221, col: 10, offset: 5431}, + pos: position{line: 222, col: 10, offset: 5477}, exprs: []any{ &litMatcher{ - pos: position{line: 221, col: 10, offset: 5431}, + pos: position{line: 222, col: 10, offset: 5477}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 221, col: 14, offset: 5435}, + pos: position{line: 222, col: 14, offset: 5481}, label: "value", expr: &zeroOrMoreExpr{ - pos: position{line: 221, col: 20, offset: 5441}, + pos: position{line: 222, col: 20, offset: 5487}, expr: &charClassMatcher{ - pos: position{line: 221, col: 20, offset: 5441}, + pos: position{line: 222, col: 20, offset: 5487}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -793,7 +794,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 221, col: 26, offset: 5447}, + pos: position{line: 222, col: 26, offset: 5493}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -802,24 +803,24 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 229, col: 13, offset: 5598}, + pos: position{line: 230, col: 13, offset: 5644}, run: (*parser).callonValue18, expr: &seqExpr{ - pos: position{line: 229, col: 13, offset: 5598}, + pos: position{line: 230, col: 13, offset: 5644}, exprs: []any{ &litMatcher{ - pos: position{line: 229, col: 13, offset: 5598}, + pos: position{line: 230, col: 13, offset: 5644}, val: "`", ignoreCase: false, want: "\"`\"", }, &labeledExpr{ - pos: position{line: 229, col: 17, offset: 5602}, + pos: position{line: 230, col: 17, offset: 5648}, label: "value", expr: &zeroOrMoreExpr{ - pos: position{line: 229, col: 23, offset: 5608}, + pos: position{line: 230, col: 23, offset: 5654}, expr: &charClassMatcher{ - pos: position{line: 229, col: 23, offset: 5608}, + pos: position{line: 230, col: 23, offset: 5654}, val: "[^`]", chars: []rune{'`'}, ignoreCase: false, @@ -828,7 +829,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 229, col: 29, offset: 5614}, + pos: position{line: 230, col: 29, offset: 5660}, val: "`", ignoreCase: false, want: "\"`\"", @@ -837,30 +838,30 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 9, offset: 5250}, + pos: position{line: 214, col: 9, offset: 5296}, run: (*parser).callonValue25, expr: &seqExpr{ - pos: position{line: 213, col: 9, offset: 5250}, + pos: position{line: 214, col: 9, offset: 5296}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 213, col: 9, offset: 5250}, + pos: position{line: 214, col: 9, offset: 5296}, expr: &litMatcher{ - pos: position{line: 213, col: 9, offset: 5250}, + pos: position{line: 214, col: 9, offset: 5296}, val: "-", ignoreCase: false, want: "\"-\"", }, }, &labeledExpr{ - pos: position{line: 213, col: 14, offset: 5255}, + pos: position{line: 214, col: 14, offset: 5301}, label: "value", expr: &seqExpr{ - pos: position{line: 213, col: 21, offset: 5262}, + pos: position{line: 214, col: 21, offset: 5308}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 213, col: 21, offset: 5262}, + pos: position{line: 214, col: 21, offset: 5308}, expr: &charClassMatcher{ - pos: position{line: 213, col: 21, offset: 5262}, + pos: position{line: 214, col: 21, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -868,15 +869,15 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 213, col: 28, offset: 5269}, + pos: position{line: 214, col: 28, offset: 5315}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 213, col: 32, offset: 5273}, + pos: position{line: 214, col: 32, offset: 5319}, expr: &charClassMatcher{ - pos: position{line: 213, col: 32, offset: 5273}, + pos: position{line: 214, col: 32, offset: 5319}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -890,36 +891,36 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 205, col: 11, offset: 5039}, + pos: position{line: 206, col: 11, offset: 5085}, run: (*parser).callonValue36, expr: &seqExpr{ - pos: position{line: 205, col: 11, offset: 5039}, + pos: position{line: 206, col: 11, offset: 5085}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 205, col: 11, offset: 5039}, + pos: position{line: 206, col: 11, offset: 5085}, expr: &litMatcher{ - pos: position{line: 205, col: 11, offset: 5039}, + pos: position{line: 206, col: 11, offset: 5085}, val: "-", ignoreCase: false, want: "\"-\"", }, }, &choiceExpr{ - pos: position{line: 205, col: 17, offset: 5045}, + pos: position{line: 206, col: 17, offset: 5091}, alternatives: []any{ &seqExpr{ - pos: position{line: 205, col: 17, offset: 5045}, + pos: position{line: 206, col: 17, offset: 5091}, exprs: []any{ &litMatcher{ - pos: position{line: 205, col: 17, offset: 5045}, + pos: position{line: 206, col: 17, offset: 5091}, val: "0x", ignoreCase: false, want: "\"0x\"", }, &oneOrMoreExpr{ - pos: position{line: 205, col: 22, offset: 5050}, + pos: position{line: 206, col: 22, offset: 5096}, expr: &charClassMatcher{ - pos: position{line: 205, col: 22, offset: 5050}, + pos: position{line: 206, col: 22, offset: 5096}, val: "[0-9a-f]i", ranges: []rune{'0', '9', 'a', 'f'}, ignoreCase: true, @@ -929,18 +930,18 @@ var g = &grammar{ }, }, &seqExpr{ - pos: position{line: 205, col: 35, offset: 5063}, + pos: position{line: 206, col: 35, offset: 5109}, exprs: []any{ &litMatcher{ - pos: position{line: 205, col: 35, offset: 5063}, + pos: position{line: 206, col: 35, offset: 5109}, val: "0o", ignoreCase: false, want: "\"0o\"", }, &oneOrMoreExpr{ - pos: position{line: 205, col: 40, offset: 5068}, + pos: position{line: 206, col: 40, offset: 5114}, expr: &charClassMatcher{ - pos: position{line: 205, col: 40, offset: 5068}, + pos: position{line: 206, col: 40, offset: 5114}, val: "[0-7]", ranges: []rune{'0', '7'}, ignoreCase: false, @@ -950,18 +951,18 @@ var g = &grammar{ }, }, &seqExpr{ - pos: position{line: 205, col: 49, offset: 5077}, + pos: position{line: 206, col: 49, offset: 5123}, exprs: []any{ &litMatcher{ - pos: position{line: 205, col: 49, offset: 5077}, + pos: position{line: 206, col: 49, offset: 5123}, val: "0b", ignoreCase: false, want: "\"0b\"", }, &oneOrMoreExpr{ - pos: position{line: 205, col: 54, offset: 5082}, + pos: position{line: 206, col: 54, offset: 5128}, expr: &charClassMatcher{ - pos: position{line: 205, col: 54, offset: 5082}, + pos: position{line: 206, col: 54, offset: 5128}, val: "[01]", chars: []rune{'0', '1'}, ignoreCase: false, @@ -971,9 +972,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 205, col: 62, offset: 5090}, + pos: position{line: 206, col: 62, offset: 5136}, expr: &charClassMatcher{ - pos: position{line: 205, col: 62, offset: 5090}, + pos: position{line: 206, col: 62, offset: 5136}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -986,19 +987,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 237, col: 8, offset: 5760}, + pos: position{line: 238, col: 8, offset: 5806}, run: (*parser).callonValue55, expr: &choiceExpr{ - pos: position{line: 237, col: 9, offset: 5761}, + pos: position{line: 238, col: 9, offset: 5807}, alternatives: []any{ &litMatcher{ - pos: position{line: 237, col: 9, offset: 5761}, + pos: position{line: 238, col: 9, offset: 5807}, val: "true", ignoreCase: true, want: "\"true\"i", }, &litMatcher{ - pos: position{line: 237, col: 19, offset: 5771}, + pos: position{line: 238, col: 19, offset: 5817}, val: "false", ignoreCase: true, want: "\"false\"i", @@ -1007,30 +1008,30 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 135, col: 105, offset: 3392}, + pos: position{line: 136, col: 105, offset: 3438}, name: "FuncCall", }, &ruleRefExpr{ - pos: position{line: 135, col: 116, offset: 3403}, + pos: position{line: 136, col: 116, offset: 3449}, name: "VariableOr", }, &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonValue61, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1042,7 +1043,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 135, col: 137, offset: 3424}, + pos: position{line: 136, col: 137, offset: 3470}, name: "ParenExpr", }, }, @@ -1056,33 +1057,33 @@ var g = &grammar{ }, { name: "VariableOr", - pos: position{line: 142, col: 1, offset: 3530}, + pos: position{line: 143, col: 1, offset: 3576}, expr: &actionExpr{ - pos: position{line: 142, col: 14, offset: 3543}, + pos: position{line: 143, col: 14, offset: 3589}, run: (*parser).callonVariableOr1, expr: &seqExpr{ - pos: position{line: 142, col: 14, offset: 3543}, + pos: position{line: 143, col: 14, offset: 3589}, exprs: []any{ &labeledExpr{ - pos: position{line: 142, col: 14, offset: 3543}, + pos: position{line: 143, col: 14, offset: 3589}, label: "variable", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonVariableOr4, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1095,9 +1096,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1105,15 +1106,15 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 142, col: 31, offset: 3560}, + pos: position{line: 143, col: 31, offset: 3606}, val: "|", ignoreCase: false, want: "\"|\"", }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1121,10 +1122,10 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 142, col: 37, offset: 3566}, + pos: position{line: 143, col: 37, offset: 3612}, label: "or", expr: &ruleRefExpr{ - pos: position{line: 142, col: 40, offset: 3569}, + pos: position{line: 143, col: 40, offset: 3615}, name: "Assignable", }, }, @@ -1136,33 +1137,33 @@ var g = &grammar{ }, { name: "Assignment", - pos: position{line: 149, col: 1, offset: 3696}, + pos: position{line: 150, col: 1, offset: 3742}, expr: &actionExpr{ - pos: position{line: 149, col: 14, offset: 3709}, + pos: position{line: 150, col: 14, offset: 3755}, run: (*parser).callonAssignment1, expr: &seqExpr{ - pos: position{line: 149, col: 14, offset: 3709}, + pos: position{line: 150, col: 14, offset: 3755}, exprs: []any{ &labeledExpr{ - pos: position{line: 149, col: 14, offset: 3709}, + pos: position{line: 150, col: 14, offset: 3755}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonAssignment4, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1175,9 +1176,9 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1185,15 +1186,15 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 149, col: 27, offset: 3722}, + pos: position{line: 150, col: 27, offset: 3768}, val: "=", ignoreCase: false, want: "\"=\"", }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1201,10 +1202,10 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 149, col: 33, offset: 3728}, + pos: position{line: 150, col: 33, offset: 3774}, label: "value", expr: &ruleRefExpr{ - pos: position{line: 149, col: 39, offset: 3734}, + pos: position{line: 150, col: 39, offset: 3780}, name: "Assignable", }, }, @@ -1216,25 +1217,25 @@ var g = &grammar{ }, { name: "Ternary", - pos: position{line: 157, col: 1, offset: 3889}, + pos: position{line: 158, col: 1, offset: 3935}, expr: &actionExpr{ - pos: position{line: 157, col: 11, offset: 3899}, + pos: position{line: 158, col: 11, offset: 3945}, run: (*parser).callonTernary1, expr: &seqExpr{ - pos: position{line: 157, col: 11, offset: 3899}, + pos: position{line: 158, col: 11, offset: 3945}, exprs: []any{ &labeledExpr{ - pos: position{line: 157, col: 11, offset: 3899}, + pos: position{line: 158, col: 11, offset: 3945}, label: "cond", expr: &ruleRefExpr{ - pos: position{line: 157, col: 16, offset: 3904}, + pos: position{line: 158, col: 16, offset: 3950}, name: "Assignable", }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1242,15 +1243,15 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 157, col: 29, offset: 3917}, + pos: position{line: 158, col: 29, offset: 3963}, val: "?", ignoreCase: false, want: "\"?\"", }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1258,17 +1259,17 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 157, col: 35, offset: 3923}, + pos: position{line: 158, col: 35, offset: 3969}, label: "ifTrue", expr: &ruleRefExpr{ - pos: position{line: 157, col: 42, offset: 3930}, + pos: position{line: 158, col: 42, offset: 3976}, name: "Value", }, }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1276,15 +1277,15 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 157, col: 50, offset: 3938}, + pos: position{line: 158, col: 50, offset: 3984}, val: ":", ignoreCase: false, want: "\":\"", }, &zeroOrMoreExpr{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, expr: &charClassMatcher{ - pos: position{line: 268, col: 18, offset: 6450}, + pos: position{line: 269, col: 18, offset: 6496}, val: "[ \\t\\r\\n]", chars: []rune{' ', '\t', '\r', '\n'}, ignoreCase: false, @@ -1292,62 +1293,62 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 157, col: 56, offset: 3944}, + pos: position{line: 158, col: 56, offset: 3990}, label: "elseVal", expr: &ruleRefExpr{ - pos: position{line: 157, col: 64, offset: 3952}, + pos: position{line: 158, col: 64, offset: 3998}, name: "Value", }, }, }, }, }, - leader: true, + leader: false, leftRecursive: true, }, { name: "MethodCall", - pos: position{line: 165, col: 1, offset: 4111}, + pos: position{line: 166, col: 1, offset: 4157}, expr: &actionExpr{ - pos: position{line: 165, col: 14, offset: 4124}, + pos: position{line: 166, col: 14, offset: 4170}, run: (*parser).callonMethodCall1, expr: &seqExpr{ - pos: position{line: 165, col: 14, offset: 4124}, + pos: position{line: 166, col: 14, offset: 4170}, exprs: []any{ &labeledExpr{ - pos: position{line: 165, col: 14, offset: 4124}, + pos: position{line: 166, col: 14, offset: 4170}, label: "value", expr: &ruleRefExpr{ - pos: position{line: 165, col: 20, offset: 4130}, + pos: position{line: 166, col: 20, offset: 4176}, name: "Value", }, }, &litMatcher{ - pos: position{line: 165, col: 26, offset: 4136}, + pos: position{line: 166, col: 26, offset: 4182}, val: ".", ignoreCase: false, want: "\".\"", }, &labeledExpr{ - pos: position{line: 165, col: 30, offset: 4140}, + pos: position{line: 166, col: 30, offset: 4186}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonMethodCall7, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1360,10 +1361,10 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 165, col: 41, offset: 4151}, + pos: position{line: 166, col: 41, offset: 4197}, label: "params", expr: &ruleRefExpr{ - pos: position{line: 165, col: 48, offset: 4158}, + pos: position{line: 166, col: 48, offset: 4204}, name: "ParamList", }, }, @@ -1375,37 +1376,37 @@ var g = &grammar{ }, { name: "Index", - pos: position{line: 174, col: 1, offset: 4351}, + pos: position{line: 175, col: 1, offset: 4397}, expr: &actionExpr{ - pos: position{line: 174, col: 9, offset: 4359}, + pos: position{line: 175, col: 9, offset: 4405}, run: (*parser).callonIndex1, expr: &seqExpr{ - pos: position{line: 174, col: 9, offset: 4359}, + pos: position{line: 175, col: 9, offset: 4405}, exprs: []any{ &labeledExpr{ - pos: position{line: 174, col: 9, offset: 4359}, + pos: position{line: 175, col: 9, offset: 4405}, label: "value", expr: &ruleRefExpr{ - pos: position{line: 174, col: 15, offset: 4365}, + pos: position{line: 175, col: 15, offset: 4411}, name: "Value", }, }, &litMatcher{ - pos: position{line: 174, col: 21, offset: 4371}, + pos: position{line: 175, col: 21, offset: 4417}, val: "[", ignoreCase: false, want: "\"[\"", }, &labeledExpr{ - pos: position{line: 174, col: 25, offset: 4375}, + pos: position{line: 175, col: 25, offset: 4421}, label: "index", expr: &ruleRefExpr{ - pos: position{line: 174, col: 31, offset: 4381}, + pos: position{line: 175, col: 31, offset: 4427}, name: "Value", }, }, &litMatcher{ - pos: position{line: 174, col: 37, offset: 4387}, + pos: position{line: 175, col: 37, offset: 4433}, val: "]", ignoreCase: false, want: "\"]\"", @@ -1418,47 +1419,47 @@ var g = &grammar{ }, { name: "FieldAccess", - pos: position{line: 182, col: 1, offset: 4530}, + pos: position{line: 183, col: 1, offset: 4576}, expr: &actionExpr{ - pos: position{line: 182, col: 15, offset: 4544}, + pos: position{line: 183, col: 15, offset: 4590}, run: (*parser).callonFieldAccess1, expr: &seqExpr{ - pos: position{line: 182, col: 15, offset: 4544}, + pos: position{line: 183, col: 15, offset: 4590}, exprs: []any{ &labeledExpr{ - pos: position{line: 182, col: 15, offset: 4544}, + pos: position{line: 183, col: 15, offset: 4590}, label: "value", expr: &ruleRefExpr{ - pos: position{line: 182, col: 21, offset: 4550}, + pos: position{line: 183, col: 21, offset: 4596}, name: "Value", }, }, &litMatcher{ - pos: position{line: 182, col: 27, offset: 4556}, + pos: position{line: 183, col: 27, offset: 4602}, val: ".", ignoreCase: false, want: "\".\"", }, &labeledExpr{ - pos: position{line: 182, col: 31, offset: 4560}, + pos: position{line: 183, col: 31, offset: 4606}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonFieldAccess7, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1478,33 +1479,33 @@ var g = &grammar{ }, { name: "FuncCall", - pos: position{line: 197, col: 1, offset: 4845}, + pos: position{line: 198, col: 1, offset: 4891}, expr: &actionExpr{ - pos: position{line: 197, col: 12, offset: 4856}, + pos: position{line: 198, col: 12, offset: 4902}, run: (*parser).callonFuncCall1, expr: &seqExpr{ - pos: position{line: 197, col: 12, offset: 4856}, + pos: position{line: 198, col: 12, offset: 4902}, exprs: []any{ &labeledExpr{ - pos: position{line: 197, col: 12, offset: 4856}, + pos: position{line: 198, col: 12, offset: 4902}, label: "name", expr: &actionExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, run: (*parser).callonFuncCall4, expr: &seqExpr{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, exprs: []any{ &charClassMatcher{ - pos: position{line: 190, col: 9, offset: 4724}, + pos: position{line: 191, col: 9, offset: 4770}, val: "[a-z]i", ranges: []rune{'a', 'z'}, ignoreCase: true, inverted: false, }, &zeroOrMoreExpr{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, expr: &charClassMatcher{ - pos: position{line: 190, col: 16, offset: 4731}, + pos: position{line: 191, col: 16, offset: 4777}, val: "[_a-z0-9]i", chars: []rune{'_'}, ranges: []rune{'a', 'z', '0', '9'}, @@ -1517,10 +1518,10 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 197, col: 23, offset: 4867}, + pos: position{line: 198, col: 23, offset: 4913}, label: "params", expr: &ruleRefExpr{ - pos: position{line: 197, col: 30, offset: 4874}, + pos: position{line: 198, col: 30, offset: 4920}, name: "ParamList", }, }, diff --git a/parser/salix.peg b/parser/salix.peg index 8dad5b7..52b8340 100644 --- a/parser/salix.peg +++ b/parser/salix.peg @@ -41,6 +41,7 @@ func toNodeSlice(v any) []ast.Node { func getPos(c *current) ast.Position { return ast.Position{ + Name: c.globalStore["name"].(string), Line: c.pos.line, Col: c.pos.col, } diff --git a/salix.go b/salix.go index 751511e..6584a05 100644 --- a/salix.go +++ b/salix.go @@ -25,30 +25,11 @@ import ( "html" "io" "reflect" + "strconv" "go.elara.ws/salix/ast" ) -var ( - ErrNoSuchFunc = errors.New("no such function") - ErrNoSuchVar = errors.New("no such variable") - ErrNoSuchMethod = errors.New("no such method") - ErrNoSuchField = errors.New("no such field") - ErrNoSuchTag = errors.New("no such tag") - ErrNotOperatorNonBool = errors.New("not operator cannot be used on a non-bool value") - ErrParamNumMismatch = errors.New("incorrect parameter amount") - ErrIncorrectParamType = errors.New("incorrect parameter type for function") - ErrEndTagWithoutStart = errors.New("end tag without a start tag") - ErrIncorrectIndexType = errors.New("incorrect index type") - ErrIndexOutOfRange = errors.New("index out of range") - ErrMapIndexNotFound = errors.New("map index not found") - ErrMapInvalidIndexType = errors.New("invalid map index type") - ErrFuncTooManyReturns = errors.New("template functions can only have two return values") - ErrFuncNoReturns = errors.New("template functions must return at least one value") - ErrFuncSecondReturnType = errors.New("the second return value of a template function must be an error") - ErrTernaryCondBool = errors.New("ternary condition must be a boolean") -) - // HTML represents unescaped HTML strings type HTML string @@ -112,7 +93,7 @@ func (t *Template) execute(w io.Writer, nodes []ast.Node, local map[string]any) case ast.Text: _, err := w.Write(node.Data) if err != nil { - return t.posError(node, "%w", err) + return ast.PosError(node, "%w", err) } case ast.Tag: newOffset, err := t.execTag(node, w, nodes, i, local) @@ -125,7 +106,7 @@ func (t *Template) execute(w io.Writer, nodes []ast.Node, local map[string]any) // should be taken care of by execTag, so if we do, // return an error because execTag was never called, // which means there was no start tag. - return ErrEndTagWithoutStart + return ast.PosError(node, "end tag without a matching start tag: %s", node.Name.Value) case ast.ExprTag: v, err := t.getValue(node.Value, local) if err != nil { @@ -235,6 +216,76 @@ func (t *Template) getValue(node ast.Node, local map[string]any) (any, error) { } } +// valueToString converts an AST node to a textual representation +// for the user to see, such as in error messages. This does not +// directly correlate to Salix source code. +func valueToString(node ast.Node) string { + if node == nil { + return "" + } + + switch node := node.(type) { + case ast.Ident: + return node.Value + case ast.String: + return strconv.Quote(node.Value) + case ast.Integer: + return strconv.FormatInt(node.Value, 10) + case ast.Float: + return strconv.FormatFloat(node.Value, 'g', -1, 64) + case ast.Bool: + return strconv.FormatBool(node.Value) + case ast.Assignment: + return node.Name.Value + " = " + valueToString(node.Value) + case ast.Index: + return valueToString(node.Value) + "[" + valueToString(node.Index) + "]" + case ast.Ternary: + return valueToString(node.Condition) + " ? " + valueToString(node.IfTrue) + " : " + valueToString(node.Else) + case ast.FieldAccess: + return valueToString(node.Value) + "." + node.Name.Value + case ast.Value: + if node.Not { + return "!" + valueToString(node.Node) + } + return valueToString(node.Node) + case ast.FuncCall: + if len(node.Params) > 1 { + return node.Name.Value + "(" + valueToString(node.Params[0]) + ", ...)" + } else if len(node.Params) == 1 { + return node.Name.Value + "(" + valueToString(node.Params[0]) + ")" + } else { + return node.Name.Value + "()" + } + case ast.MethodCall: + if len(node.Params) > 1 { + return valueToString(node.Value) + "." + node.Name.Value + "(" + valueToString(node.Params[0]) + ", ...)" + } else if len(node.Params) == 1 { + return valueToString(node.Value) + "." + node.Name.Value + "(" + valueToString(node.Params[0]) + ")" + } else { + return valueToString(node.Value) + "." + node.Name.Value + "()" + } + case ast.Expr: + if len(node.Rest) == 0 { + return valueToString(node.First) + } + return valueToString(node.First) + node.Rest[0].Operator.Value + valueToString(node.Rest[0]) + case ast.Tag: + if len(node.Params) > 1 { + return "#" + node.Name.Value + "(" + valueToString(node.Params[0]) + ", ...)" + } else if len(node.Params) == 1 { + return "#" + node.Name.Value + "(" + valueToString(node.Params[0]) + ")" + } else { + return "#" + node.Name.Value + "()" + } + case ast.EndTag: + return "#" + node.Name.Value + case ast.ExprTag: + return "#(" + valueToString(node.Value) + ")" + default: + return "..." + } +} + // unwrapASTValue unwraps an ast.Value node into its underlying value func (t *Template) unwrapASTValue(node ast.Value, local map[string]any) (any, error) { v, err := t.getValue(node.Node, local) @@ -245,7 +296,7 @@ func (t *Template) unwrapASTValue(node ast.Value, local map[string]any) (any, er if node.Not { rval := reflect.ValueOf(v) if rval.Kind() != reflect.Bool { - return nil, ErrNotOperatorNonBool + return nil, ast.PosError(node, "%s: the ! operator can only be used on boolean values", valueToString(node)) } return !rval.Bool(), nil } @@ -279,7 +330,7 @@ func (t *Template) getVar(id ast.Ident, local map[string]any) (any, error) { return v, nil } - return reflect.Value{}, t.posError(id, "%w: %s", ErrNoSuchVar, id.Value) + return reflect.Value{}, ast.PosError(id, "no such variable: %s", id.Value) } func (t *Template) getTag(name string) (Tag, bool) { @@ -305,7 +356,7 @@ func (t *Template) getTag(name string) (Tag, bool) { func (t *Template) execTag(node ast.Tag, w io.Writer, nodes []ast.Node, i int, local map[string]any) (newOffset int, err error) { tag, ok := t.getTag(node.Name.Value) if !ok { - return 0, t.posError(node, "%w: %s", ErrNoSuchTag, node.Name.Value) + return 0, ast.PosError(node, "no such tag: %s", node.Name.Value) } var block []ast.Node @@ -314,11 +365,11 @@ func (t *Template) execTag(node ast.Tag, w io.Writer, nodes []ast.Node, i int, l i += len(block) + 1 } - tc := &TagContext{w, t, local} + tc := &TagContext{node, w, t, local} err = tag.Run(tc, block, node.Params) if err != nil { - return 0, err + return 0, errors.Join(ast.PosError(node, "%s ->", valueToString(node)), err) } return i, nil @@ -328,7 +379,7 @@ func (t *Template) execTag(node ast.Tag, w io.Writer, nodes []ast.Node, i int, l func (t *Template) execFuncCall(fc ast.FuncCall, local map[string]any) (any, error) { fn, err := t.getVar(fc.Name, local) if err != nil { - return nil, t.posError(fc, "%w: %s", ErrNoSuchFunc, fc.Name.Value) + return nil, ast.PosError(fc, "no such function: %s", fc.Name.Value) } return t.execFunc(reflect.ValueOf(fn), fc, fc.Params, local) } @@ -348,33 +399,34 @@ func (t *Template) getIndex(i ast.Index, local map[string]any) (any, error) { rval := reflect.ValueOf(val) rindex := reflect.ValueOf(index) switch rval.Kind() { - case reflect.Slice, reflect.Array: + case reflect.Slice, reflect.Array, reflect.String: intType := reflect.TypeOf(0) if rindex.CanConvert(intType) { rindex = rindex.Convert(intType) } else { - return nil, ErrIncorrectIndexType + return nil, ast.PosError(i, "%s: invalid index type: %T", valueToString(i), index) } intIndex := rindex.Interface().(int) if intIndex < rval.Len() { return rval.Index(intIndex).Interface(), nil } else { - return nil, t.posError(i, "%w: %d", ErrIndexOutOfRange, intIndex) + return nil, ast.PosError(i, "%s: index out of range: %d", valueToString(i), intIndex) } case reflect.Map: if rindex.CanConvert(rval.Type().Key()) { rindex = rindex.Convert(rval.Type().Key()) } else { - return nil, t.posError(i, "%w: %T (expected %s)", ErrMapInvalidIndexType, index, rval.Type().Key()) + return nil, ast.PosError(i, "%s: invalid map index type: %T (expected %s)", valueToString(i), index, rval.Type().Key()) } if out := rval.MapIndex(rindex); out.IsValid() { return out.Interface(), nil } else { - return nil, t.posError(i, "%w: %q", ErrMapIndexNotFound, index) + return nil, ast.PosError(i, "%s: map index not found: %q", valueToString(i), index) } + default: + return nil, ast.PosError(i, "%s: cannot index type: %T", valueToString(i), val) } - return nil, nil } // getField tries to get a struct field from the underlying value @@ -389,7 +441,7 @@ func (t *Template) getField(fa ast.FieldAccess, local map[string]any) (any, erro } field := rval.FieldByName(fa.Name.Value) if !field.IsValid() { - return nil, t.posError(fa, "%w: %s", ErrNoSuchField, fa.Name.Value) + return nil, ast.PosError(fa, "%s: no such field: %s", valueToString(fa), fa.Name.Value) } return field.Interface(), nil } @@ -406,7 +458,7 @@ func (t *Template) execMethodCall(mc ast.MethodCall, local map[string]any) (any, } mtd := rval.MethodByName(mc.Name.Value) if !mtd.IsValid() { - return nil, t.posError(mc, "%w: %s", ErrNoSuchMethod, mc.Name.Value) + return nil, ast.PosError(mc, "no such method: %s", mc.Name.Value) } return t.execFunc(mtd, mc, mc.Params, local) } @@ -415,17 +467,17 @@ func (t *Template) execMethodCall(mc ast.MethodCall, local map[string]any) (any, func (t *Template) execFunc(fn reflect.Value, node ast.Node, args []ast.Node, local map[string]any) (any, error) { fnType := fn.Type() if fnType.NumIn() != len(args) { - return nil, t.posError(node, "%w: %d (expected %d)", ErrParamNumMismatch, len(args), fnType.NumIn()) + return nil, ast.PosError(node, "%s: invalid parameter amount: %d (expected %d)", valueToString(node), len(args), fnType.NumIn()) } - if err := validateFunc(fnType); err != nil { - return nil, t.posError(node, "%w", err) + if err := validateFunc(fnType, node); err != nil { + return nil, err } 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") + return nil, ast.PosError(arg, "%s: an assignment cannot be used as a function argument", valueToString(node)) } paramVal, err := t.getValue(arg, local) if err != nil { @@ -435,7 +487,7 @@ func (t *Template) execFunc(fn reflect.Value, node ast.Node, args []ast.Node, lo if params[i].CanConvert(fnType.In(i)) { params[i] = params[i].Convert(fnType.In(i)) } else { - return nil, t.posError(node, "%w", ErrIncorrectParamType) + return nil, ast.PosError(node, "%s: invalid parameter type: %T (expected %s)", valueToString(node), paramVal, fnType.In(i)) } } @@ -443,23 +495,26 @@ func (t *Template) execFunc(fn reflect.Value, node ast.Node, args []ast.Node, lo if len(ret) == 1 { retv := ret[0].Interface() if err, ok := retv.(error); ok { - return nil, err + return nil, ast.PosError(node, "%s: %w", valueToString(node), err) } return ret[0].Interface(), nil } else { - return ret[0].Interface(), ret[1].Interface().(error) + if ret[1].IsNil() { + return ret[0].Interface(), nil + } + return ret[0].Interface(), ast.PosError(node, "%s: %w", valueToString(node), ret[1].Interface().(error)) } } func (t *Template) evalTernary(tr ast.Ternary, local map[string]any) (any, error) { condVal, err := t.getValue(tr.Condition, local) if err != nil { - return nil, t.posError(tr.Condition, "%w", ErrTernaryCondBool) + return nil, err } cond, ok := condVal.(bool) if !ok { - return nil, errors.New("ternary condition must be a boolean") + return nil, ast.PosError(tr.Condition, "%s: ternary condition must be a boolean value", valueToString(tr.Condition)) } if cond { @@ -486,21 +541,17 @@ func (t *Template) handleAssignment(a ast.Assignment, local map[string]any) erro return nil } -func (t *Template) posError(n ast.Node, format string, v ...any) error { - return ast.PosError(n, t.name, format, v...) -} - -func validateFunc(t reflect.Type) error { +func validateFunc(t reflect.Type, node ast.Node) error { numOut := t.NumOut() if numOut > 2 { - return ErrFuncTooManyReturns + return ast.PosError(node, "template functions cannot have more than two return values") } else if numOut == 0 { - return ErrFuncNoReturns + return ast.PosError(node, "template functions must have at least one return value") } - if numOut == 2 { - if !t.Out(1).Implements(reflect.TypeOf(error(nil))) { - return ErrFuncSecondReturnType + errType := reflect.TypeOf((*error)(nil)).Elem() + if !t.Out(1).Implements(errType) { + return ast.PosError(node, "the second return value of a template function must be an error") } } diff --git a/tags.go b/tags.go index de5a1fb..fa2c1ca 100644 --- a/tags.go +++ b/tags.go @@ -39,6 +39,7 @@ var globalTags = map[string]Tag{ // TagContext is passed to Tag implementations to allow them to control the interpreter type TagContext struct { + Tag ast.Tag w io.Writer t *Template local map[string]any @@ -65,6 +66,18 @@ func (tc *TagContext) GetValue(node ast.Node, local map[string]any) (any, error) return tc.t.getValue(node, mergeMap(tc.local, local)) } +// PosError returns an error with the file position prepended. This should be used +// for errors wherever possible, to make it easier for users to find errors. +func (tc *TagContext) PosError(node ast.Node, format string, v ...any) error { + return ast.PosError(node, format, v...) +} + +// NodeToString returns a textual representation of the given AST node for users to see, +// such as in error messages. This does not directly correlate to Salix source code. +func (tc *TagContext) NodeToString(node ast.Node) string { + return valueToString(node) +} + // Write writes b to the underlying writer. It implements // the io.Writer interface. func (tc *TagContext) Write(b []byte) (int, error) { diff --git a/vars.go b/vars.go index e261cd4..1d59987 100644 --- a/vars.go +++ b/vars.go @@ -19,6 +19,7 @@ package salix import ( + "fmt" "reflect" "strings" ) @@ -38,12 +39,12 @@ var globalVars = map[string]any{ "join": strings.Join, } -func tmplLen(v any) int { +func tmplLen(v any) (int, error) { val := reflect.ValueOf(v) switch val.Kind() { case reflect.Array, reflect.Slice, reflect.String, reflect.Map: - return val.Len() + return val.Len(), nil default: - return -1 + return 0, fmt.Errorf("cannot get length of %T", v) } }