diff --git a/v1/ast/check.go b/v1/ast/check.go index 11b22ea3c1..57c2fa5d75 100644 --- a/v1/ast/check.go +++ b/v1/ast/check.go @@ -39,8 +39,6 @@ type typeChecker struct { // newTypeChecker returns a new typeChecker object that has no errors. func newTypeChecker() *typeChecker { return &typeChecker{ - builtins: make(map[string]*Builtin), - schemaTypes: make(map[string]types.Type), exprCheckers: map[string]exprChecker{ "eq": checkExprEq, }, @@ -62,6 +60,7 @@ func (tc *typeChecker) copy() *typeChecker { return newTypeChecker(). WithVarRewriter(tc.varRewriter). WithSchemaSet(tc.ss). + WithSchemaTypes(tc.schemaTypes). WithAllowNet(tc.allowNet). WithInputType(tc.input). WithAllowUndefinedFunctionCalls(tc.allowUndefinedFuncs). @@ -84,6 +83,11 @@ func (tc *typeChecker) WithSchemaSet(ss *SchemaSet) *typeChecker { return tc } +func (tc *typeChecker) WithSchemaTypes(schemaTypes map[string]types.Type) *typeChecker { + tc.schemaTypes = schemaTypes + return tc +} + func (tc *typeChecker) WithAllowNet(hosts []string) *typeChecker { tc.allowNet = hosts return tc @@ -124,6 +128,7 @@ func (tc *typeChecker) CheckBody(env *TypeEnv, body Body) (*TypeEnv, Errors) { errors := []*Error{} env = tc.newEnv(env) + vis := newRefChecker(env, tc.varRewriter) WalkExprs(body, func(expr *Expr) bool { @@ -134,7 +139,8 @@ func (tc *typeChecker) CheckBody(env *TypeEnv, body Body) (*TypeEnv, Errors) { hasClosureErrors := len(closureErrs) > 0 - vis := newRefChecker(env, tc.varRewriter) + // reset errors from previous iteration + vis.errs = nil NewGenericVisitor(vis.Visit).Walk(expr) for _, err := range vis.errs { errors = append(errors, err) @@ -200,6 +206,10 @@ func (tc *typeChecker) checkClosures(env *TypeEnv, expr *Expr) Errors { } func (tc *typeChecker) getSchemaType(schemaAnnot *SchemaAnnotation, rule *Rule) (types.Type, *Error) { + if tc.schemaTypes == nil { + tc.schemaTypes = make(map[string]types.Type) + } + if refType, exists := tc.schemaTypes[schemaAnnot.Schema.String()]; exists { return refType, nil } @@ -353,7 +363,7 @@ func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error { // If the type checker wasn't provided with a required capabilities // structure then just skip. In some cases, type checking might be run // without the need to record what builtins are required. - if tc.required != nil { + if tc.required != nil && tc.builtins != nil { if bi, ok := tc.builtins[operator]; ok { tc.required.addBuiltinSorted(bi) } @@ -433,14 +443,13 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error { func checkExprEq(env *TypeEnv, expr *Expr) *Error { pre := getArgTypes(env, expr.Operands()) - exp := Equality.Decl.FuncArgs() - if len(pre) < len(exp.Args) { - return newArgError(expr.Location, expr.Operator(), "too few arguments", pre, exp) + if len(pre) < Equality.Decl.Arity() { + return newArgError(expr.Location, expr.Operator(), "too few arguments", pre, Equality.Decl.FuncArgs()) } - if len(exp.Args) < len(pre) { - return newArgError(expr.Location, expr.Operator(), "too many arguments", pre, exp) + if Equality.Decl.Arity() < len(pre) { + return newArgError(expr.Location, expr.Operator(), "too many arguments", pre, Equality.Decl.FuncArgs()) } a, b := expr.Operand(0), expr.Operand(1) @@ -684,7 +693,6 @@ func rewriteVarsNop(node Ref) Ref { } func newRefChecker(env *TypeEnv, f varRewriter) *refChecker { - if f == nil { f = rewriteVarsNop } diff --git a/v1/ast/policy.go b/v1/ast/policy.go index 7b568c3928..0e0422a9f6 100644 --- a/v1/ast/policy.go +++ b/v1/ast/policy.go @@ -1351,6 +1351,16 @@ func (expr *Expr) Complement() *Expr { return &cpy } +// ComplementNoWith returns a copy of this expression with the negation flag flipped +// and the with modifier removed. This is the same as calling .Complement().NoWith() +// but without making an intermediate copy. +func (expr *Expr) ComplementNoWith() *Expr { + cpy := *expr + cpy.Negated = !cpy.Negated + cpy.With = nil + return &cpy +} + // Equal returns true if this Expr equals the other Expr. func (expr *Expr) Equal(other *Expr) bool { return expr.Compare(other) == 0 @@ -1441,9 +1451,11 @@ func (expr *Expr) sortOrder() int { func (expr *Expr) CopyWithoutTerms() *Expr { cpy := *expr - cpy.With = make([]*With, len(expr.With)) - for i := range expr.With { - cpy.With[i] = expr.With[i].Copy() + if expr.With != nil { + cpy.With = make([]*With, len(expr.With)) + for i := range expr.With { + cpy.With[i] = expr.With[i].Copy() + } } return &cpy diff --git a/v1/ast/term.go b/v1/ast/term.go index 223e0af19b..d79f4418bd 100644 --- a/v1/ast/term.go +++ b/v1/ast/term.go @@ -71,30 +71,37 @@ func InterfaceToValue(x interface{}) (Value, error) { return intNumber(x), nil case string: return String(x), nil - case []interface{}: - r := make([]*Term, len(x)) + case []any: + r := util.NewPtrSlice[Term](len(x)) for i, e := range x { e, err := InterfaceToValue(e) if err != nil { return nil, err } - r[i] = &Term{Value: e} + r[i].Value = e } return NewArray(r...), nil - case map[string]interface{}: - r := newobject(len(x)) + case map[string]any: + kvs := util.NewPtrSlice[Term](len(x) * 2) + idx := 0 for k, v := range x { k, err := InterfaceToValue(k) if err != nil { return nil, err } + kvs[idx].Value = k v, err := InterfaceToValue(v) if err != nil { return nil, err } - r.Insert(NewTerm(k), NewTerm(v)) + kvs[idx+1].Value = v + idx += 2 } - return r, nil + tuples := make([][2]*Term, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + tuples[i/2] = *(*[2]*Term)(kvs[i : i+2]) + } + return NewObject(tuples...), nil case map[string]string: r := newobject(len(x)) for k, v := range x { diff --git a/v1/ast/visit.go b/v1/ast/visit.go index d83c31149e..91cfa208e2 100644 --- a/v1/ast/visit.go +++ b/v1/ast/visit.go @@ -357,14 +357,14 @@ func (vis *GenericVisitor) Walk(x interface{}) { vis.Walk(x.Get(k)) }) case Object: - x.Foreach(func(k, _ *Term) { + for _, k := range x.Keys() { vis.Walk(k) vis.Walk(x.Get(k)) - }) + } case *Array: - x.Foreach(func(t *Term) { - vis.Walk(t) - }) + for i := 0; i < x.Len(); i++ { + vis.Walk(x.Elem(i)) + } case Set: xSlice := x.Slice() for i := range xSlice { diff --git a/v1/rego/rego.go b/v1/rego/rego.go index 9499a213ff..1b7ea47bdd 100644 --- a/v1/rego/rego.go +++ b/v1/rego/rego.go @@ -1735,9 +1735,6 @@ func (r *Rego) PrepareForEval(ctx context.Context, opts ...PrepareOption) (Prepa } txnErr := txnClose(ctx, err) // Always call closer - if err != nil { - return PreparedEvalQuery{}, err - } if txnErr != nil { return PreparedEvalQuery{}, txnErr } diff --git a/v1/rego/rego_bench_test.go b/v1/rego/rego_bench_test.go index 8cef218516..903cc89ff2 100644 --- a/v1/rego/rego_bench_test.go +++ b/v1/rego/rego_bench_test.go @@ -2,7 +2,9 @@ package rego import ( "context" + "encoding/json" "fmt" + "os" "testing" "github.com/open-policy-agent/opa/internal/runtime" @@ -68,3 +70,52 @@ func BenchmarkPartialObjectRuleCrossModule(b *testing.B) { }) } } + +func BenchmarkCustomFunctionInHotPath(b *testing.B) { + ctx := context.Background() + + bs, err := os.ReadFile("testdata/ast.json") + if err != nil { + b.Fatal(err) + } + + input := ast.MustParseTerm(string(bs)) + module := ast.MustParseModule(`package test + + import rego.v1 + + r := count(refs) + + refs contains value if { + walk(input, [_, value]) + is_ref(value) + } + + is_ref(value) if value.type == "ref" + is_ref(value) if value[0].type == "ref"`) + + r := New(Query("data.test.r = x"), ParsedModule(module)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + res, err := pq.Eval(ctx, EvalParsedInput(input.Value)) + if err != nil { + b.Fatal(err) + } + + if res == nil { + b.Fatal("expected result") + } + + if res[0].Bindings["x"].(json.Number) != "402" { + b.Fatalf("expected 402, got %v", res[0].Bindings["x"]) + } + } +} diff --git a/v1/rego/testdata/ast.json b/v1/rego/testdata/ast.json new file mode 100644 index 0000000000..b6364f2a1d --- /dev/null +++ b/v1/rego/testdata/ast.json @@ -0,0 +1,7647 @@ +{ + "package": { + "location": "1:1:1:8", + "path": [ + { + "type": "var", + "value": "data" + }, + { + "location": "1:9:1:14", + "type": "string", + "value": "regal" + }, + { + "location": "1:15:1:18", + "type": "string", + "value": "ast" + } + ], + "annotations": [ + { + "location": "55:1:58:85", + "scope": "rule", + "description": "find vars like input[x].foo[y] where x and y are vars\nnote: value.type == \"ref\" check must have been done before calling this function\n" + }, + { + "location": "81:1:84:64", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns an array with\nall variables declared in terms, i,e [x, y] or {x: y}, etc.\n" + }, + { + "location": "91:1:94:70", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns true if any variable\nis found in terms, with early exit (as opposed to find_term_vars)\n" + }, + { + "scope": "rule", + "description": "traverses all nodes under provided node (using `walk`), and returns an array with\nall variables declared via assignment (:=), `some`, `every` and in comprehensions\nDEPRECATED: uses ast.found.vars instead\n", + "location": "165:1:169:44" + }, + { + "location": "182:1:192:10", + "scope": "rule", + "description": "object containing all variables found in the input AST, keyed first by the index of\nthe rule where the variables were found (as a numeric string), and then the context\nof the variable, which will be one of:\n- term\n- assign\n- every\n- some\n- somein\n- ref\n" + }, + { + "location": "205:1:206:41", + "scope": "rule", + "description": "all refs foundd in module" + }, + { + "description": "all symbols foundd in module", + "location": "218:1:219:44", + "scope": "rule" + }, + { + "location": "229:1:230:50", + "scope": "rule", + "description": "all comprehensions found in module" + }, + { + "location": "242:1:247:28", + "scope": "rule", + "description": "finds all vars declared in `rule` *before* the `location` provided\nnote: this isn't 100% accurate, as it doesn't take into account `=`\nassignments / unification, but it's likely good enough since other rules\nrecommend against those\n" + }, + { + "location": "292:1:293:77", + "scope": "rule", + "description": "find *only* names in the local scope, and not e.g. rule names" + }, + { + "location": "306:1:309:82", + "scope": "rule", + "description": "similar to `find_vars_in_local_scope`, but returns all variable names in scope\nof the given location *and* the rule names present in the scope (i.e. module)\n" + }, + { + "location": "317:1:320:39", + "scope": "rule", + "description": "find all variables declared via `some` declarations (and *not* `some .. in`)\nin the scope of the given location\n" + }, + { + "location": "326:1:327:41", + "scope": "rule", + "description": "all expressions in module" + } + ] + }, + "imports": [ + { + "location": "3:1:3:7", + "path": { + "type": "ref", + "value": [ + { + "type": "var", + "value": "rego", + "location": "3:8:3:12" + }, + { + "location": "3:13:3:15", + "type": "string", + "value": "v1" + } + ], + "location": "3:8:3:15" + } + }, + { + "location": "5:1:5:7", + "path": { + "location": "5:8:5:23", + "type": "ref", + "value": [ + { + "location": "5:8:5:12", + "type": "var", + "value": "data" + }, + { + "location": "5:13:5:18", + "type": "string", + "value": "regal" + }, + { + "location": "5:19:5:23", + "type": "string", + "value": "util" + } + ] + } + } + ], + "rules": [ + { + "location": "7:1:11:2", + "head": { + "args": [ + { + "location": "7:19:7:22", + "type": "var", + "value": "obj" + } + ], + "assign": true, + "value": { + "location": "7:27:11:2", + "type": "arraycomprehension", + "value": { + "term": { + "type": "var", + "value": "value", + "location": "7:28:7:33" + }, + "body": [ + { + "location": "8:2:8:23", + "terms": [ + { + "location": "8:2:8:6", + "type": "ref", + "value": [ + { + "location": "8:2:8:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "8:7:8:10", + "type": "var", + "value": "obj" + }, + { + "value": [ + { + "location": "8:13:8:14", + "type": "var", + "value": "$0" + }, + { + "location": "8:16:8:21", + "type": "var", + "value": "value" + } + ], + "location": "8:12:8:22", + "type": "array" + } + ] + }, + { + "location": "9:2:9:21", + "terms": [ + { + "location": "9:13:9:15", + "type": "ref", + "value": [ + { + "location": "9:13:9:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "9:2:9:7", + "type": "var", + "value": "value" + }, + { + "location": "9:8:9:12", + "type": "string", + "value": "type" + } + ], + "location": "9:2:9:12", + "type": "ref" + }, + { + "location": "9:16:9:21", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "10:2:10:33", + "terms": [ + { + "value": [ + { + "location": "10:28:10:30", + "type": "var", + "value": "equal" + } + ], + "location": "10:28:10:30", + "type": "ref" + }, + { + "location": "10:2:10:27", + "type": "call", + "value": [ + { + "location": "10:2:10:9", + "type": "ref", + "value": [ + { + "location": "10:2:10:9", + "type": "var", + "value": "indexof" + } + ] + }, + { + "value": [ + { + "location": "10:10:10:15", + "type": "var", + "value": "value" + }, + { + "location": "10:16:10:21", + "type": "string", + "value": "value" + } + ], + "location": "10:10:10:21", + "type": "ref" + }, + { + "location": "10:23:10:26", + "type": "string", + "value": "$" + } + ] + }, + { + "value": -1, + "location": "10:31:10:32", + "type": "number" + } + ] + } + ] + } + }, + "location": "7:1:11:2", + "ref": [ + { + "type": "var", + "value": "_find_nested_vars", + "location": "7:1:7:18" + } + ] + } + }, + { + "location": "16:1:19:2", + "head": { + "location": "16:1:16:32", + "ref": [ + { + "location": "16:1:16:18", + "type": "var", + "value": "_find_assign_vars" + } + ], + "args": [ + { + "location": "16:19:16:24", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "16:29:16:32", + "type": "var", + "value": "var" + } + }, + "body": [ + { + "location": "17:2:17:24", + "terms": [ + { + "location": "17:16:17:18", + "type": "ref", + "value": [ + { + "location": "17:16:17:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "17:2:17:15", + "type": "ref", + "value": [ + { + "location": "17:2:17:7", + "type": "var", + "value": "value" + }, + { + "location": "17:8:17:9", + "type": "number", + "value": 1 + }, + { + "location": "17:11:17:15", + "type": "string", + "value": "type" + } + ] + }, + { + "type": "string", + "value": "var", + "location": "17:19:17:24" + } + ] + }, + { + "location": "18:2:18:19", + "terms": [ + { + "location": "18:6:18:8", + "type": "ref", + "value": [ + { + "location": "18:6:18:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "18:2:18:5" + }, + { + "location": "18:9:18:19", + "type": "array", + "value": [ + { + "location": "18:10:18:18", + "type": "ref", + "value": [ + { + "location": "18:10:18:15", + "type": "var", + "value": "value" + }, + { + "location": "18:16:18:17", + "type": "number", + "value": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "location": "25:1:28:2", + "head": { + "ref": [ + { + "location": "25:1:25:18", + "type": "var", + "value": "_find_assign_vars" + } + ], + "args": [ + { + "value": "value", + "location": "25:19:25:24", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "25:29:25:33", + "type": "var", + "value": "vars" + }, + "location": "25:1:25:33" + }, + "body": [ + { + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "26:16:26:18", + "type": "var", + "value": "internal" + }, + { + "location": "26:16:26:18", + "type": "string", + "value": "member_2" + } + ], + "location": "26:16:26:18" + }, + { + "location": "26:2:26:15", + "type": "ref", + "value": [ + { + "location": "26:2:26:7", + "type": "var", + "value": "value" + }, + { + "value": 1, + "location": "26:8:26:9", + "type": "number" + }, + { + "location": "26:11:26:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "26:19:26:38", + "type": "set", + "value": [ + { + "type": "string", + "value": "array", + "location": "26:20:26:27" + }, + { + "type": "string", + "value": "object", + "location": "26:29:26:37" + } + ] + } + ], + "location": "26:2:26:38" + }, + { + "location": "27:2:27:37", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "27:7:27:9", + "type": "var", + "value": "assign" + } + ], + "location": "27:7:27:9" + }, + { + "location": "27:2:27:6", + "type": "var", + "value": "vars" + }, + { + "value": [ + { + "location": "27:10:27:27", + "type": "ref", + "value": [ + { + "location": "27:10:27:27", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "location": "27:28:27:36", + "type": "ref", + "value": [ + { + "location": "27:28:27:33", + "type": "var", + "value": "value" + }, + { + "location": "27:34:27:35", + "type": "number", + "value": 1 + } + ] + } + ], + "location": "27:10:27:37", + "type": "call" + } + ] + } + ] + }, + { + "location": "31:1:34:2", + "head": { + "ref": [ + { + "location": "31:1:31:21", + "type": "var", + "value": "_find_some_decl_vars" + } + ], + "args": [ + { + "location": "31:22:31:27", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "31:32:34:2", + "type": "arraycomprehension", + "value": { + "body": [ + { + "location": "32:2:32:17", + "terms": { + "location": "32:2:32:6", + "symbols": [ + { + "type": "call", + "value": [ + { + "location": "32:9:32:11", + "type": "ref", + "value": [ + { + "location": "32:9:32:11", + "type": "var", + "value": "internal" + }, + { + "location": "32:9:32:11", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "32:7:32:8", + "type": "var", + "value": "v" + }, + { + "location": "32:12:32:17", + "type": "var", + "value": "value" + } + ], + "location": "32:7:32:17" + } + ] + } + }, + { + "location": "33:2:33:17", + "terms": [ + { + "location": "33:9:33:11", + "type": "ref", + "value": [ + { + "location": "33:9:33:11", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "33:2:33:8", + "type": "ref", + "value": [ + { + "location": "33:2:33:3", + "type": "var", + "value": "v" + }, + { + "location": "33:4:33:8", + "type": "string", + "value": "type" + } + ] + }, + { + "value": "var", + "location": "33:12:33:17", + "type": "string" + } + ] + } + ], + "term": { + "location": "31:33:31:34", + "type": "var", + "value": "v" + } + } + }, + "location": "31:1:34:2" + } + }, + { + "location": "37:1:42:2", + "head": { + "ref": [ + { + "location": "37:1:37:24", + "type": "var", + "value": "_find_some_in_decl_vars" + } + ], + "args": [ + { + "location": "37:25:37:30", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "value": "vars", + "location": "37:35:37:39", + "type": "var" + }, + "location": "37:1:37:39" + }, + "body": [ + { + "terms": [ + { + "location": "38:6:38:8", + "type": "ref", + "value": [ + { + "location": "38:6:38:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "38:2:38:5", + "type": "var", + "value": "arr" + }, + { + "location": "38:9:38:23", + "type": "ref", + "value": [ + { + "value": "value", + "location": "38:9:38:14", + "type": "var" + }, + { + "location": "38:15:38:16", + "type": "number", + "value": 0 + }, + { + "location": "38:18:38:23", + "type": "string", + "value": "value" + } + ] + } + ], + "location": "38:2:38:23" + }, + { + "terms": [ + { + "location": "39:13:39:15", + "type": "ref", + "value": [ + { + "location": "39:13:39:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "39:2:39:12", + "type": "call", + "value": [ + { + "location": "39:2:39:7", + "type": "ref", + "value": [ + { + "location": "39:2:39:7", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "39:8:39:11", + "type": "var", + "value": "arr" + } + ] + }, + { + "location": "39:16:39:17", + "type": "number", + "value": 3 + } + ], + "location": "39:2:39:17" + }, + { + "location": "41:2:41:35", + "terms": [ + { + "location": "41:7:41:9", + "type": "ref", + "value": [ + { + "location": "41:7:41:9", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "41:2:41:6", + "type": "var", + "value": "vars" + }, + { + "location": "41:10:41:35", + "type": "call", + "value": [ + { + "location": "41:10:41:27", + "type": "ref", + "value": [ + { + "location": "41:10:41:27", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "41:28:41:31", + "type": "var", + "value": "arr" + }, + { + "location": "41:32:41:33", + "type": "number", + "value": 1 + } + ], + "location": "41:28:41:34" + } + ] + } + ] + } + ] + }, + { + "head": { + "args": [ + { + "location": "45:25:45:30", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "45:35:45:39", + "type": "var", + "value": "vars" + }, + "location": "45:1:45:39", + "ref": [ + { + "value": "_find_some_in_decl_vars", + "location": "45:1:45:24", + "type": "var" + } + ] + }, + "body": [ + { + "location": "46:2:46:23", + "terms": [ + { + "location": "46:6:46:8", + "type": "ref", + "value": [ + { + "location": "46:6:46:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "46:2:46:5", + "type": "var", + "value": "arr" + }, + { + "type": "ref", + "value": [ + { + "location": "46:9:46:14", + "type": "var", + "value": "value" + }, + { + "location": "46:15:46:16", + "type": "number", + "value": 0 + }, + { + "type": "string", + "value": "value", + "location": "46:18:46:23" + } + ], + "location": "46:9:46:23" + } + ] + }, + { + "location": "47:2:47:17", + "terms": [ + { + "location": "47:13:47:15", + "type": "ref", + "value": [ + { + "location": "47:13:47:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "47:2:47:7", + "type": "ref", + "value": [ + { + "location": "47:2:47:7", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "47:8:47:11", + "type": "var", + "value": "arr" + } + ], + "location": "47:2:47:12", + "type": "call" + }, + { + "type": "number", + "value": 4, + "location": "47:16:47:17" + } + ] + }, + { + "location": "49:2:52:3", + "terms": [ + { + "location": "49:7:49:9", + "type": "ref", + "value": [ + { + "location": "49:7:49:9", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "49:2:49:6", + "type": "var", + "value": "vars" + }, + { + "type": "arraycomprehension", + "value": { + "term": { + "location": "49:11:49:12", + "type": "var", + "value": "v" + }, + "body": [ + { + "location": "50:3:50:19", + "terms": { + "location": "50:3:50:7", + "symbols": [ + { + "location": "50:8:50:19", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "50:10:50:12", + "type": "var", + "value": "internal" + }, + { + "location": "50:10:50:12", + "type": "string", + "value": "member_2" + } + ], + "location": "50:10:50:12" + }, + { + "value": "i", + "location": "50:8:50:9", + "type": "var" + }, + { + "value": [ + { + "location": "50:14:50:15", + "type": "number", + "value": 1 + }, + { + "type": "number", + "value": 2, + "location": "50:17:50:18" + } + ], + "location": "50:13:50:19", + "type": "array" + } + ] + } + ] + } + }, + { + "location": "51:3:51:38", + "terms": { + "location": "51:3:51:7", + "symbols": [ + { + "location": "51:8:51:38", + "type": "call", + "value": [ + { + "location": "51:10:51:12", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "51:10:51:12" + }, + { + "value": "member_2", + "location": "51:10:51:12", + "type": "string" + } + ] + }, + { + "location": "51:8:51:9", + "type": "var", + "value": "v" + }, + { + "type": "call", + "value": [ + { + "location": "51:13:51:30", + "type": "ref", + "value": [ + { + "location": "51:13:51:30", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "location": "51:31:51:37", + "type": "ref", + "value": [ + { + "location": "51:31:51:34", + "type": "var", + "value": "arr" + }, + { + "location": "51:35:51:36", + "type": "var", + "value": "i" + } + ] + } + ], + "location": "51:13:51:38" + } + ] + } + ] + } + } + ] + }, + "location": "49:10:52:3" + } + ] + } + ], + "location": "45:1:53:2" + }, + { + "location": "59:1:64:2", + "annotations": [ + { + "location": "55:1:58:85", + "scope": "rule", + "description": "find vars like input[x].foo[y] where x and y are vars\nnote: value.type == \"ref\" check must have been done before calling this function\n" + } + ], + "head": { + "ref": [ + { + "location": "59:1:59:14", + "type": "var", + "value": "find_ref_vars" + } + ], + "args": [ + { + "location": "59:15:59:20", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "59:25:64:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "59:26:59:29", + "type": "var", + "value": "var" + }, + "body": [ + { + "location": "60:2:60:28", + "terms": { + "symbols": [ + { + "location": "60:7:60:28", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "60:14:60:16", + "type": "var", + "value": "internal" + }, + { + "location": "60:14:60:16", + "type": "string", + "value": "member_3" + } + ], + "location": "60:14:60:16" + }, + { + "location": "60:7:60:8", + "type": "var", + "value": "i" + }, + { + "type": "var", + "value": "var", + "location": "60:10:60:13" + }, + { + "value": [ + { + "type": "var", + "value": "value", + "location": "60:17:60:22" + }, + { + "type": "string", + "value": "value", + "location": "60:23:60:28" + } + ], + "location": "60:17:60:28", + "type": "ref" + } + ] + } + ], + "location": "60:2:60:6" + } + }, + { + "location": "62:2:62:7", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "gt", + "location": "62:4:62:5", + "type": "var" + } + ], + "location": "62:4:62:5" + }, + { + "location": "62:2:62:3", + "type": "var", + "value": "i" + }, + { + "location": "62:6:62:7", + "type": "number", + "value": 0 + } + ] + }, + { + "terms": [ + { + "location": "63:11:63:13", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "63:11:63:13" + } + ] + }, + { + "value": [ + { + "location": "63:2:63:5", + "type": "var", + "value": "var" + }, + { + "location": "63:6:63:10", + "type": "string", + "value": "type" + } + ], + "location": "63:2:63:10", + "type": "ref" + }, + { + "location": "63:14:63:19", + "type": "string", + "value": "var" + } + ], + "location": "63:2:63:19" + } + ] + } + }, + "location": "59:1:64:2" + } + }, + { + "body": [ + { + "location": "69:2:72:3", + "terms": [ + { + "value": [ + { + "location": "69:10:69:12", + "type": "var", + "value": "assign" + } + ], + "location": "69:10:69:12", + "type": "ref" + }, + { + "type": "var", + "value": "key_var", + "location": "69:2:69:9" + }, + { + "location": "69:13:72:3", + "type": "arraycomprehension", + "value": { + "body": [ + { + "location": "70:3:70:26", + "terms": [ + { + "location": "70:18:70:20", + "type": "ref", + "value": [ + { + "location": "70:18:70:20", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "type": "var", + "value": "value", + "location": "70:3:70:8" + }, + { + "location": "70:9:70:12", + "type": "string", + "value": "key" + }, + { + "location": "70:13:70:17", + "type": "string", + "value": "type" + } + ], + "location": "70:3:70:17", + "type": "ref" + }, + { + "value": "var", + "location": "70:21:70:26", + "type": "string" + } + ] + }, + { + "location": "71:3:71:38", + "terms": [ + { + "location": "71:33:71:35", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "71:33:71:35", + "type": "var" + } + ] + }, + { + "location": "71:3:71:32", + "type": "call", + "value": [ + { + "location": "71:3:71:10", + "type": "ref", + "value": [ + { + "location": "71:3:71:10", + "type": "var", + "value": "indexof" + } + ] + }, + { + "location": "71:11:71:26", + "type": "ref", + "value": [ + { + "value": "value", + "location": "71:11:71:16", + "type": "var" + }, + { + "location": "71:17:71:20", + "type": "string", + "value": "key" + }, + { + "location": "71:21:71:26", + "type": "string", + "value": "value" + } + ] + }, + { + "value": "$", + "location": "71:28:71:31", + "type": "string" + } + ] + }, + { + "location": "71:36:71:37", + "type": "number", + "value": -1 + } + ] + } + ], + "term": { + "location": "69:14:69:23", + "type": "ref", + "value": [ + { + "location": "69:14:69:19", + "type": "var", + "value": "value" + }, + { + "location": "69:20:69:23", + "type": "string", + "value": "key" + } + ] + } + } + } + ] + }, + { + "location": "73:2:76:3", + "terms": [ + { + "location": "73:10:73:12", + "type": "ref", + "value": [ + { + "location": "73:10:73:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "73:2:73:9", + "type": "var", + "value": "val_var" + }, + { + "value": { + "term": { + "location": "73:14:73:25", + "type": "ref", + "value": [ + { + "type": "var", + "value": "value", + "location": "73:14:73:19" + }, + { + "location": "73:20:73:25", + "type": "string", + "value": "value" + } + ] + }, + "body": [ + { + "location": "74:3:74:28", + "terms": [ + { + "location": "74:20:74:22", + "type": "ref", + "value": [ + { + "location": "74:20:74:22", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "74:3:74:8", + "type": "var", + "value": "value" + }, + { + "location": "74:9:74:14", + "type": "string", + "value": "value" + }, + { + "location": "74:15:74:19", + "type": "string", + "value": "type" + } + ], + "location": "74:3:74:19" + }, + { + "location": "74:23:74:28", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "75:3:75:40", + "terms": [ + { + "location": "75:35:75:37", + "type": "ref", + "value": [ + { + "location": "75:35:75:37", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "75:3:75:34", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "75:3:75:10", + "type": "var", + "value": "indexof" + } + ], + "location": "75:3:75:10" + }, + { + "location": "75:11:75:28", + "type": "ref", + "value": [ + { + "location": "75:11:75:16", + "type": "var", + "value": "value" + }, + { + "location": "75:17:75:22", + "type": "string", + "value": "value" + }, + { + "location": "75:23:75:28", + "type": "string", + "value": "value" + } + ] + }, + { + "location": "75:30:75:33", + "type": "string", + "value": "$" + } + ] + }, + { + "location": "75:38:75:39", + "type": "number", + "value": -1 + } + ] + } + ] + }, + "location": "73:13:76:3", + "type": "arraycomprehension" + } + ] + }, + { + "location": "78:2:78:40", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "assign", + "location": "78:7:78:9", + "type": "var" + } + ], + "location": "78:7:78:9" + }, + { + "value": "vars", + "location": "78:2:78:6", + "type": "var" + }, + { + "location": "78:10:78:40", + "type": "call", + "value": [ + { + "location": "78:10:78:22", + "type": "ref", + "value": [ + { + "location": "78:10:78:15", + "type": "var", + "value": "array" + }, + { + "location": "78:16:78:22", + "type": "string", + "value": "concat" + } + ] + }, + { + "value": "key_var", + "location": "78:23:78:30", + "type": "var" + }, + { + "location": "78:32:78:39", + "type": "var", + "value": "val_var" + } + ] + } + ] + } + ], + "location": "68:1:79:2", + "head": { + "location": "68:1:68:32", + "ref": [ + { + "value": "_find_every_vars", + "location": "68:1:68:17", + "type": "var" + } + ], + "args": [ + { + "location": "68:18:68:23", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "68:28:68:32", + "type": "var", + "value": "vars" + } + } + }, + { + "location": "85:1:89:2", + "annotations": [ + { + "location": "81:1:84:64", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns an array with\nall variables declared in terms, i,e [x, y] or {x: y}, etc.\n" + } + ], + "head": { + "ref": [ + { + "location": "85:1:85:15", + "type": "var", + "value": "find_term_vars" + } + ], + "args": [ + { + "value": "terms", + "location": "85:16:85:21", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "85:26:89:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "85:27:85:31", + "type": "var", + "value": "term" + }, + "body": [ + { + "location": "86:2:86:24", + "terms": [ + { + "value": [ + { + "location": "86:2:86:6", + "type": "var", + "value": "walk" + } + ], + "location": "86:2:86:6", + "type": "ref" + }, + { + "location": "86:7:86:12", + "type": "var", + "value": "terms" + }, + { + "location": "86:14:86:23", + "type": "array", + "value": [ + { + "location": "86:15:86:16", + "type": "var", + "value": "$1" + }, + { + "type": "var", + "value": "term", + "location": "86:18:86:22" + } + ] + } + ] + }, + { + "location": "88:2:88:20", + "terms": [ + { + "location": "88:12:88:14", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "88:12:88:14" + } + ] + }, + { + "location": "88:2:88:11", + "type": "ref", + "value": [ + { + "location": "88:2:88:6", + "type": "var", + "value": "term" + }, + { + "value": "type", + "location": "88:7:88:11", + "type": "string" + } + ] + }, + { + "location": "88:15:88:20", + "type": "string", + "value": "var" + } + ] + } + ] + } + }, + "location": "85:1:89:2" + } + }, + { + "head": { + "value": { + "type": "boolean", + "value": true + }, + "location": "95:1:95:20", + "ref": [ + { + "location": "95:1:95:13", + "type": "var", + "value": "has_term_var" + } + ], + "args": [ + { + "type": "var", + "value": "terms", + "location": "95:14:95:19" + } + ] + }, + "body": [ + { + "location": "96:2:96:24", + "terms": [ + { + "value": [ + { + "value": "walk", + "location": "96:2:96:6", + "type": "var" + } + ], + "location": "96:2:96:6", + "type": "ref" + }, + { + "location": "96:7:96:12", + "type": "var", + "value": "terms" + }, + { + "location": "96:14:96:23", + "type": "array", + "value": [ + { + "location": "96:15:96:16", + "type": "var", + "value": "$2" + }, + { + "location": "96:18:96:22", + "type": "var", + "value": "term" + } + ] + } + ] + }, + { + "location": "98:2:98:20", + "terms": [ + { + "location": "98:12:98:14", + "type": "ref", + "value": [ + { + "location": "98:12:98:14", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "98:2:98:11", + "type": "ref", + "value": [ + { + "location": "98:2:98:6", + "type": "var", + "value": "term" + }, + { + "location": "98:7:98:11", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "98:15:98:20", + "type": "string", + "value": "var" + } + ] + } + ], + "location": "95:1:99:2", + "annotations": [ + { + "description": "traverses all nodes in provided terms (using `walk`), and returns true if any variable\nis found in terms, with early exit (as opposed to find_term_vars)\n", + "location": "91:1:94:70", + "scope": "rule" + } + ] + }, + { + "location": "101:1:112:2", + "head": { + "value": { + "type": "object", + "value": [ + [ + { + "value": "term", + "location": "101:29:101:35", + "type": "string" + }, + { + "location": "101:37:101:87", + "type": "call", + "value": [ + { + "location": "101:37:101:51", + "type": "ref", + "value": [ + { + "location": "101:37:101:51", + "type": "var", + "value": "find_term_vars" + } + ] + }, + { + "type": "call", + "value": [ + { + "location": "101:52:101:69", + "type": "ref", + "value": [ + { + "location": "101:52:101:69", + "type": "var", + "value": "function_ret_args" + } + ] + }, + { + "location": "101:70:101:77", + "type": "var", + "value": "fn_name" + }, + { + "location": "101:79:101:84", + "type": "var", + "value": "value" + } + ], + "location": "101:52:101:86" + } + ] + } + ] + ], + "location": "101:28:101:87" + }, + "location": "101:1:101:87", + "ref": [ + { + "location": "101:1:101:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "location": "101:12:101:17", + "type": "var", + "value": "value" + }, + { + "location": "101:19:101:23", + "type": "var", + "value": "last" + } + ], + "assign": true + }, + "body": [ + { + "terms": [ + { + "location": "102:7:102:9", + "type": "ref", + "value": [ + { + "location": "102:7:102:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "var", + "value": "last", + "location": "102:2:102:6" + }, + { + "location": "102:10:102:17", + "type": "string", + "value": "terms" + } + ], + "location": "102:2:102:17" + }, + { + "location": "103:2:103:24", + "terms": [ + { + "location": "103:16:103:18", + "type": "ref", + "value": [ + { + "location": "103:16:103:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "103:2:103:15", + "type": "ref", + "value": [ + { + "location": "103:2:103:7", + "type": "var", + "value": "value" + }, + { + "location": "103:8:103:9", + "type": "number", + "value": 0 + }, + { + "location": "103:11:103:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "103:19:103:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "104:2:104:33", + "terms": [ + { + "location": "104:25:104:27", + "type": "ref", + "value": [ + { + "location": "104:25:104:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "104:2:104:7", + "type": "var", + "value": "value" + }, + { + "location": "104:8:104:9", + "type": "number", + "value": 0 + }, + { + "location": "104:11:104:16", + "type": "string", + "value": "value" + }, + { + "location": "104:17:104:18", + "type": "number", + "value": 0 + }, + { + "location": "104:20:104:24", + "type": "string", + "value": "type" + } + ], + "location": "104:2:104:24" + }, + { + "location": "104:28:104:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "105:2:105:37", + "terms": [ + { + "location": "105:26:105:28", + "type": "ref", + "value": [ + { + "location": "105:26:105:28", + "type": "var", + "value": "neq" + } + ] + }, + { + "location": "105:2:105:25", + "type": "ref", + "value": [ + { + "location": "105:2:105:7", + "type": "var", + "value": "value" + }, + { + "location": "105:8:105:9", + "type": "number", + "value": 0 + }, + { + "location": "105:11:105:16", + "type": "string", + "value": "value" + }, + { + "value": 0, + "location": "105:17:105:18", + "type": "number" + }, + { + "value": "value", + "location": "105:20:105:25", + "type": "string" + } + ] + }, + { + "type": "string", + "value": "assign", + "location": "105:29:105:37" + } + ] + }, + { + "location": "107:2:107:42", + "terms": [ + { + "location": "107:10:107:12", + "type": "ref", + "value": [ + { + "location": "107:10:107:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "107:2:107:9", + "type": "var", + "value": "fn_name" + }, + { + "location": "107:13:107:42", + "type": "call", + "value": [ + { + "location": "107:13:107:26", + "type": "ref", + "value": [ + { + "value": "ref_to_string", + "location": "107:13:107:26", + "type": "var" + } + ] + }, + { + "value": [ + { + "location": "107:27:107:32", + "type": "var", + "value": "value" + }, + { + "location": "107:33:107:34", + "type": "number", + "value": 0 + }, + { + "location": "107:36:107:41", + "type": "string", + "value": "value" + } + ], + "location": "107:27:107:41", + "type": "ref" + } + ] + } + ] + }, + { + "location": "109:2:109:28", + "negated": true, + "terms": [ + { + "location": "109:6:109:14", + "type": "ref", + "value": [ + { + "type": "var", + "value": "contains", + "location": "109:6:109:14" + } + ] + }, + { + "value": "fn_name", + "location": "109:15:109:22", + "type": "var" + }, + { + "location": "109:24:109:27", + "type": "string", + "value": "$" + } + ] + }, + { + "location": "110:2:110:31", + "terms": [ + { + "location": "110:10:110:12", + "type": "ref", + "value": [ + { + "location": "110:10:110:12", + "type": "var", + "value": "internal" + }, + { + "location": "110:10:110:12", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "110:2:110:9", + "type": "var", + "value": "fn_name" + }, + { + "location": "110:13:110:31", + "type": "var", + "value": "all_function_names" + } + ] + }, + { + "location": "111:2:111:38", + "terms": [ + { + "location": "111:2:111:22", + "type": "ref", + "value": [ + { + "location": "111:2:111:22", + "type": "var", + "value": "function_ret_in_args" + } + ] + }, + { + "location": "111:23:111:30", + "type": "var", + "value": "fn_name" + }, + { + "location": "111:32:111:37", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "114:1:119:2", + "head": { + "assign": true, + "value": { + "location": "114:28:114:64", + "type": "object", + "value": [ + [ + { + "value": "assign", + "location": "114:29:114:37", + "type": "string" + }, + { + "location": "114:39:114:64", + "type": "call", + "value": [ + { + "location": "114:39:114:56", + "type": "ref", + "value": [ + { + "location": "114:39:114:56", + "type": "var", + "value": "_find_assign_vars" + } + ] + }, + { + "location": "114:57:114:62", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "114:1:114:64", + "ref": [ + { + "location": "114:1:114:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "value": "value", + "location": "114:12:114:17", + "type": "var" + }, + { + "location": "114:19:114:23", + "type": "var", + "value": "last" + } + ] + }, + "body": [ + { + "location": "115:2:115:17", + "terms": [ + { + "location": "115:7:115:9", + "type": "ref", + "value": [ + { + "location": "115:7:115:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "115:2:115:6", + "type": "var", + "value": "last" + }, + { + "value": "terms", + "location": "115:10:115:17", + "type": "string" + } + ] + }, + { + "location": "116:2:116:24", + "terms": [ + { + "location": "116:16:116:18", + "type": "ref", + "value": [ + { + "location": "116:16:116:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "116:2:116:15", + "type": "ref", + "value": [ + { + "value": "value", + "location": "116:2:116:7", + "type": "var" + }, + { + "location": "116:8:116:9", + "type": "number", + "value": 0 + }, + { + "location": "116:11:116:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "116:19:116:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "117:2:117:33", + "terms": [ + { + "location": "117:25:117:27", + "type": "ref", + "value": [ + { + "location": "117:25:117:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "value": "value", + "location": "117:2:117:7", + "type": "var" + }, + { + "location": "117:8:117:9", + "type": "number", + "value": 0 + }, + { + "location": "117:11:117:16", + "type": "string", + "value": "value" + }, + { + "location": "117:17:117:18", + "type": "number", + "value": 0 + }, + { + "location": "117:20:117:24", + "type": "string", + "value": "type" + } + ], + "location": "117:2:117:24" + }, + { + "location": "117:28:117:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "118:2:118:37", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "118:26:118:28", + "type": "var", + "value": "equal" + } + ], + "location": "118:26:118:28" + }, + { + "location": "118:2:118:25", + "type": "ref", + "value": [ + { + "location": "118:2:118:7", + "type": "var", + "value": "value" + }, + { + "location": "118:8:118:9", + "type": "number", + "value": 0 + }, + { + "location": "118:11:118:16", + "type": "string", + "value": "value" + }, + { + "location": "118:17:118:18", + "type": "number", + "value": 0 + }, + { + "location": "118:20:118:25", + "type": "string", + "value": "value" + } + ] + }, + { + "location": "118:29:118:37", + "type": "string", + "value": "assign" + } + ] + } + ] + }, + { + "location": "125:1:130:2", + "head": { + "value": { + "location": "125:28:125:64", + "type": "object", + "value": [ + [ + { + "location": "125:29:125:37", + "type": "string", + "value": "assign" + }, + { + "location": "125:39:125:64", + "type": "call", + "value": [ + { + "location": "125:39:125:56", + "type": "ref", + "value": [ + { + "location": "125:39:125:56", + "type": "var", + "value": "_find_assign_vars" + } + ] + }, + { + "location": "125:57:125:62", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "125:1:125:64", + "ref": [ + { + "type": "var", + "value": "_find_vars", + "location": "125:1:125:11" + } + ], + "args": [ + { + "location": "125:12:125:17", + "type": "var", + "value": "value" + }, + { + "location": "125:19:125:23", + "type": "var", + "value": "last" + } + ], + "assign": true + }, + "body": [ + { + "location": "126:2:126:17", + "terms": [ + { + "location": "126:7:126:9", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "126:7:126:9", + "type": "var" + } + ] + }, + { + "location": "126:2:126:6", + "type": "var", + "value": "last" + }, + { + "location": "126:10:126:17", + "type": "string", + "value": "terms" + } + ] + }, + { + "location": "127:2:127:24", + "terms": [ + { + "location": "127:16:127:18", + "type": "ref", + "value": [ + { + "location": "127:16:127:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "127:2:127:15", + "type": "ref", + "value": [ + { + "location": "127:2:127:7", + "type": "var", + "value": "value" + }, + { + "location": "127:8:127:9", + "type": "number", + "value": 0 + }, + { + "location": "127:11:127:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "127:19:127:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "128:2:128:33", + "terms": [ + { + "location": "128:25:128:27", + "type": "ref", + "value": [ + { + "location": "128:25:128:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "128:2:128:24", + "type": "ref", + "value": [ + { + "value": "value", + "location": "128:2:128:7", + "type": "var" + }, + { + "location": "128:8:128:9", + "type": "number", + "value": 0 + }, + { + "value": "value", + "location": "128:11:128:16", + "type": "string" + }, + { + "location": "128:17:128:18", + "type": "number", + "value": 0 + }, + { + "location": "128:20:128:24", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "128:28:128:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "129:2:129:33", + "terms": [ + { + "location": "129:26:129:28", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "129:26:129:28", + "type": "var" + } + ] + }, + { + "type": "ref", + "value": [ + { + "value": "value", + "location": "129:2:129:7", + "type": "var" + }, + { + "type": "number", + "value": 0, + "location": "129:8:129:9" + }, + { + "location": "129:11:129:16", + "type": "string", + "value": "value" + }, + { + "location": "129:17:129:18", + "type": "number", + "value": 0 + }, + { + "location": "129:20:129:25", + "type": "string", + "value": "value" + } + ], + "location": "129:2:129:25" + }, + { + "location": "129:29:129:33", + "type": "string", + "value": "eq" + } + ] + } + ] + }, + { + "location": "132:1:132:77", + "head": { + "args": [ + { + "location": "132:12:132:17", + "type": "var", + "value": "value" + }, + { + "location": "132:19:132:20", + "type": "var", + "value": "$3" + } + ], + "assign": true, + "value": { + "location": "132:25:132:54", + "type": "object", + "value": [ + [ + { + "location": "132:26:132:31", + "type": "string", + "value": "ref" + }, + { + "location": "132:33:132:54", + "type": "call", + "value": [ + { + "location": "132:33:132:46", + "type": "ref", + "value": [ + { + "value": "find_ref_vars", + "location": "132:33:132:46", + "type": "var" + } + ] + }, + { + "location": "132:47:132:52", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "132:1:132:54", + "ref": [ + { + "location": "132:1:132:11", + "type": "var", + "value": "_find_vars" + } + ] + }, + "body": [ + { + "location": "132:58:132:77", + "terms": [ + { + "value": [ + { + "location": "132:69:132:71", + "type": "var", + "value": "equal" + } + ], + "location": "132:69:132:71", + "type": "ref" + }, + { + "location": "132:58:132:68", + "type": "ref", + "value": [ + { + "location": "132:58:132:63", + "type": "var", + "value": "value" + }, + { + "location": "132:64:132:68", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "132:72:132:77", + "type": "string", + "value": "ref" + } + ] + } + ] + }, + { + "location": "134:1:137:2", + "head": { + "location": "134:1:134:70", + "ref": [ + { + "location": "134:1:134:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "location": "134:12:134:17", + "type": "var", + "value": "value" + }, + { + "value": "last", + "location": "134:19:134:23", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "134:28:134:70", + "type": "object", + "value": [ + [ + { + "location": "134:29:134:37", + "type": "string", + "value": "somein" + }, + { + "location": "134:39:134:70", + "type": "call", + "value": [ + { + "location": "134:39:134:62", + "type": "ref", + "value": [ + { + "location": "134:39:134:62", + "type": "var", + "value": "_find_some_in_decl_vars" + } + ] + }, + { + "type": "var", + "value": "value", + "location": "134:63:134:68" + } + ] + } + ] + ] + } + }, + "body": [ + { + "location": "135:2:135:19", + "terms": [ + { + "location": "135:7:135:9", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "135:7:135:9" + } + ] + }, + { + "location": "135:2:135:6", + "type": "var", + "value": "last" + }, + { + "value": "symbols", + "location": "135:10:135:19", + "type": "string" + } + ] + }, + { + "location": "136:2:136:25", + "terms": [ + { + "location": "136:16:136:18", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "136:16:136:18" + } + ] + }, + { + "location": "136:2:136:15", + "type": "ref", + "value": [ + { + "location": "136:2:136:7", + "type": "var", + "value": "value" + }, + { + "location": "136:8:136:9", + "type": "number", + "value": 0 + }, + { + "location": "136:11:136:15", + "type": "string", + "value": "type" + } + ] + }, + { + "value": "call", + "location": "136:19:136:25", + "type": "string" + } + ] + } + ] + }, + { + "body": [ + { + "location": "140:2:140:19", + "terms": [ + { + "location": "140:7:140:9", + "type": "ref", + "value": [ + { + "location": "140:7:140:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "140:2:140:6", + "type": "var", + "value": "last" + }, + { + "location": "140:10:140:19", + "type": "string", + "value": "symbols" + } + ] + }, + { + "location": "141:2:141:25", + "terms": [ + { + "location": "141:16:141:18", + "type": "ref", + "value": [ + { + "location": "141:16:141:18", + "type": "var", + "value": "neq" + } + ] + }, + { + "location": "141:2:141:15", + "type": "ref", + "value": [ + { + "location": "141:2:141:7", + "type": "var", + "value": "value" + }, + { + "location": "141:8:141:9", + "type": "number", + "value": 0 + }, + { + "location": "141:11:141:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "141:19:141:25", + "type": "string", + "value": "call" + } + ] + } + ], + "location": "139:1:142:2", + "head": { + "ref": [ + { + "value": "_find_vars", + "location": "139:1:139:11", + "type": "var" + } + ], + "args": [ + { + "type": "var", + "value": "value", + "location": "139:12:139:17" + }, + { + "location": "139:19:139:23", + "type": "var", + "value": "last" + } + ], + "assign": true, + "value": { + "type": "object", + "value": [ + [ + { + "location": "139:29:139:35", + "type": "string", + "value": "some" + }, + { + "type": "call", + "value": [ + { + "location": "139:37:139:57", + "type": "ref", + "value": [ + { + "location": "139:37:139:57", + "type": "var", + "value": "_find_some_decl_vars" + } + ] + }, + { + "location": "139:58:139:63", + "type": "var", + "value": "value" + } + ], + "location": "139:37:139:65" + } + ] + ], + "location": "139:28:139:65" + }, + "location": "139:1:139:65" + } + }, + { + "location": "144:1:147:2", + "head": { + "assign": true, + "value": { + "location": "144:28:144:62", + "type": "object", + "value": [ + [ + { + "location": "144:29:144:36", + "type": "string", + "value": "every" + }, + { + "location": "144:38:144:62", + "type": "call", + "value": [ + { + "location": "144:38:144:54", + "type": "ref", + "value": [ + { + "location": "144:38:144:54", + "type": "var", + "value": "_find_every_vars" + } + ] + }, + { + "location": "144:55:144:60", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "144:1:144:62", + "ref": [ + { + "value": "_find_vars", + "location": "144:1:144:11", + "type": "var" + } + ], + "args": [ + { + "type": "var", + "value": "value", + "location": "144:12:144:17" + }, + { + "value": "last", + "location": "144:19:144:23", + "type": "var" + } + ] + }, + "body": [ + { + "location": "145:2:145:17", + "terms": [ + { + "location": "145:7:145:9", + "type": "ref", + "value": [ + { + "location": "145:7:145:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "var", + "value": "last", + "location": "145:2:145:6" + }, + { + "location": "145:10:145:17", + "type": "string", + "value": "terms" + } + ] + }, + { + "location": "146:2:146:14", + "terms": { + "location": "146:2:146:14", + "type": "ref", + "value": [ + { + "location": "146:2:146:7", + "type": "var", + "value": "value" + }, + { + "value": "domain", + "location": "146:8:146:14", + "type": "string" + } + ] + } + } + ] + }, + { + "head": { + "args": [ + { + "location": "149:12:149:17", + "type": "var", + "value": "value" + }, + { + "location": "149:19:149:23", + "type": "var", + "value": "last" + } + ], + "assign": true, + "value": { + "location": "149:28:149:46", + "type": "object", + "value": [ + [ + { + "location": "149:29:149:35", + "type": "string", + "value": "args" + }, + { + "location": "149:37:149:45", + "type": "var", + "value": "arg_vars" + } + ] + ] + }, + "location": "149:1:149:46", + "ref": [ + { + "location": "149:1:149:11", + "type": "var", + "value": "_find_vars" + } + ] + }, + "body": [ + { + "terms": [ + { + "location": "150:7:150:9", + "type": "ref", + "value": [ + { + "location": "150:7:150:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "150:2:150:6", + "type": "var", + "value": "last" + }, + { + "value": "args", + "location": "150:10:150:16", + "type": "string" + } + ], + "location": "150:2:150:16" + }, + { + "location": "152:2:155:3", + "terms": [ + { + "location": "152:11:152:13", + "type": "ref", + "value": [ + { + "location": "152:11:152:13", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "152:2:152:10", + "type": "var", + "value": "arg_vars" + }, + { + "location": "152:14:155:3", + "type": "arraycomprehension", + "value": { + "term": { + "location": "152:15:152:18", + "type": "var", + "value": "arg" + }, + "body": [ + { + "location": "153:3:153:20", + "terms": { + "location": "153:3:153:7", + "symbols": [ + { + "location": "153:8:153:20", + "type": "call", + "value": [ + { + "location": "153:12:153:14", + "type": "ref", + "value": [ + { + "location": "153:12:153:14", + "type": "var", + "value": "internal" + }, + { + "location": "153:12:153:14", + "type": "string", + "value": "member_2" + } + ] + }, + { + "value": "arg", + "location": "153:8:153:11", + "type": "var" + }, + { + "value": "value", + "location": "153:15:153:20", + "type": "var" + } + ] + } + ] + } + }, + { + "location": "154:3:154:20", + "terms": [ + { + "location": "154:12:154:14", + "type": "ref", + "value": [ + { + "location": "154:12:154:14", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "154:3:154:6", + "type": "var", + "value": "arg" + }, + { + "location": "154:7:154:11", + "type": "string", + "value": "type" + } + ], + "location": "154:3:154:11", + "type": "ref" + }, + { + "location": "154:15:154:20", + "type": "string", + "value": "var" + } + ] + } + ] + } + } + ] + }, + { + "location": "157:2:157:21", + "terms": [ + { + "location": "157:18:157:19", + "type": "ref", + "value": [ + { + "location": "157:18:157:19", + "type": "var", + "value": "gt" + } + ] + }, + { + "location": "157:2:157:17", + "type": "call", + "value": [ + { + "value": [ + { + "location": "157:2:157:7", + "type": "var", + "value": "count" + } + ], + "location": "157:2:157:7", + "type": "ref" + }, + { + "location": "157:8:157:16", + "type": "var", + "value": "arg_vars" + } + ] + }, + { + "location": "157:20:157:21", + "type": "number", + "value": 0 + } + ] + } + ], + "location": "149:1:158:2" + }, + { + "location": "160:1:163:2", + "head": { + "assign": true, + "value": { + "location": "160:22:160:40", + "type": "call", + "value": [ + { + "value": [ + { + "location": "160:22:160:29", + "type": "var", + "value": "sprintf" + } + ], + "location": "160:22:160:29", + "type": "ref" + }, + { + "location": "160:30:160:34", + "type": "string", + "value": "%d" + }, + { + "location": "160:36:160:39", + "type": "array", + "value": [ + { + "location": "160:37:160:38", + "type": "var", + "value": "i" + } + ] + } + ] + }, + "location": "160:1:160:40", + "ref": [ + { + "location": "160:1:160:12", + "type": "var", + "value": "_rule_index" + } + ], + "args": [ + { + "type": "var", + "value": "rule", + "location": "160:13:160:17" + } + ] + }, + "body": [ + { + "location": "161:2:161:21", + "terms": { + "location": "161:2:161:6", + "symbols": [ + { + "location": "161:7:161:21", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "161:12:161:14", + "type": "var", + "value": "internal" + }, + { + "location": "161:12:161:14", + "type": "string", + "value": "member_3" + } + ], + "location": "161:12:161:14" + }, + { + "location": "161:7:161:8", + "type": "var", + "value": "i" + }, + { + "value": "r", + "location": "161:10:161:11", + "type": "var" + }, + { + "location": "161:15:161:21", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "162:2:162:11", + "terms": [ + { + "location": "162:4:162:6", + "type": "ref", + "value": [ + { + "location": "162:4:162:6", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "162:2:162:3", + "type": "var", + "value": "r" + }, + { + "value": "rule", + "location": "162:7:162:11", + "type": "var" + } + ] + } + ] + }, + { + "location": "170:1:174:2", + "annotations": [ + { + "scope": "rule", + "description": "traverses all nodes under provided node (using `walk`), and returns an array with\nall variables declared via assignment (:=), `some`, `every` and in comprehensions\nDEPRECATED: uses ast.found.vars instead\n", + "location": "165:1:169:44" + } + ], + "head": { + "assign": true, + "value": { + "value": { + "term": { + "type": "var", + "value": "var", + "location": "170:21:170:24" + }, + "body": [ + { + "location": "171:2:171:27", + "terms": [ + { + "location": "171:2:171:6", + "type": "ref", + "value": [ + { + "location": "171:2:171:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "171:7:171:11", + "type": "var", + "value": "node" + }, + { + "location": "171:13:171:26", + "type": "array", + "value": [ + { + "location": "171:14:171:18", + "type": "var", + "value": "path" + }, + { + "location": "171:20:171:25", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "173:2:173:50", + "terms": [ + { + "value": [ + { + "location": "173:6:173:8", + "type": "var", + "value": "assign" + } + ], + "location": "173:6:173:8", + "type": "ref" + }, + { + "location": "173:2:173:5", + "type": "var", + "value": "var" + }, + { + "location": "173:9:174:2", + "type": "ref", + "value": [ + { + "location": "173:9:173:44", + "type": "call", + "value": [ + { + "location": "173:9:173:19", + "type": "ref", + "value": [ + { + "location": "173:9:173:19", + "type": "var", + "value": "_find_vars" + } + ] + }, + { + "value": "value", + "location": "173:20:173:25", + "type": "var" + }, + { + "location": "173:27:173:44", + "type": "call", + "value": [ + { + "location": "173:27:173:37", + "type": "ref", + "value": [ + { + "location": "173:27:173:32", + "type": "var", + "value": "regal" + }, + { + "location": "173:33:173:37", + "type": "string", + "value": "last" + } + ] + }, + { + "location": "173:38:173:42", + "type": "var", + "value": "path" + } + ] + } + ] + }, + { + "location": "173:45:173:46", + "type": "var", + "value": "$4" + }, + { + "location": "173:48:173:49", + "type": "var", + "value": "$5" + } + ] + } + ] + } + ] + }, + "location": "170:20:174:2", + "type": "arraycomprehension" + }, + "location": "170:1:174:2", + "ref": [ + { + "location": "170:1:170:10", + "type": "var", + "value": "find_vars" + } + ], + "args": [ + { + "location": "170:11:170:15", + "type": "var", + "value": "node" + } + ] + } + }, + { + "location": "178:1:178:22", + "head": { + "location": "178:1:178:22", + "ref": [ + { + "location": "178:1:178:7", + "type": "var", + "value": "_rules" + } + ], + "assign": true, + "value": { + "location": "178:11:178:22", + "type": "ref", + "value": [ + { + "location": "178:11:178:16", + "type": "var", + "value": "input" + }, + { + "location": "178:17:178:22", + "type": "string", + "value": "rules" + } + ] + } + } + }, + { + "location": "180:1:180:79", + "head": { + "location": "180:1:180:60", + "ref": [ + { + "location": "180:1:180:7", + "type": "var", + "value": "_rules" + } + ], + "assign": true, + "value": { + "location": "180:11:180:60", + "type": "ref", + "value": [ + { + "type": "var", + "value": "data", + "location": "180:11:180:15" + }, + { + "value": "workspace", + "location": "180:16:180:25", + "type": "string" + }, + { + "location": "180:26:180:32", + "type": "string", + "value": "parsed" + }, + { + "location": "180:33:180:53", + "type": "ref", + "value": [ + { + "location": "180:33:180:38", + "type": "var", + "value": "input" + }, + { + "location": "180:39:180:44", + "type": "string", + "value": "regal" + }, + { + "type": "string", + "value": "file", + "location": "180:45:180:49" + }, + { + "type": "string", + "value": "uri", + "location": "180:50:180:53" + } + ] + }, + { + "location": "180:55:180:60", + "type": "string", + "value": "rules" + } + ] + } + }, + "body": [ + { + "location": "180:64:180:79", + "negated": true, + "terms": { + "location": "180:68:180:79", + "type": "ref", + "value": [ + { + "value": "input", + "location": "180:68:180:73", + "type": "var" + }, + { + "value": "rules", + "location": "180:74:180:79", + "type": "string" + } + ] + } + } + ] + }, + { + "head": { + "location": "193:1:193:45", + "ref": [ + { + "location": "193:1:193:6", + "type": "var", + "value": "found" + }, + { + "location": "193:7:193:11", + "type": "string", + "value": "vars" + }, + { + "location": "193:12:193:22", + "type": "var", + "value": "rule_index" + }, + { + "location": "193:24:193:31", + "type": "var", + "value": "context" + } + ], + "key": { + "location": "193:42:193:45", + "type": "var", + "value": "var" + } + }, + "body": [ + { + "location": "194:2:194:24", + "terms": { + "symbols": [ + { + "location": "194:7:194:24", + "type": "call", + "value": [ + { + "location": "194:15:194:17", + "type": "ref", + "value": [ + { + "location": "194:15:194:17", + "type": "var", + "value": "internal" + }, + { + "location": "194:15:194:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "194:7:194:8", + "type": "var", + "value": "i" + }, + { + "location": "194:10:194:14", + "type": "var", + "value": "rule" + }, + { + "location": "194:18:194:24", + "type": "var", + "value": "_rules" + } + ] + } + ], + "location": "194:2:194:6" + } + }, + { + "location": "197:2:197:34", + "terms": [ + { + "value": [ + { + "value": "assign", + "location": "197:13:197:15", + "type": "var" + } + ], + "location": "197:13:197:15", + "type": "ref" + }, + { + "location": "197:2:197:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "197:16:197:34", + "type": "call", + "value": [ + { + "location": "197:16:197:23", + "type": "ref", + "value": [ + { + "value": "sprintf", + "location": "197:16:197:23", + "type": "var" + } + ] + }, + { + "location": "197:24:197:28", + "type": "string", + "value": "%d" + }, + { + "location": "197:30:197:33", + "type": "array", + "value": [ + { + "value": "i", + "location": "197:31:197:32", + "type": "var" + } + ] + } + ] + } + ] + }, + { + "location": "199:2:199:27", + "terms": [ + { + "location": "199:2:199:6", + "type": "ref", + "value": [ + { + "location": "199:2:199:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "value": "rule", + "location": "199:7:199:11", + "type": "var" + }, + { + "location": "199:13:199:26", + "type": "array", + "value": [ + { + "location": "199:14:199:18", + "type": "var", + "value": "path" + }, + { + "location": "199:20:199:25", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "201:2:201:59", + "terms": { + "location": "201:2:201:6", + "symbols": [ + { + "location": "201:7:201:59", + "type": "call", + "value": [ + { + "location": "201:21:201:23", + "type": "ref", + "value": [ + { + "location": "201:21:201:23", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_3", + "location": "201:21:201:23" + } + ] + }, + { + "location": "201:7:201:14", + "type": "var", + "value": "context" + }, + { + "location": "201:16:201:20", + "type": "var", + "value": "vars" + }, + { + "location": "201:24:201:59", + "type": "call", + "value": [ + { + "location": "201:24:201:34", + "type": "ref", + "value": [ + { + "location": "201:24:201:34", + "type": "var", + "value": "_find_vars" + } + ] + }, + { + "location": "201:35:201:40", + "type": "var", + "value": "value" + }, + { + "location": "201:42:201:59", + "type": "call", + "value": [ + { + "location": "201:42:201:52", + "type": "ref", + "value": [ + { + "location": "201:42:201:47", + "type": "var", + "value": "regal" + }, + { + "location": "201:48:201:52", + "type": "string", + "value": "last" + } + ] + }, + { + "type": "var", + "value": "path", + "location": "201:53:201:57" + } + ] + } + ] + } + ] + } + ] + } + }, + { + "location": "202:2:202:18", + "terms": { + "location": "202:2:202:6", + "symbols": [ + { + "location": "202:7:202:18", + "type": "call", + "value": [ + { + "location": "202:11:202:13", + "type": "ref", + "value": [ + { + "location": "202:11:202:13", + "type": "var", + "value": "internal" + }, + { + "location": "202:11:202:13", + "type": "string", + "value": "member_2" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "202:7:202:10" + }, + { + "type": "var", + "value": "vars", + "location": "202:14:202:18" + } + ] + } + ] + } + } + ], + "location": "193:1:203:2", + "annotations": [ + { + "location": "182:1:192:10", + "scope": "rule", + "description": "object containing all variables found in the input AST, keyed first by the index of\nthe rule where the variables were found (as a numeric string), and then the context\nof the variable, which will be one of:\n- term\n- assign\n- every\n- some\n- somein\n- ref\n" + } + ] + }, + { + "location": "207:1:216:2", + "annotations": [ + { + "location": "205:1:206:41", + "scope": "rule", + "description": "all refs foundd in module" + } + ], + "head": { + "ref": [ + { + "value": "found", + "location": "207:1:207:6", + "type": "var" + }, + { + "location": "207:7:207:11", + "type": "string", + "value": "refs" + }, + { + "location": "207:12:207:22", + "type": "var", + "value": "rule_index" + } + ], + "key": { + "value": "value", + "location": "207:33:207:38", + "type": "var" + }, + "location": "207:1:207:38" + }, + "body": [ + { + "location": "208:2:208:24", + "terms": { + "location": "208:2:208:6", + "symbols": [ + { + "location": "208:7:208:24", + "type": "call", + "value": [ + { + "location": "208:15:208:17", + "type": "ref", + "value": [ + { + "location": "208:15:208:17", + "type": "var", + "value": "internal" + }, + { + "location": "208:15:208:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "208:7:208:8", + "type": "var", + "value": "i" + }, + { + "location": "208:10:208:14", + "type": "var", + "value": "rule" + }, + { + "location": "208:18:208:24", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "211:2:211:34", + "terms": [ + { + "location": "211:13:211:15", + "type": "ref", + "value": [ + { + "location": "211:13:211:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "211:2:211:12", + "type": "var", + "value": "rule_index" + }, + { + "value": [ + { + "location": "211:16:211:23", + "type": "ref", + "value": [ + { + "location": "211:16:211:23", + "type": "var", + "value": "sprintf" + } + ] + }, + { + "location": "211:24:211:28", + "type": "string", + "value": "%d" + }, + { + "location": "211:30:211:33", + "type": "array", + "value": [ + { + "location": "211:31:211:32", + "type": "var", + "value": "i" + } + ] + } + ], + "location": "211:16:211:34", + "type": "call" + } + ] + }, + { + "location": "213:2:213:24", + "terms": [ + { + "location": "213:2:213:6", + "type": "ref", + "value": [ + { + "location": "213:2:213:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "213:7:213:11", + "type": "var", + "value": "rule" + }, + { + "location": "213:13:213:23", + "type": "array", + "value": [ + { + "location": "213:14:213:15", + "type": "var", + "value": "$6" + }, + { + "location": "213:17:213:22", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "215:2:215:15", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "215:2:215:8", + "type": "var", + "value": "is_ref" + } + ], + "location": "215:2:215:8" + }, + { + "location": "215:9:215:14", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "annotations": [ + { + "location": "218:1:219:44", + "scope": "rule", + "description": "all symbols foundd in module" + } + ], + "head": { + "ref": [ + { + "location": "220:1:220:6", + "type": "var", + "value": "found" + }, + { + "location": "220:7:220:14", + "type": "string", + "value": "symbols" + }, + { + "value": "rule_index", + "location": "220:15:220:25", + "type": "var" + } + ], + "key": { + "location": "220:36:220:49", + "type": "ref", + "value": [ + { + "location": "220:36:220:41", + "type": "var", + "value": "value" + }, + { + "type": "string", + "value": "symbols", + "location": "220:42:220:49" + } + ] + }, + "location": "220:1:220:49" + }, + "body": [ + { + "location": "221:2:221:24", + "terms": { + "location": "221:2:221:6", + "symbols": [ + { + "location": "221:7:221:24", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "221:15:221:17", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_3", + "location": "221:15:221:17" + } + ], + "location": "221:15:221:17" + }, + { + "location": "221:7:221:8", + "type": "var", + "value": "i" + }, + { + "location": "221:10:221:14", + "type": "var", + "value": "rule" + }, + { + "location": "221:18:221:24", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "224:2:224:34", + "terms": [ + { + "location": "224:13:224:15", + "type": "ref", + "value": [ + { + "location": "224:13:224:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "224:2:224:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "224:16:224:34", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "224:16:224:23", + "type": "var", + "value": "sprintf" + } + ], + "location": "224:16:224:23" + }, + { + "location": "224:24:224:28", + "type": "string", + "value": "%d" + }, + { + "location": "224:30:224:33", + "type": "array", + "value": [ + { + "location": "224:31:224:32", + "type": "var", + "value": "i" + } + ] + } + ] + } + ] + }, + { + "location": "226:2:226:24", + "terms": [ + { + "location": "226:2:226:6", + "type": "ref", + "value": [ + { + "type": "var", + "value": "walk", + "location": "226:2:226:6" + } + ] + }, + { + "location": "226:7:226:11", + "type": "var", + "value": "rule" + }, + { + "location": "226:13:226:23", + "type": "array", + "value": [ + { + "location": "226:14:226:15", + "type": "var", + "value": "$7" + }, + { + "value": "value", + "location": "226:17:226:22", + "type": "var" + } + ] + } + ] + } + ], + "location": "220:1:227:2" + }, + { + "location": "231:1:240:2", + "annotations": [ + { + "location": "229:1:230:50", + "scope": "rule", + "description": "all comprehensions found in module" + } + ], + "head": { + "ref": [ + { + "location": "231:1:231:6", + "type": "var", + "value": "found" + }, + { + "location": "231:7:231:21", + "type": "string", + "value": "comprehensions" + }, + { + "location": "231:22:231:32", + "type": "var", + "value": "rule_index" + } + ], + "key": { + "location": "231:43:231:48", + "type": "var", + "value": "value" + }, + "location": "231:1:231:48" + }, + "body": [ + { + "location": "232:2:232:24", + "terms": { + "location": "232:2:232:6", + "symbols": [ + { + "location": "232:7:232:24", + "type": "call", + "value": [ + { + "location": "232:15:232:17", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "232:15:232:17" + }, + { + "location": "232:15:232:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "232:7:232:8", + "type": "var", + "value": "i" + }, + { + "type": "var", + "value": "rule", + "location": "232:10:232:14" + }, + { + "value": "_rules", + "location": "232:18:232:24", + "type": "var" + } + ] + } + ] + } + }, + { + "location": "235:2:235:34", + "terms": [ + { + "location": "235:13:235:15", + "type": "ref", + "value": [ + { + "location": "235:13:235:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "235:2:235:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "235:16:235:34", + "type": "call", + "value": [ + { + "location": "235:16:235:23", + "type": "ref", + "value": [ + { + "location": "235:16:235:23", + "type": "var", + "value": "sprintf" + } + ] + }, + { + "location": "235:24:235:28", + "type": "string", + "value": "%d" + }, + { + "location": "235:30:235:33", + "type": "array", + "value": [ + { + "value": "i", + "location": "235:31:235:32", + "type": "var" + } + ] + } + ] + } + ] + }, + { + "location": "237:2:237:24", + "terms": [ + { + "location": "237:2:237:6", + "type": "ref", + "value": [ + { + "value": "walk", + "location": "237:2:237:6", + "type": "var" + } + ] + }, + { + "type": "var", + "value": "rule", + "location": "237:7:237:11" + }, + { + "value": [ + { + "value": "$8", + "location": "237:14:237:15", + "type": "var" + }, + { + "value": "value", + "location": "237:17:237:22", + "type": "var" + } + ], + "location": "237:13:237:23", + "type": "array" + } + ] + }, + { + "location": "239:2:239:81", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "239:13:239:15", + "type": "var", + "value": "internal" + }, + { + "location": "239:13:239:15", + "type": "string", + "value": "member_2" + } + ], + "location": "239:13:239:15" + }, + { + "location": "239:2:239:12", + "type": "ref", + "value": [ + { + "location": "239:2:239:7", + "type": "var", + "value": "value" + }, + { + "location": "239:8:239:12", + "type": "string", + "value": "type" + } + ] + }, + { + "value": [ + { + "type": "string", + "value": "arraycomprehension", + "location": "239:17:239:37" + }, + { + "value": "objectcomprehension", + "location": "239:39:239:60", + "type": "string" + }, + { + "location": "239:62:239:80", + "type": "string", + "value": "setcomprehension" + } + ], + "location": "239:16:239:81", + "type": "set" + } + ] + } + ] + }, + { + "head": { + "assign": true, + "value": { + "location": "248:45:253:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "248:46:248:49", + "type": "var", + "value": "var" + }, + "body": [ + { + "location": "249:2:249:44", + "terms": [ + { + "location": "249:6:249:8", + "type": "ref", + "value": [ + { + "type": "var", + "value": "assign", + "location": "249:6:249:8" + } + ] + }, + { + "value": "var", + "location": "249:2:249:5", + "type": "var" + }, + { + "location": "249:9:249:44", + "type": "ref", + "value": [ + { + "location": "249:9:249:14", + "type": "var", + "value": "found" + }, + { + "location": "249:15:249:19", + "type": "string", + "value": "vars" + }, + { + "type": "call", + "value": [ + { + "location": "249:20:249:31", + "type": "ref", + "value": [ + { + "location": "249:20:249:31", + "type": "var", + "value": "_rule_index" + } + ] + }, + { + "location": "249:32:249:36", + "type": "var", + "value": "rule" + } + ], + "location": "249:20:249:38" + }, + { + "location": "249:39:249:40", + "type": "var", + "value": "$9" + }, + { + "location": "249:42:249:43", + "type": "var", + "value": "$10" + } + ] + } + ] + }, + { + "terms": [ + { + "location": "251:6:251:17", + "type": "ref", + "value": [ + { + "location": "251:6:251:17", + "type": "var", + "value": "is_wildcard" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "251:18:251:21" + } + ], + "location": "251:2:251:22", + "negated": true + }, + { + "location": "252:2:252:64", + "terms": [ + { + "location": "252:2:252:18", + "type": "ref", + "value": [ + { + "location": "252:2:252:18", + "type": "var", + "value": "_before_location" + } + ] + }, + { + "type": "var", + "value": "rule", + "location": "252:19:252:23" + }, + { + "location": "252:25:252:28", + "type": "var", + "value": "var" + }, + { + "location": "252:30:252:64", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "252:30:252:34", + "type": "var", + "value": "util" + }, + { + "location": "252:35:252:53", + "type": "string", + "value": "to_location_object" + } + ], + "location": "252:30:252:53" + }, + { + "location": "252:54:252:62", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + } + }, + "location": "248:1:253:2", + "ref": [ + { + "location": "248:1:248:25", + "type": "var", + "value": "find_vars_in_local_scope" + } + ], + "args": [ + { + "location": "248:26:248:30", + "type": "var", + "value": "rule" + }, + { + "type": "var", + "value": "location", + "location": "248:32:248:40" + } + ] + }, + "location": "248:1:253:2", + "annotations": [ + { + "location": "242:1:247:28", + "scope": "rule", + "description": "finds all vars declared in `rule` *before* the `location` provided\nnote: this isn't 100% accurate, as it doesn't take into account `=`\nassignments / unification, but it's likely good enough since other rules\nrecommend against those\n" + } + ] + }, + { + "location": "255:1:262:2", + "head": { + "args": [ + { + "location": "255:15:255:23", + "type": "var", + "value": "location" + } + ], + "assign": true, + "value": { + "location": "255:28:255:31", + "type": "var", + "value": "end" + }, + "location": "255:1:255:31", + "ref": [ + { + "location": "255:1:255:14", + "type": "var", + "value": "_end_location" + } + ] + }, + "body": [ + { + "terms": [ + { + "value": [ + { + "value": "assign", + "location": "256:6:256:8", + "type": "var" + } + ], + "location": "256:6:256:8", + "type": "ref" + }, + { + "location": "256:2:256:5", + "type": "var", + "value": "loc" + }, + { + "value": [ + { + "location": "256:9:256:32", + "type": "ref", + "value": [ + { + "location": "256:9:256:13", + "type": "var", + "value": "util" + }, + { + "location": "256:14:256:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "type": "var", + "value": "location", + "location": "256:33:256:41" + } + ], + "location": "256:9:256:42", + "type": "call" + } + ], + "location": "256:2:256:42" + }, + { + "location": "257:2:257:32", + "terms": [ + { + "location": "257:8:257:10", + "type": "ref", + "value": [ + { + "location": "257:8:257:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "257:2:257:7", + "type": "var", + "value": "lines" + }, + { + "location": "257:11:257:32", + "type": "call", + "value": [ + { + "location": "257:11:257:16", + "type": "ref", + "value": [ + { + "location": "257:11:257:16", + "type": "var", + "value": "split" + } + ] + }, + { + "location": "257:17:257:25", + "type": "ref", + "value": [ + { + "location": "257:17:257:20", + "type": "var", + "value": "loc" + }, + { + "location": "257:21:257:25", + "type": "string", + "value": "text" + } + ] + }, + { + "location": "257:27:257:31", + "type": "string", + "value": "\n" + } + ] + } + ] + }, + { + "location": "258:2:261:3", + "terms": [ + { + "value": [ + { + "type": "var", + "value": "assign", + "location": "258:6:258:8" + } + ], + "location": "258:6:258:8", + "type": "ref" + }, + { + "location": "258:2:258:5", + "type": "var", + "value": "end" + }, + { + "location": "258:9:261:3", + "type": "object", + "value": [ + [ + { + "location": "259:3:259:8", + "type": "string", + "value": "row" + }, + { + "value": [ + { + "value": [ + { + "location": "259:35:259:36", + "type": "var", + "value": "minus" + } + ], + "location": "259:35:259:36", + "type": "ref" + }, + { + "value": [ + { + "location": "259:19:259:20", + "type": "ref", + "value": [ + { + "location": "259:19:259:20", + "type": "var", + "value": "plus" + } + ] + }, + { + "location": "259:11:259:18", + "type": "ref", + "value": [ + { + "location": "259:11:259:14", + "type": "var", + "value": "loc" + }, + { + "location": "259:15:259:18", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "259:21:259:34", + "type": "call", + "value": [ + { + "location": "259:21:259:26", + "type": "ref", + "value": [ + { + "location": "259:21:259:26", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "259:27:259:32", + "type": "var", + "value": "lines" + } + ] + } + ], + "location": "259:11:259:35", + "type": "call" + }, + { + "type": "number", + "value": 1, + "location": "259:37:259:38" + } + ], + "location": "259:11:259:39", + "type": "call" + } + ], + [ + { + "type": "string", + "value": "col", + "location": "260:3:260:8" + }, + { + "location": "260:10:260:44", + "type": "call", + "value": [ + { + "location": "260:18:260:19", + "type": "ref", + "value": [ + { + "location": "260:18:260:19", + "type": "var", + "value": "plus" + } + ] + }, + { + "value": [ + { + "location": "260:10:260:13", + "type": "var", + "value": "loc" + }, + { + "location": "260:14:260:17", + "type": "string", + "value": "col" + } + ], + "location": "260:10:260:17", + "type": "ref" + }, + { + "location": "260:20:260:45", + "type": "call", + "value": [ + { + "location": "260:20:260:25", + "type": "ref", + "value": [ + { + "type": "var", + "value": "count", + "location": "260:20:260:25" + } + ] + }, + { + "location": "260:26:260:44", + "type": "call", + "value": [ + { + "location": "260:26:260:36", + "type": "ref", + "value": [ + { + "location": "260:26:260:31", + "type": "var", + "value": "regal" + }, + { + "location": "260:32:260:36", + "type": "string", + "value": "last" + } + ] + }, + { + "location": "260:37:260:42", + "type": "var", + "value": "lines" + } + ] + } + ] + } + ] + } + ] + ] + } + ] + } + ] + }, + { + "location": "266:1:278:2", + "head": { + "location": "266:1:266:36", + "ref": [ + { + "type": "var", + "value": "_before_location", + "location": "266:1:266:17" + } + ], + "args": [ + { + "location": "266:18:266:22", + "type": "var", + "value": "rule" + }, + { + "location": "266:24:266:25", + "type": "var", + "value": "$11" + }, + { + "location": "266:27:266:35", + "type": "var", + "value": "location" + } + ], + "value": { + "type": "boolean", + "value": true + } + }, + "body": [ + { + "location": "267:2:267:42", + "terms": [ + { + "value": [ + { + "location": "267:6:267:8", + "type": "var", + "value": "assign" + } + ], + "location": "267:6:267:8", + "type": "ref" + }, + { + "location": "267:2:267:5", + "type": "var", + "value": "loc" + }, + { + "location": "267:9:267:42", + "type": "call", + "value": [ + { + "location": "267:9:267:32", + "type": "ref", + "value": [ + { + "location": "267:9:267:13", + "type": "var", + "value": "util" + }, + { + "location": "267:14:267:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "267:33:267:41", + "type": "var", + "value": "location" + } + ] + } + ] + }, + { + "location": "269:2:269:66", + "terms": [ + { + "location": "269:14:269:16", + "type": "ref", + "value": [ + { + "location": "269:14:269:16", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "269:2:269:13", + "type": "var", + "value": "value_start" + }, + { + "value": [ + { + "type": "ref", + "value": [ + { + "location": "269:17:269:21", + "type": "var", + "value": "util" + }, + { + "location": "269:22:269:40", + "type": "string", + "value": "to_location_object" + } + ], + "location": "269:17:269:40" + }, + { + "location": "269:41:269:65", + "type": "ref", + "value": [ + { + "location": "269:41:269:45", + "type": "var", + "value": "rule" + }, + { + "location": "269:46:269:50", + "type": "string", + "value": "head" + }, + { + "value": "value", + "location": "269:51:269:56", + "type": "string" + }, + { + "value": "location", + "location": "269:57:269:65", + "type": "string" + } + ] + } + ], + "location": "269:17:269:66", + "type": "call" + } + ] + }, + { + "location": "271:2:271:28", + "terms": [ + { + "value": [ + { + "value": "gte", + "location": "271:10:271:12", + "type": "var" + } + ], + "location": "271:10:271:12", + "type": "ref" + }, + { + "type": "ref", + "value": [ + { + "location": "271:2:271:5", + "type": "var", + "value": "loc" + }, + { + "location": "271:6:271:9", + "type": "string", + "value": "row" + } + ], + "location": "271:2:271:9" + }, + { + "location": "271:13:271:28", + "type": "ref", + "value": [ + { + "location": "271:13:271:24", + "type": "var", + "value": "value_start" + }, + { + "location": "271:25:271:28", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "272:2:272:28", + "terms": [ + { + "location": "272:10:272:12", + "type": "ref", + "value": [ + { + "value": "gte", + "location": "272:10:272:12", + "type": "var" + } + ] + }, + { + "location": "272:2:272:9", + "type": "ref", + "value": [ + { + "location": "272:2:272:5", + "type": "var", + "value": "loc" + }, + { + "location": "272:6:272:9", + "type": "string", + "value": "col" + } + ] + }, + { + "location": "272:13:272:28", + "type": "ref", + "value": [ + { + "location": "272:13:272:24", + "type": "var", + "value": "value_start" + }, + { + "location": "272:25:272:28", + "type": "string", + "value": "col" + } + ] + } + ] + }, + { + "location": "274:2:274:79", + "terms": [ + { + "location": "274:12:274:14", + "type": "ref", + "value": [ + { + "value": "assign", + "location": "274:12:274:14", + "type": "var" + } + ] + }, + { + "value": "value_end", + "location": "274:2:274:11", + "type": "var" + }, + { + "location": "274:15:274:79", + "type": "call", + "value": [ + { + "location": "274:15:274:28", + "type": "ref", + "value": [ + { + "type": "var", + "value": "_end_location", + "location": "274:15:274:28" + } + ] + }, + { + "location": "274:29:274:79", + "type": "call", + "value": [ + { + "location": "274:29:274:52", + "type": "ref", + "value": [ + { + "location": "274:29:274:33", + "type": "var", + "value": "util" + }, + { + "value": "to_location_object", + "location": "274:34:274:52", + "type": "string" + } + ] + }, + { + "location": "274:53:274:77", + "type": "ref", + "value": [ + { + "location": "274:53:274:57", + "type": "var", + "value": "rule" + }, + { + "location": "274:58:274:62", + "type": "string", + "value": "head" + }, + { + "location": "274:63:274:68", + "type": "string", + "value": "value" + }, + { + "location": "274:69:274:77", + "type": "string", + "value": "location" + } + ] + } + ] + } + ] + } + ] + }, + { + "location": "276:2:276:26", + "terms": [ + { + "location": "276:10:276:12", + "type": "ref", + "value": [ + { + "value": "lte", + "location": "276:10:276:12", + "type": "var" + } + ] + }, + { + "location": "276:2:276:9", + "type": "ref", + "value": [ + { + "location": "276:2:276:5", + "type": "var", + "value": "loc" + }, + { + "location": "276:6:276:9", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "276:13:276:26", + "type": "ref", + "value": [ + { + "location": "276:13:276:22", + "type": "var", + "value": "value_end" + }, + { + "location": "276:23:276:26", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "277:2:277:26", + "terms": [ + { + "location": "277:10:277:12", + "type": "ref", + "value": [ + { + "location": "277:10:277:12", + "type": "var", + "value": "lte" + } + ] + }, + { + "location": "277:2:277:9", + "type": "ref", + "value": [ + { + "location": "277:2:277:5", + "type": "var", + "value": "loc" + }, + { + "location": "277:6:277:9", + "type": "string", + "value": "col" + } + ] + }, + { + "location": "277:13:277:26", + "type": "ref", + "value": [ + { + "value": "value_end", + "location": "277:13:277:22", + "type": "var" + }, + { + "location": "277:23:277:26", + "type": "string", + "value": "col" + } + ] + } + ] + } + ] + }, + { + "head": { + "ref": [ + { + "location": "280:1:280:17", + "type": "var", + "value": "_before_location" + } + ], + "args": [ + { + "location": "280:18:280:19", + "type": "var", + "value": "$12" + }, + { + "value": "var", + "location": "280:21:280:24", + "type": "var" + }, + { + "location": "280:26:280:34", + "type": "var", + "value": "location" + } + ], + "value": { + "type": "boolean", + "value": true + }, + "location": "280:1:280:35" + }, + "body": [ + { + "location": "281:2:281:83", + "terms": [ + { + "value": [ + { + "location": "281:44:281:45", + "type": "var", + "value": "lt" + } + ], + "location": "281:44:281:45", + "type": "ref" + }, + { + "value": [ + { + "location": "281:2:281:39", + "type": "call", + "value": [ + { + "value": [ + { + "type": "var", + "value": "util", + "location": "281:2:281:6" + }, + { + "type": "string", + "value": "to_location_object", + "location": "281:7:281:25" + } + ], + "location": "281:2:281:25", + "type": "ref" + }, + { + "value": [ + { + "location": "281:26:281:29", + "type": "var", + "value": "var" + }, + { + "location": "281:30:281:38", + "type": "string", + "value": "location" + } + ], + "location": "281:26:281:38", + "type": "ref" + } + ] + }, + { + "location": "281:40:281:43", + "type": "string", + "value": "row" + } + ], + "location": "281:2:281:45", + "type": "ref" + }, + { + "location": "281:46:282:2", + "type": "ref", + "value": [ + { + "location": "281:46:281:79", + "type": "call", + "value": [ + { + "location": "281:46:281:69", + "type": "ref", + "value": [ + { + "location": "281:46:281:50", + "type": "var", + "value": "util" + }, + { + "location": "281:51:281:69", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "281:70:281:78", + "type": "var", + "value": "location" + } + ] + }, + { + "location": "281:80:281:83", + "type": "string", + "value": "row" + } + ] + } + ] + } + ], + "location": "280:1:282:2" + }, + { + "location": "284:1:290:2", + "head": { + "value": { + "type": "boolean", + "value": true + }, + "location": "284:1:284:35", + "ref": [ + { + "location": "284:1:284:17", + "type": "var", + "value": "_before_location" + } + ], + "args": [ + { + "location": "284:18:284:19", + "type": "var", + "value": "$13" + }, + { + "value": "var", + "location": "284:21:284:24", + "type": "var" + }, + { + "type": "var", + "value": "location", + "location": "284:26:284:34" + } + ] + }, + "body": [ + { + "terms": [ + { + "location": "285:10:285:12", + "type": "ref", + "value": [ + { + "location": "285:10:285:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "285:2:285:9", + "type": "var", + "value": "var_loc" + }, + { + "location": "285:13:285:50", + "type": "call", + "value": [ + { + "location": "285:13:285:36", + "type": "ref", + "value": [ + { + "location": "285:13:285:17", + "type": "var", + "value": "util" + }, + { + "location": "285:18:285:36", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "285:37:285:49", + "type": "ref", + "value": [ + { + "location": "285:37:285:40", + "type": "var", + "value": "var" + }, + { + "location": "285:41:285:49", + "type": "string", + "value": "location" + } + ] + } + ] + } + ], + "location": "285:2:285:50" + }, + { + "location": "286:2:286:42", + "terms": [ + { + "value": [ + { + "location": "286:6:286:8", + "type": "var", + "value": "assign" + } + ], + "location": "286:6:286:8", + "type": "ref" + }, + { + "value": "loc", + "location": "286:2:286:5", + "type": "var" + }, + { + "location": "286:9:286:42", + "type": "call", + "value": [ + { + "location": "286:9:286:32", + "type": "ref", + "value": [ + { + "location": "286:9:286:13", + "type": "var", + "value": "util" + }, + { + "location": "286:14:286:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "286:33:286:41", + "type": "var", + "value": "location" + } + ] + } + ] + }, + { + "location": "288:2:288:24", + "terms": [ + { + "location": "288:14:288:16", + "type": "ref", + "value": [ + { + "location": "288:14:288:16", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "288:2:288:13", + "type": "ref", + "value": [ + { + "value": "var_loc", + "location": "288:2:288:9", + "type": "var" + }, + { + "location": "288:10:288:13", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "288:17:288:24", + "type": "ref", + "value": [ + { + "location": "288:17:288:20", + "type": "var", + "value": "loc" + }, + { + "location": "288:21:288:24", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "289:2:289:23", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "lt", + "location": "289:14:289:15", + "type": "var" + } + ], + "location": "289:14:289:15" + }, + { + "value": [ + { + "location": "289:2:289:9", + "type": "var", + "value": "var_loc" + }, + { + "location": "289:10:289:13", + "type": "string", + "value": "col" + } + ], + "location": "289:2:289:13", + "type": "ref" + }, + { + "value": [ + { + "location": "289:16:289:19", + "type": "var", + "value": "loc" + }, + { + "location": "289:20:289:23", + "type": "string", + "value": "col" + } + ], + "location": "289:16:289:23", + "type": "ref" + } + ] + } + ] + }, + { + "location": "294:1:299:2", + "annotations": [ + { + "location": "292:1:293:77", + "scope": "rule", + "description": "find *only* names in the local scope, and not e.g. rule names" + } + ], + "head": { + "location": "294:1:294:51", + "ref": [ + { + "location": "294:1:294:26", + "type": "var", + "value": "find_names_in_local_scope" + } + ], + "args": [ + { + "location": "294:27:294:31", + "type": "var", + "value": "rule" + }, + { + "location": "294:33:294:41", + "type": "var", + "value": "location" + } + ], + "assign": true, + "value": { + "type": "var", + "value": "names", + "location": "294:46:294:51" + } + }, + "body": [ + { + "location": "295:2:295:43", + "terms": [ + { + "location": "295:15:295:17", + "type": "ref", + "value": [ + { + "location": "295:15:295:17", + "type": "var", + "value": "assign" + } + ] + }, + { + "value": "fn_arg_names", + "location": "295:2:295:14", + "type": "var" + }, + { + "value": [ + { + "location": "295:18:295:37", + "type": "ref", + "value": [ + { + "type": "var", + "value": "_function_arg_names", + "location": "295:18:295:37" + } + ] + }, + { + "location": "295:38:295:42", + "type": "var", + "value": "rule" + } + ], + "location": "295:18:295:43", + "type": "call" + } + ] + }, + { + "location": "296:2:296:106", + "terms": [ + { + "location": "296:12:296:14", + "type": "ref", + "value": [ + { + "location": "296:12:296:14", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "296:2:296:11", + "type": "var", + "value": "var_names" + }, + { + "location": "296:15:296:106", + "type": "setcomprehension", + "value": { + "term": { + "value": [ + { + "type": "var", + "value": "var", + "location": "296:16:296:19" + }, + { + "type": "string", + "value": "value", + "location": "296:20:296:25" + } + ], + "location": "296:16:296:25", + "type": "ref" + }, + "body": [ + { + "location": "296:28:296:105", + "terms": { + "location": "296:28:296:32", + "symbols": [ + { + "location": "296:33:296:105", + "type": "call", + "value": [ + { + "location": "296:37:296:39", + "type": "ref", + "value": [ + { + "location": "296:37:296:39", + "type": "var", + "value": "internal" + }, + { + "location": "296:37:296:39", + "type": "string", + "value": "member_2" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "296:33:296:36" + }, + { + "location": "296:40:296:106", + "type": "call", + "value": [ + { + "location": "296:40:296:64", + "type": "ref", + "value": [ + { + "location": "296:40:296:64", + "type": "var", + "value": "find_vars_in_local_scope" + } + ] + }, + { + "location": "296:65:296:69", + "type": "var", + "value": "rule" + }, + { + "location": "296:71:296:105", + "type": "call", + "value": [ + { + "location": "296:71:296:94", + "type": "ref", + "value": [ + { + "value": "util", + "location": "296:71:296:75", + "type": "var" + }, + { + "location": "296:76:296:94", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "296:95:296:103", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + } + ] + } + } + ] + } + } + ] + }, + { + "location": "298:2:298:35", + "terms": [ + { + "location": "298:8:298:10", + "type": "ref", + "value": [ + { + "location": "298:8:298:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "298:2:298:7", + "type": "var", + "value": "names" + }, + { + "location": "298:11:298:35", + "type": "call", + "value": [ + { + "location": "298:24:298:25", + "type": "ref", + "value": [ + { + "location": "298:24:298:25", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "298:11:298:23", + "type": "var", + "value": "fn_arg_names" + }, + { + "type": "var", + "value": "var_names", + "location": "298:26:298:35" + } + ] + } + ] + } + ] + }, + { + "location": "301:1:304:2", + "head": { + "value": { + "location": "301:30:304:2", + "type": "setcomprehension", + "value": { + "term": { + "value": [ + { + "location": "301:31:301:34", + "type": "var", + "value": "arg" + }, + { + "location": "301:35:301:40", + "type": "string", + "value": "value" + } + ], + "location": "301:31:301:40", + "type": "ref" + }, + "body": [ + { + "location": "302:2:302:28", + "terms": { + "location": "302:2:302:6", + "symbols": [ + { + "location": "302:7:302:28", + "type": "call", + "value": [ + { + "location": "302:11:302:13", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "302:11:302:13" + }, + { + "location": "302:11:302:13", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "302:7:302:10", + "type": "var", + "value": "arg" + }, + { + "value": [ + { + "location": "302:14:302:18", + "type": "var", + "value": "rule" + }, + { + "location": "302:19:302:23", + "type": "string", + "value": "head" + }, + { + "value": "args", + "location": "302:24:302:28", + "type": "string" + } + ], + "location": "302:14:302:28", + "type": "ref" + } + ] + } + ] + } + }, + { + "location": "303:2:303:19", + "terms": [ + { + "location": "303:11:303:13", + "type": "ref", + "value": [ + { + "location": "303:11:303:13", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "303:2:303:10", + "type": "ref", + "value": [ + { + "value": "arg", + "location": "303:2:303:5", + "type": "var" + }, + { + "location": "303:6:303:10", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "303:14:303:19", + "type": "string", + "value": "var" + } + ] + } + ] + } + }, + "location": "301:1:304:2", + "ref": [ + { + "location": "301:1:301:20", + "type": "var", + "value": "_function_arg_names" + } + ], + "args": [ + { + "location": "301:21:301:25", + "type": "var", + "value": "rule" + } + ], + "assign": true + } + }, + { + "location": "310:1:315:2", + "annotations": [ + { + "location": "306:1:309:82", + "scope": "rule", + "description": "similar to `find_vars_in_local_scope`, but returns all variable names in scope\nof the given location *and* the rule names present in the scope (i.e. module)\n" + } + ], + "head": { + "assign": true, + "value": { + "location": "310:40:310:45", + "type": "var", + "value": "names" + }, + "location": "310:1:310:45", + "ref": [ + { + "location": "310:1:310:20", + "type": "var", + "value": "find_names_in_scope" + } + ], + "args": [ + { + "location": "310:21:310:25", + "type": "var", + "value": "rule" + }, + { + "value": "location", + "location": "310:27:310:35", + "type": "var" + } + ] + }, + "body": [ + { + "location": "311:2:311:78", + "terms": [ + { + "location": "311:9:311:11", + "type": "ref", + "value": [ + { + "location": "311:9:311:11", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "311:2:311:8", + "type": "var", + "value": "locals" + }, + { + "location": "311:12:311:78", + "type": "call", + "value": [ + { + "location": "311:12:311:37", + "type": "ref", + "value": [ + { + "value": "find_names_in_local_scope", + "location": "311:12:311:37", + "type": "var" + } + ] + }, + { + "location": "311:38:311:42", + "type": "var", + "value": "rule" + }, + { + "location": "311:44:311:78", + "type": "call", + "value": [ + { + "location": "311:44:311:67", + "type": "ref", + "value": [ + { + "location": "311:44:311:48", + "type": "var", + "value": "util" + }, + { + "type": "string", + "value": "to_location_object", + "location": "311:49:311:67" + } + ] + }, + { + "location": "311:68:311:76", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + }, + { + "location": "314:2:314:55", + "terms": [ + { + "location": "314:8:314:10", + "type": "ref", + "value": [ + { + "location": "314:8:314:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "314:2:314:7", + "type": "var", + "value": "names" + }, + { + "location": "314:12:314:56", + "type": "call", + "value": [ + { + "location": "314:47:314:48", + "type": "ref", + "value": [ + { + "location": "314:47:314:48", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "314:12:314:47", + "type": "call", + "value": [ + { + "location": "314:23:314:24", + "type": "ref", + "value": [ + { + "location": "314:23:314:24", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "314:12:314:22", + "type": "var", + "value": "rule_names" + }, + { + "location": "314:25:314:45", + "type": "var", + "value": "imported_identifiers" + } + ] + }, + { + "type": "var", + "value": "locals", + "location": "314:49:314:55" + } + ] + } + ] + } + ] + }, + { + "location": "321:1:324:2", + "annotations": [ + { + "location": "317:1:320:39", + "scope": "rule", + "description": "find all variables declared via `some` declarations (and *not* `some .. in`)\nin the scope of the given location\n" + } + ], + "head": { + "assign": true, + "value": { + "location": "321:50:324:2", + "type": "setcomprehension", + "value": { + "term": { + "location": "321:51:321:65", + "type": "ref", + "value": [ + { + "location": "321:51:321:59", + "type": "var", + "value": "some_var" + }, + { + "location": "321:60:321:65", + "type": "string", + "value": "value" + } + ] + }, + "body": [ + { + "location": "322:2:322:56", + "terms": { + "symbols": [ + { + "location": "322:7:322:56", + "type": "call", + "value": [ + { + "value": [ + { + "location": "322:16:322:18", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_2", + "location": "322:16:322:18" + } + ], + "location": "322:16:322:18", + "type": "ref" + }, + { + "location": "322:7:322:15", + "type": "var", + "value": "some_var" + }, + { + "location": "322:19:322:56", + "type": "ref", + "value": [ + { + "location": "322:19:322:24", + "type": "var", + "value": "found" + }, + { + "location": "322:25:322:29", + "type": "string", + "value": "vars" + }, + { + "location": "322:30:322:48", + "type": "call", + "value": [ + { + "value": [ + { + "location": "322:30:322:41", + "type": "var", + "value": "_rule_index" + } + ], + "location": "322:30:322:41", + "type": "ref" + }, + { + "location": "322:42:322:46", + "type": "var", + "value": "rule" + } + ] + }, + { + "location": "322:49:322:55", + "type": "string", + "value": "some" + } + ] + } + ] + } + ], + "location": "322:2:322:6" + } + }, + { + "location": "323:2:323:44", + "terms": [ + { + "location": "323:2:323:18", + "type": "ref", + "value": [ + { + "location": "323:2:323:18", + "type": "var", + "value": "_before_location" + } + ] + }, + { + "location": "323:19:323:23", + "type": "var", + "value": "rule" + }, + { + "location": "323:25:323:33", + "type": "var", + "value": "some_var" + }, + { + "location": "323:35:323:43", + "type": "var", + "value": "location" + } + ] + } + ] + } + }, + "location": "321:1:324:2", + "ref": [ + { + "location": "321:1:321:30", + "type": "var", + "value": "find_some_decl_names_in_scope" + } + ], + "args": [ + { + "location": "321:31:321:35", + "type": "var", + "value": "rule" + }, + { + "location": "321:37:321:45", + "type": "var", + "value": "location" + } + ] + } + }, + { + "body": [ + { + "location": "329:2:329:38", + "terms": { + "location": "329:2:329:6", + "symbols": [ + { + "location": "329:7:329:38", + "type": "call", + "value": [ + { + "location": "329:24:329:26", + "type": "ref", + "value": [ + { + "location": "329:24:329:26", + "type": "var", + "value": "internal" + }, + { + "location": "329:24:329:26", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "329:7:329:17", + "type": "var", + "value": "rule_index" + }, + { + "location": "329:19:329:23", + "type": "var", + "value": "rule" + }, + { + "location": "329:27:329:38", + "type": "ref", + "value": [ + { + "location": "329:27:329:32", + "type": "var", + "value": "input" + }, + { + "location": "329:33:329:38", + "type": "string", + "value": "rules" + } + ] + } + ] + } + ] + } + }, + { + "location": "330:2:330:36", + "terms": { + "symbols": [ + { + "location": "330:7:330:36", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "330:24:330:26", + "type": "var", + "value": "internal" + }, + { + "location": "330:24:330:26", + "type": "string", + "value": "member_3" + } + ], + "location": "330:24:330:26" + }, + { + "location": "330:7:330:17", + "type": "var", + "value": "expr_index" + }, + { + "value": "expr", + "location": "330:19:330:23", + "type": "var" + }, + { + "location": "330:27:330:36", + "type": "ref", + "value": [ + { + "type": "var", + "value": "rule", + "location": "330:27:330:31" + }, + { + "location": "330:32:330:36", + "type": "string", + "value": "body" + } + ] + } + ] + } + ], + "location": "330:2:330:6" + } + } + ], + "location": "328:1:331:2", + "annotations": [ + { + "location": "326:1:327:41", + "scope": "rule", + "description": "all expressions in module" + } + ], + "head": { + "location": "328:1:328:38", + "ref": [ + { + "location": "328:1:328:6", + "type": "var", + "value": "exprs" + }, + { + "location": "328:7:328:17", + "type": "var", + "value": "rule_index" + }, + { + "location": "328:19:328:29", + "type": "var", + "value": "expr_index" + } + ], + "assign": true, + "value": { + "location": "328:34:328:38", + "type": "var", + "value": "expr" + } + } + } + ], + "comments": [ + { + "text": "IHNpbXBsZSBhc3NpZ25tZW50LCBpLmUuIGB4IDo9IDEwMGAgcmV0dXJucyBgeGA=", + "location": "13:1:13:49" + }, + { + "location": "14:1:14:49", + "text": "IGFsd2F5cyByZXR1cm5zIGEgc2luZ2xlIHZhciwgYnV0IHdyYXBwZWQgaW4gYW4=" + }, + { + "location": "15:1:15:24", + "text": "IGFycmF5IGZvciBjb25zaXN0ZW5jeQ==" + }, + { + "location": "21:1:21:41", + "text": "ICdkZXN0cnVjdHVyaW5nJyBhcnJheSBhc3NpZ25tZW50LCBpLmUu" + }, + { + "location": "22:1:22:25", + "text": "IFthLCBiLCBjXSA6PSBbMSwgMiwgM10=" + }, + { + "location": "23:1:23:5", + "text": "IG9y" + }, + { + "location": "24:1:24:27", + "text": "IHthOiBifSA6PSB7ImZvbyI6ICJiYXIifQ==" + }, + { + "location": "30:1:30:56", + "text": "IHZhciBkZWNsYXJlZCB2aWEgYHNvbWVgLCBpLmUuIGBzb21lIHhgIG9yIGBzb21lIHgsIHlg" + }, + { + "location": "36:1:36:56", + "text": "IHNpbmdsZSB2YXIgZGVjbGFyZWQgdmlhIGBzb21lIGluYCwgaS5lLiBgc29tZSB4IGluIHlg" + }, + { + "text": "IHR3byB2YXJzIGRlY2xhcmVkIHZpYSBgc29tZSBpbmAsIGkuZS4gYHNvbWUgeCwgeSBpbiB6YA==", + "location": "44:1:44:57" + }, + { + "location": "55:1:58:85", + "text": "IE1FVEFEQVRB" + }, + { + "location": "56:1:56:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "57:1:57:58", + "text": "ICAgZmluZCB2YXJzIGxpa2UgaW5wdXRbeF0uZm9vW3ldIHdoZXJlIHggYW5kIHkgYXJlIHZhcnM=" + }, + { + "location": "58:1:58:85", + "text": "ICAgbm90ZTogdmFsdWUudHlwZSA9PSAicmVmIiBjaGVjayBtdXN0IGhhdmUgYmVlbiBkb25lIGJlZm9yZSBjYWxsaW5nIHRoaXMgZnVuY3Rpb24=" + }, + { + "location": "66:1:66:63", + "text": "IG9uZSBvciB0d28gdmFycyBkZWNsYXJlZCB2aWEgYGV2ZXJ5YCwgaS5lLiBgZXZlcnkgeCBpbiB5IHt9YA==" + }, + { + "location": "67:1:67:40", + "text": "IG9yIGBldmVyeWAsIGkuZS4gYGV2ZXJ5IHgsIHkgaW4geSB7fWA=" + }, + { + "location": "81:1:84:64", + "text": "IE1FVEFEQVRB" + }, + { + "location": "82:1:82:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "83:1:83:84", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyBpbiBwcm92aWRlZCB0ZXJtcyAodXNpbmcgYHdhbGtgKSwgYW5kIHJldHVybnMgYW4gYXJyYXkgd2l0aA==" + }, + { + "text": "ICAgYWxsIHZhcmlhYmxlcyBkZWNsYXJlZCBpbiB0ZXJtcywgaSxlIFt4LCB5XSBvciB7eDogeX0sIGV0Yy4=", + "location": "84:1:84:64" + }, + { + "location": "91:1:94:70", + "text": "IE1FVEFEQVRB" + }, + { + "location": "92:1:92:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "93:1:93:91", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyBpbiBwcm92aWRlZCB0ZXJtcyAodXNpbmcgYHdhbGtgKSwgYW5kIHJldHVybnMgdHJ1ZSBpZiBhbnkgdmFyaWFibGU=" + }, + { + "location": "94:1:94:70", + "text": "ICAgaXMgZm91bmQgaW4gdGVybXMsIHdpdGggZWFybHkgZXhpdCAoYXMgb3Bwb3NlZCB0byBmaW5kX3Rlcm1fdmFycyk=" + }, + { + "location": "110:32:110:65", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "121:1:121:77", + "text": "IGA9YCBpc24ndCBuZWNlc3NhcmlseSBhc3NpZ25tZW50LCBhbmQgb25seSBjb25zaWRlcmluZyB0aGUgdmFyaWFibGUgb24gdGhl" + }, + { + "location": "122:1:122:77", + "text": "IGxlZnQtaGFuZCBzaWRlIGlzIGVxdWFsbHkgZHViaW91cywgYnV0IHdlJ2xsIHRyZWF0IGB4ID0gMWAgYXMgYHggOj0gMWAgZm9y" + }, + { + "location": "123:1:123:79", + "text": "IHRoZSBwdXJwb3NlIG9mIHRoaXMgZnVuY3Rpb24gdW50aWwgd2UgaGF2ZSBhIG1vcmUgcm9idXN0IHdheSBvZiBkZWFsaW5nIHdpdGg=" + }, + { + "text": "IHVuaWZpY2F0aW9u", + "location": "124:1:124:14" + }, + { + "location": "161:22:161:55", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "165:1:169:44", + "text": "IE1FVEFEQVRB" + }, + { + "location": "166:1:166:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "167:1:167:86", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyB1bmRlciBwcm92aWRlZCBub2RlICh1c2luZyBgd2Fsa2ApLCBhbmQgcmV0dXJucyBhbiBhcnJheSB3aXRo" + }, + { + "location": "168:1:168:86", + "text": "ICAgYWxsIHZhcmlhYmxlcyBkZWNsYXJlZCB2aWEgYXNzaWdubWVudCAoOj0pLCBgc29tZWAsIGBldmVyeWAgYW5kIGluIGNvbXByZWhlbnNpb25z" + }, + { + "location": "169:1:169:44", + "text": "ICAgREVQUkVDQVRFRDogdXNlcyBhc3QuZm91bmQudmFycyBpbnN0ZWFk" + }, + { + "location": "176:1:176:85", + "text": "IGhhY2sgdG8gd29yayBhcm91bmQgdGhlIGRpZmZlcmVudCBpbnB1dCBtb2RlbHMgb2YgbGludGluZyB2cy4gdGhlIGxzcCBwYWNrYWdlLi4gd2U=" + }, + { + "location": "177:1:177:49", + "text": "IHNob3VsZCBwcm9iYWJseSBjb25zaWRlciBzb21ldGhpbmcgbW9yZSByb2J1c3Q=" + }, + { + "location": "182:1:192:10", + "text": "IE1FVEFEQVRBOg==" + }, + { + "text": "IGRlc2NyaXB0aW9uOiB8", + "location": "183:1:183:17" + }, + { + "location": "184:1:184:88", + "text": "ICAgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIHZhcmlhYmxlcyBmb3VuZCBpbiB0aGUgaW5wdXQgQVNULCBrZXllZCBmaXJzdCBieSB0aGUgaW5kZXggb2Y=" + }, + { + "location": "185:1:185:88", + "text": "ICAgdGhlIHJ1bGUgd2hlcmUgdGhlIHZhcmlhYmxlcyB3ZXJlIGZvdW5kIChhcyBhIG51bWVyaWMgc3RyaW5nKSwgYW5kIHRoZW4gdGhlIGNvbnRleHQ=" + }, + { + "location": "186:1:186:43", + "text": "ICAgb2YgdGhlIHZhcmlhYmxlLCB3aGljaCB3aWxsIGJlIG9uZSBvZjo=" + }, + { + "location": "187:1:187:11", + "text": "ICAgLSB0ZXJt" + }, + { + "text": "ICAgLSBhc3NpZ24=", + "location": "188:1:188:13" + }, + { + "location": "189:1:189:12", + "text": "ICAgLSBldmVyeQ==" + }, + { + "location": "190:1:190:11", + "text": "ICAgLSBzb21l" + }, + { + "location": "191:1:191:13", + "text": "ICAgLSBzb21laW4=" + }, + { + "location": "192:1:192:10", + "text": "ICAgLSByZWY=" + }, + { + "location": "196:2:196:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "205:1:206:41", + "text": "IE1FVEFEQVRB" + }, + { + "location": "206:1:206:41", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgcmVmcyBmb3VuZGQgaW4gbW9kdWxl" + }, + { + "location": "210:2:210:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "218:1:219:44", + "text": "IE1FVEFEQVRB" + }, + { + "location": "219:1:219:44", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgc3ltYm9scyBmb3VuZGQgaW4gbW9kdWxl" + }, + { + "location": "223:2:223:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "229:1:230:50", + "text": "IE1FVEFEQVRB" + }, + { + "location": "230:1:230:50", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgY29tcHJlaGVuc2lvbnMgZm91bmQgaW4gbW9kdWxl" + }, + { + "location": "234:2:234:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "242:1:247:28", + "text": "IE1FVEFEQVRB" + }, + { + "location": "243:1:243:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "244:1:244:71", + "text": "ICAgZmluZHMgYWxsIHZhcnMgZGVjbGFyZWQgaW4gYHJ1bGVgICpiZWZvcmUqIHRoZSBgbG9jYXRpb25gIHByb3ZpZGVk" + }, + { + "location": "245:1:245:72", + "text": "ICAgbm90ZTogdGhpcyBpc24ndCAxMDAlIGFjY3VyYXRlLCBhcyBpdCBkb2Vzbid0IHRha2UgaW50byBhY2NvdW50IGA9YA==" + }, + { + "location": "246:1:246:77", + "text": "ICAgYXNzaWdubWVudHMgLyB1bmlmaWNhdGlvbiwgYnV0IGl0J3MgbGlrZWx5IGdvb2QgZW5vdWdoIHNpbmNlIG90aGVyIHJ1bGVz" + }, + { + "location": "247:1:247:28", + "text": "ICAgcmVjb21tZW5kIGFnYWluc3QgdGhvc2U=" + }, + { + "location": "249:45:249:78", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "264:1:264:62", + "text": "IHNwZWNpYWwgY2FzZSDigJQgdGhlIHZhbHVlIGxvY2F0aW9uIG9mIHRoZSBydWxlIGhlYWQgInNlZXMi" + }, + { + "location": "265:1:265:48", + "text": "IGFsbCBsb2NhbCB2YXJpYWJsZXMgZGVjbGFyZWQgaW4gdGhlIHJ1bGUgYm9keQ==" + }, + { + "location": "292:1:293:77", + "text": "IE1FVEFEQVRB" + }, + { + "location": "293:1:293:77", + "text": "IGRlc2NyaXB0aW9uOiBmaW5kICpvbmx5KiBuYW1lcyBpbiB0aGUgbG9jYWwgc2NvcGUsIGFuZCBub3QgZS5nLiBydWxlIG5hbWVz" + }, + { + "location": "306:1:309:82", + "text": "IE1FVEFEQVRB" + }, + { + "location": "307:1:307:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "308:1:308:83", + "text": "ICAgc2ltaWxhciB0byBgZmluZF92YXJzX2luX2xvY2FsX3Njb3BlYCwgYnV0IHJldHVybnMgYWxsIHZhcmlhYmxlIG5hbWVzIGluIHNjb3Bl" + }, + { + "location": "309:1:309:82", + "text": "ICAgb2YgdGhlIGdpdmVuIGxvY2F0aW9uICphbmQqIHRoZSBydWxlIG5hbWVzIHByZXNlbnQgaW4gdGhlIHNjb3BlIChpLmUuIG1vZHVsZSk=" + }, + { + "location": "313:2:313:36", + "text": "IHBhcmVucyBiZWxvdyBhZGRlZCBieSBvcGEtZm10IDop" + }, + { + "location": "317:1:320:39", + "text": "IE1FVEFEQVRB" + }, + { + "location": "318:1:318:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "319:1:319:81", + "text": "ICAgZmluZCBhbGwgdmFyaWFibGVzIGRlY2xhcmVkIHZpYSBgc29tZWAgZGVjbGFyYXRpb25zIChhbmQgKm5vdCogYHNvbWUgLi4gaW5gKQ==" + }, + { + "location": "320:1:320:39", + "text": "ICAgaW4gdGhlIHNjb3BlIG9mIHRoZSBnaXZlbiBsb2NhdGlvbg==" + }, + { + "location": "322:57:322:90", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "326:1:327:41", + "text": "IE1FVEFEQVRB" + }, + { + "location": "327:1:327:41", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgZXhwcmVzc2lvbnMgaW4gbW9kdWxl" + } + ], + "regal": { + "file": { + "name": "bundle/regal/ast/search.rego", + "lines": [ + "package regal.ast", + "", + "import rego.v1", + "", + "import data.regal.util", + "", + "_find_nested_vars(obj) := [value |", + "\twalk(obj, [_, value])", + "\tvalue.type == \"var\"", + "\tindexof(value.value, \"$\") == -1", + "]", + "", + "# simple assignment, i.e. `x := 100` returns `x`", + "# always returns a single var, but wrapped in an", + "# array for consistency", + "_find_assign_vars(value) := var if {", + "\tvalue[1].type == \"var\"", + "\tvar := [value[1]]", + "}", + "", + "# 'destructuring' array assignment, i.e.", + "# [a, b, c] := [1, 2, 3]", + "# or", + "# {a: b} := {\"foo\": \"bar\"}", + "_find_assign_vars(value) := vars if {", + "\tvalue[1].type in {\"array\", \"object\"}", + "\tvars := _find_nested_vars(value[1])", + "}", + "", + "# var declared via `some`, i.e. `some x` or `some x, y`", + "_find_some_decl_vars(value) := [v |", + "\tsome v in value", + "\tv.type == \"var\"", + "]", + "", + "# single var declared via `some in`, i.e. `some x in y`", + "_find_some_in_decl_vars(value) := vars if {", + "\tarr := value[0].value", + "\tcount(arr) == 3", + "", + "\tvars := _find_nested_vars(arr[1])", + "}", + "", + "# two vars declared via `some in`, i.e. `some x, y in z`", + "_find_some_in_decl_vars(value) := vars if {", + "\tarr := value[0].value", + "\tcount(arr) == 4", + "", + "\tvars := [v |", + "\t\tsome i in [1, 2]", + "\t\tsome v in _find_nested_vars(arr[i])", + "\t]", + "}", + "", + "# METADATA", + "# description: |", + "# find vars like input[x].foo[y] where x and y are vars", + "# note: value.type == \"ref\" check must have been done before calling this function", + "find_ref_vars(value) := [var |", + "\tsome i, var in value.value", + "", + "\ti > 0", + "\tvar.type == \"var\"", + "]", + "", + "# one or two vars declared via `every`, i.e. `every x in y {}`", + "# or `every`, i.e. `every x, y in y {}`", + "_find_every_vars(value) := vars if {", + "\tkey_var := [value.key |", + "\t\tvalue.key.type == \"var\"", + "\t\tindexof(value.key.value, \"$\") == -1", + "\t]", + "\tval_var := [value.value |", + "\t\tvalue.value.type == \"var\"", + "\t\tindexof(value.value.value, \"$\") == -1", + "\t]", + "", + "\tvars := array.concat(key_var, val_var)", + "}", + "", + "# METADATA", + "# description: |", + "# traverses all nodes in provided terms (using `walk`), and returns an array with", + "# all variables declared in terms, i,e [x, y] or {x: y}, etc.", + "find_term_vars(terms) := [term |", + "\twalk(terms, [_, term])", + "", + "\tterm.type == \"var\"", + "]", + "", + "# METADATA", + "# description: |", + "# traverses all nodes in provided terms (using `walk`), and returns true if any variable", + "# is found in terms, with early exit (as opposed to find_term_vars)", + "has_term_var(terms) if {", + "\twalk(terms, [_, term])", + "", + "\tterm.type == \"var\"", + "}", + "", + "_find_vars(value, last) := {\"term\": find_term_vars(function_ret_args(fn_name, value))} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value != \"assign\"", + "", + "\tfn_name := ref_to_string(value[0].value)", + "", + "\tnot contains(fn_name, \"$\")", + "\tfn_name in all_function_names # regal ignore:external-reference", + "\tfunction_ret_in_args(fn_name, value)", + "}", + "", + "_find_vars(value, last) := {\"assign\": _find_assign_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value == \"assign\"", + "}", + "", + "# `=` isn't necessarily assignment, and only considering the variable on the", + "# left-hand side is equally dubious, but we'll treat `x = 1` as `x := 1` for", + "# the purpose of this function until we have a more robust way of dealing with", + "# unification", + "_find_vars(value, last) := {\"assign\": _find_assign_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value == \"eq\"", + "}", + "", + "_find_vars(value, _) := {\"ref\": find_ref_vars(value)} if value.type == \"ref\"", + "", + "_find_vars(value, last) := {\"somein\": _find_some_in_decl_vars(value)} if {", + "\tlast == \"symbols\"", + "\tvalue[0].type == \"call\"", + "}", + "", + "_find_vars(value, last) := {\"some\": _find_some_decl_vars(value)} if {", + "\tlast == \"symbols\"", + "\tvalue[0].type != \"call\"", + "}", + "", + "_find_vars(value, last) := {\"every\": _find_every_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue.domain", + "}", + "", + "_find_vars(value, last) := {\"args\": arg_vars} if {", + "\tlast == \"args\"", + "", + "\targ_vars := [arg |", + "\t\tsome arg in value", + "\t\targ.type == \"var\"", + "\t]", + "", + "\tcount(arg_vars) > 0", + "}", + "", + "_rule_index(rule) := sprintf(\"%d\", [i]) if {", + "\tsome i, r in _rules # regal ignore:external-reference", + "\tr == rule", + "}", + "", + "# METADATA", + "# description: |", + "# traverses all nodes under provided node (using `walk`), and returns an array with", + "# all variables declared via assignment (:=), `some`, `every` and in comprehensions", + "# DEPRECATED: uses ast.found.vars instead", + "find_vars(node) := [var |", + "\twalk(node, [path, value])", + "", + "\tvar := _find_vars(value, regal.last(path))[_][_]", + "]", + "", + "# hack to work around the different input models of linting vs. the lsp package.. we", + "# should probably consider something more robust", + "_rules := input.rules", + "", + "_rules := data.workspace.parsed[input.regal.file.uri].rules if not input.rules", + "", + "# METADATA:", + "# description: |", + "# object containing all variables found in the input AST, keyed first by the index of", + "# the rule where the variables were found (as a numeric string), and then the context", + "# of the variable, which will be one of:", + "# - term", + "# - assign", + "# - every", + "# - some", + "# - somein", + "# - ref", + "found.vars[rule_index][context] contains var if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [path, value])", + "", + "\tsome context, vars in _find_vars(value, regal.last(path))", + "\tsome var in vars", + "}", + "", + "# METADATA", + "# description: all refs foundd in module", + "found.refs[rule_index] contains value if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "", + "\tis_ref(value)", + "}", + "", + "# METADATA", + "# description: all symbols foundd in module", + "found.symbols[rule_index] contains value.symbols if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "}", + "", + "# METADATA", + "# description: all comprehensions found in module", + "found.comprehensions[rule_index] contains value if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "", + "\tvalue.type in {\"arraycomprehension\", \"objectcomprehension\", \"setcomprehension\"}", + "}", + "", + "# METADATA", + "# description: |", + "# finds all vars declared in `rule` *before* the `location` provided", + "# note: this isn't 100% accurate, as it doesn't take into account `=`", + "# assignments / unification, but it's likely good enough since other rules", + "# recommend against those", + "find_vars_in_local_scope(rule, location) := [var |", + "\tvar := found.vars[_rule_index(rule)][_][_] # regal ignore:external-reference", + "", + "\tnot is_wildcard(var)", + "\t_before_location(rule, var, util.to_location_object(location))", + "]", + "", + "_end_location(location) := end if {", + "\tloc := util.to_location_object(location)", + "\tlines := split(loc.text, \"\\n\")", + "\tend := {", + "\t\t\"row\": (loc.row + count(lines)) - 1,", + "\t\t\"col\": loc.col + count(regal.last(lines)),", + "\t}", + "}", + "", + "# special case — the value location of the rule head \"sees\"", + "# all local variables declared in the rule body", + "_before_location(rule, _, location) if {", + "\tloc := util.to_location_object(location)", + "", + "\tvalue_start := util.to_location_object(rule.head.value.location)", + "", + "\tloc.row >= value_start.row", + "\tloc.col >= value_start.col", + "", + "\tvalue_end := _end_location(util.to_location_object(rule.head.value.location))", + "", + "\tloc.row <= value_end.row", + "\tloc.col <= value_end.col", + "}", + "", + "_before_location(_, var, location) if {", + "\tutil.to_location_object(var.location).row < util.to_location_object(location).row", + "}", + "", + "_before_location(_, var, location) if {", + "\tvar_loc := util.to_location_object(var.location)", + "\tloc := util.to_location_object(location)", + "", + "\tvar_loc.row == loc.row", + "\tvar_loc.col < loc.col", + "}", + "", + "# METADATA", + "# description: find *only* names in the local scope, and not e.g. rule names", + "find_names_in_local_scope(rule, location) := names if {", + "\tfn_arg_names := _function_arg_names(rule)", + "\tvar_names := {var.value | some var in find_vars_in_local_scope(rule, util.to_location_object(location))}", + "", + "\tnames := fn_arg_names | var_names", + "}", + "", + "_function_arg_names(rule) := {arg.value |", + "\tsome arg in rule.head.args", + "\targ.type == \"var\"", + "}", + "", + "# METADATA", + "# description: |", + "# similar to `find_vars_in_local_scope`, but returns all variable names in scope", + "# of the given location *and* the rule names present in the scope (i.e. module)", + "find_names_in_scope(rule, location) := names if {", + "\tlocals := find_names_in_local_scope(rule, util.to_location_object(location))", + "", + "\t# parens below added by opa-fmt :)", + "\tnames := (rule_names | imported_identifiers) | locals", + "}", + "", + "# METADATA", + "# description: |", + "# find all variables declared via `some` declarations (and *not* `some .. in`)", + "# in the scope of the given location", + "find_some_decl_names_in_scope(rule, location) := {some_var.value |", + "\tsome some_var in found.vars[_rule_index(rule)][\"some\"] # regal ignore:external-reference", + "\t_before_location(rule, some_var, location)", + "}", + "", + "# METADATA", + "# description: all expressions in module", + "exprs[rule_index][expr_index] := expr if {", + "\tsome rule_index, rule in input.rules", + "\tsome expr_index, expr in rule.body", + "}", + "" + ], + "abs": "/Users/anderseknert/git/styra/regal/bundle/regal/ast/search.rego" + }, + "environment": { + "path_separator": "/" + } + } +} diff --git a/v1/topdown/bindings.go b/v1/topdown/bindings.go index 139efe8d4e..ae6ca15daa 100644 --- a/v1/topdown/bindings.go +++ b/v1/topdown/bindings.go @@ -68,7 +68,7 @@ func (u *bindings) Plug(a *ast.Term) *ast.Term { } func (u *bindings) PlugNamespaced(a *ast.Term, caller *bindings) *ast.Term { - if u != nil { + if u != nil && u.instr != nil { u.instr.startTimer(evalOpPlug) t := u.plugNamespaced(a, caller) u.instr.stopTimer(evalOpPlug) diff --git a/v1/topdown/eval.go b/v1/topdown/eval.go index dda64bac46..9f88a7b156 100644 --- a/v1/topdown/eval.go +++ b/v1/topdown/eval.go @@ -8,6 +8,7 @@ import ( "sort" "strconv" "strings" + "sync" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/metrics" @@ -57,58 +58,82 @@ func (ee deferredEarlyExitError) Error() string { return fmt.Sprintf("%v: deferred early exit", ee.e.query) } +// Note(æ): this struct is formatted for optimal alignment as it is big, internal and instantiated +// *very* frequently during evaluation. If you need to add fields here, please consider the alignment +// of the struct, and use something like betteralign (https://github.com/dkorunic/betteralign) if you +// need help with that. type eval struct { ctx context.Context metrics metrics.Metrics seed io.Reader + cancel Cancel + queryCompiler ast.QueryCompiler + store storage.Store + txn storage.Transaction + virtualCache VirtualCache + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + printHook print.Hook time *ast.Term - queryID uint64 queryIDFact *queryIDFactory parent *eval caller *eval - cancel Cancel - query ast.Body - queryCompiler ast.QueryCompiler - index int - indexing bool - earlyExit bool bindings *bindings - store storage.Store baseCache *baseCache - txn storage.Transaction compiler *ast.Compiler input *ast.Term data *ast.Term external *resolverTrie targetStack *refStack - tracers []QueryTracer - traceEnabled bool traceLastLocation *ast.Location // Last location of a trace event. - plugTraceVars bool instr *Instrumentation builtins map[string]*Builtin builtinCache builtins.Cache ndBuiltinCache builtins.NDBCache functionMocks *functionMocksStack - virtualCache VirtualCache comprehensionCache *comprehensionCache - interQueryBuiltinCache cache.InterQueryCache - interQueryBuiltinValueCache cache.InterQueryValueCache saveSet *saveSet saveStack *saveStack saveSupport *saveSupport saveNamespace *ast.Term - skipSaveNamespace bool inliningControl *inliningControl - genvarprefix string - genvarid int runtime *ast.Term builtinErrors *builtinErrors - printHook print.Hook + roundTripper CustomizeRoundTripper + genvarprefix string + query ast.Body + tracers []QueryTracer tracingOpts tracing.Options + queryID uint64 + index int + genvarid int + indexing bool + earlyExit bool + traceEnabled bool + plugTraceVars bool + skipSaveNamespace bool findOne bool strictObjects bool - roundTripper CustomizeRoundTripper +} + +type evp struct { + pool sync.Pool +} + +func (ep *evp) Put(e *eval) { + ep.pool.Put(e) +} + +func (ep *evp) Get() *eval { + return ep.pool.Get().(*eval) +} + +var evalPool = evp{ + pool: sync.Pool{ + New: func() any { + return &eval{} + }, + }, } func (e *eval) Run(iter evalIterator) error { @@ -157,25 +182,23 @@ func (e *eval) builtinFunc(name string) (*ast.Builtin, BuiltinFunc, bool) { return nil, nil, false } -func (e *eval) closure(query ast.Body) *eval { - cpy := *e +func (e *eval) closure(query ast.Body, cpy *eval) { + *cpy = *e cpy.index = 0 cpy.query = query cpy.queryID = cpy.queryIDFact.Next() cpy.parent = e cpy.findOne = false - return &cpy } -func (e *eval) child(query ast.Body) *eval { - cpy := *e +func (e *eval) child(query ast.Body, cpy *eval) { + *cpy = *e cpy.index = 0 cpy.query = query cpy.queryID = cpy.queryIDFact.Next() cpy.bindings = newBindings(cpy.queryID, e.instr) cpy.parent = e cpy.findOne = false - return &cpy } func (e *eval) next(iter evalIterator) error { @@ -385,31 +408,97 @@ func (e *eval) evalExpr(iter evalIterator) error { } func (e *eval) evalStep(iter evalIterator) error { - expr := e.query[e.index] if expr.Negated { return e.evalNot(iter) } - var defined bool var err error + + // NOTE(æ): the reason why there's one branch for the tracing case and one almost + // identical branch below for when tracing is disabled is that the tracing case + // allocates wildly. These allocations are cause by the "defined" boolean variable + // escaping to the heap as its value is set from inside of closures. There may very + // well be more elegant solutions to this problem, but this is one that works, and + // saves several *million* allocations for some workloads. So feel free to refactor + // this, but do make sure that the common non-tracing case doesn't pay in allocations + // for something that is only needed when tracing is enabled. + if e.traceEnabled { + var defined bool + switch terms := expr.Terms.(type) { + case []*ast.Term: + switch { + case expr.IsEquality(): + err = e.unify(terms[1], terms[2], func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + default: + err = e.evalCall(terms, func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + } + case *ast.Term: + // generateVar inlined here to avoid extra allocations in hot path + rterm := ast.VarTerm(fmt.Sprintf("%s_term_%d_%d", e.genvarprefix, e.queryID, e.index)) + err = e.unify(terms, rterm, func() error { + if e.saveSet.Contains(rterm, e.bindings) { + return e.saveExpr(ast.NewExpr(rterm), e.bindings, func() error { + return iter(e) + }) + } + if !e.bindings.Plug(rterm).Equal(ast.InternedBooleanTerm(false)) { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + } + return nil + }) + case *ast.Every: + eval := evalEvery{ + Every: terms, + e: e, + expr: expr, + } + err = eval.eval(func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + + default: // guard-rail for adding extra (Expr).Terms types + return fmt.Errorf("got %T terms: %[1]v", terms) + } + + if err != nil { + return err + } + + if !defined { + e.traceFail(expr) + } + + return nil + } + switch terms := expr.Terms.(type) { case []*ast.Term: switch { case expr.IsEquality(): err = e.unify(terms[1], terms[2], func() error { - defined = true - err := iter(e) - e.traceRedo(expr) - return err + return iter(e) }) default: err = e.evalCall(terms, func() error { - defined = true - err := iter(e) - e.traceRedo(expr) - return err + return iter(e) }) } case *ast.Term: @@ -422,10 +511,7 @@ func (e *eval) evalStep(iter evalIterator) error { }) } if !e.bindings.Plug(rterm).Equal(ast.InternedBooleanTerm(false)) { - defined = true - err := iter(e) - e.traceRedo(expr) - return err + return iter(e) } return nil }) @@ -436,25 +522,14 @@ func (e *eval) evalStep(iter evalIterator) error { expr: expr, } err = eval.eval(func() error { - defined = true - err := iter(e) - e.traceRedo(expr) - return err + return iter(e) }) default: // guard-rail for adding extra (Expr).Terms types return fmt.Errorf("got %T terms: %[1]v", terms) } - if err != nil { - return err - } - - if !defined { - e.traceFail(expr) - } - - return nil + return err } func (e *eval) evalNot(iter evalIterator) error { @@ -465,15 +540,19 @@ func (e *eval) evalNot(iter evalIterator) error { return e.evalNotPartial(iter) } - negation := ast.NewBody(expr.Complement().NoWith()) - child := e.closure(negation) + negation := ast.NewBody(expr.ComplementNoWith()) + child := evalPool.Get() + + e.closure(negation, child) + + defer evalPool.Put(child) var defined bool if e.traceEnabled { child.traceEnter(negation) } - err := child.eval(func(*eval) error { + if err := child.eval(func(*eval) error { if e.traceEnabled { child.traceExit(negation) child.traceRedo(negation) @@ -481,9 +560,7 @@ func (e *eval) evalNot(iter evalIterator) error { defined = true return nil - }) - - if err != nil { + }); err != nil { return err } @@ -630,11 +707,14 @@ func (e *eval) evalWithPop(input, data *ast.Term) { } func (e *eval) evalNotPartial(iter evalIterator) error { - // Prepare query normally. expr := e.query[e.index] - negation := expr.Complement().NoWith() - child := e.closure(ast.NewBody(negation)) + negation := expr.ComplementNoWith() + + child := evalPool.Get() + defer evalPool.Put(child) + + e.closure(ast.NewBody(negation), child) // Unknowns is the set of variables that are marked as unknown. The variables // are namespaced with the query ID that they originate in. This ensures that @@ -873,7 +953,7 @@ func (e *eval) evalCallValue(arity int, terms []*ast.Term, mock *ast.Term, iter return e.unify(terms[len(terms)-1], mock, iter) case len(terms) == arity+1: - if mock.Value.Compare(ast.Boolean(false)) != 0 { + if !ast.Boolean(false).Equal(mock.Value) { return iter() } return nil @@ -1199,7 +1279,10 @@ func (e *eval) buildComprehensionCache(a *ast.Term) (*ast.Term, error) { } func (e *eval) buildComprehensionCacheArray(x *ast.ArrayComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { - child := e.child(x.Body) + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { values := make([]*ast.Term, len(keys)) @@ -1218,7 +1301,10 @@ func (e *eval) buildComprehensionCacheArray(x *ast.ArrayComprehension, keys []*a } func (e *eval) buildComprehensionCacheSet(x *ast.SetComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { - child := e.child(x.Body) + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { values := make([]*ast.Term, len(keys)) @@ -1238,7 +1324,10 @@ func (e *eval) buildComprehensionCacheSet(x *ast.SetComprehension, keys []*ast.T } func (e *eval) buildComprehensionCacheObject(x *ast.ObjectComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { - child := e.child(x.Body) + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { values := make([]*ast.Term, len(keys)) @@ -1319,7 +1408,11 @@ func (e *eval) amendComprehension(a *ast.Term, b1 *bindings) (*ast.Term, error) func (e *eval) biunifyComprehensionArray(x *ast.ArrayComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { result := ast.NewArray() - child := e.closure(x.Body) + child := evalPool.Get() + + e.closure(x.Body, child) + defer evalPool.Put(child) + err := child.Run(func(child *eval) error { result = result.Append(child.bindings.Plug(x.Term)) return nil @@ -1332,7 +1425,11 @@ func (e *eval) biunifyComprehensionArray(x *ast.ArrayComprehension, b *ast.Term, func (e *eval) biunifyComprehensionSet(x *ast.SetComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { result := ast.NewSet() - child := e.closure(x.Body) + child := evalPool.Get() + + e.closure(x.Body, child) + defer evalPool.Put(child) + err := child.Run(func(child *eval) error { result.Add(child.bindings.Plug(x.Term)) return nil @@ -1344,8 +1441,13 @@ func (e *eval) biunifyComprehensionSet(x *ast.SetComprehension, b *ast.Term, b1, } func (e *eval) biunifyComprehensionObject(x *ast.ObjectComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + child := evalPool.Get() + defer evalPool.Put(child) + + e.closure(x.Body, child) + result := ast.NewObject() - child := e.closure(x.Body) + err := child.Run(func(child *eval) error { key := child.bindings.Plug(x.Key) value := child.bindings.Plug(x.Value) @@ -1482,12 +1584,22 @@ func (e *eval) getRules(ref ast.Ref, args []*ast.Term) (*ast.IndexResult, error) return nil, nil } + resolver := resolverPool.Get().(*evalResolver) + defer func() { + resolver.e = nil + resolver.args = nil + resolverPool.Put(resolver) + }() + var result *ast.IndexResult var err error if e.indexing { - result, err = index.Lookup(&evalResolver{e: e, args: args}) + resolver.e = e + resolver.args = args + result, err = index.Lookup(resolver) } else { - result, err = index.AllRules(&evalResolver{e: e}) + resolver.e = e + result, err = index.AllRules(resolver) } if err != nil { return nil, err @@ -1524,6 +1636,14 @@ type evalResolver struct { args []*ast.Term } +var ( + resolverPool = sync.Pool{ + New: func() any { + return &evalResolver{} + }, + } +) + func (e *evalResolver) Resolve(ref ast.Ref) (ast.Value, error) { e.e.instr.startTimer(evalOpResolve) @@ -1786,7 +1906,7 @@ func (e evalBuiltin) eval(iter unifyIterator) error { case e.bi.Decl.Result() == nil: return iter() case len(operands) == numDeclArgs: - if v.Compare(ast.Boolean(false)) == 0 { + if ast.Boolean(false).Equal(v) { return nil // nothing to do } return iter() @@ -1810,7 +1930,7 @@ func (e evalBuiltin) eval(iter unifyIterator) error { case e.bi.Decl.Result() == nil: err = iter() case len(operands) == numDeclArgs: - if output.Value.Compare(ast.Boolean(false)) != 0 { + if !ast.Boolean(false).Equal(output.Value) { err = iter() } // else: nothing to do, don't iter() default: @@ -1850,9 +1970,9 @@ func (e evalBuiltin) eval(iter unifyIterator) error { type evalFunc struct { e *eval + ir *ast.IndexResult ref ast.Ref terms []*ast.Term - ir *ast.IndexResult } func (e evalFunc) eval(iter unifyIterator) error { @@ -1891,9 +2011,9 @@ func (e evalFunc) eval(iter unifyIterator) error { func (e evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) error { var cacheKey ast.Ref - var hit bool - var err error if !e.e.partial() { + var hit bool + var err error cacheKey, hit, err = e.evalCache(argCount, iter) if err != nil { return err @@ -1981,8 +2101,10 @@ func (e evalFunc) evalCache(argCount int, iter unifyIterator) (ast.Ref, bool, er } func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, cacheKey ast.Ref, prev *ast.Term, findOne bool) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.findOne = findOne args := make([]*ast.Term, len(e.terms)-1) @@ -2014,8 +2136,8 @@ func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, cacheKey ast.R } if len(rule.Head.Args) == len(e.terms)-1 { - if result.Value.Compare(ast.Boolean(false)) == 0 { - if prev != nil && ast.Compare(prev, result) != 0 { + if ast.Boolean(false).Equal(result.Value) { + if prev != nil && !prev.Equal(result) { return functionConflictErr(rule.Location) } prev = result @@ -2029,7 +2151,7 @@ func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, cacheKey ast.R // an example. if !e.e.partial() { if prev != nil { - if ast.Compare(prev, result) != 0 { + if !prev.Equal(result) { return functionConflictErr(rule.Location) } child.traceRedo(rule) @@ -2073,8 +2195,10 @@ func (e evalFunc) partialEvalSupport(declArgsLen int, iter unifyIterator) error } func (e evalFunc) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) error { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) e.e.saveStack.PushQuery(nil) @@ -2123,13 +2247,13 @@ func (e evalFunc) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) error { type evalTree struct { e *eval - ref ast.Ref - plugged ast.Ref - pos int bindings *bindings rterm *ast.Term rbindings *bindings node *ast.TreeNode + ref ast.Ref + plugged ast.Ref + pos int } func (e evalTree) eval(iter unifyIterator) error { @@ -2354,12 +2478,12 @@ func (e evalTree) leaves(plugged ast.Ref, node *ast.TreeNode) (ast.Object, error type evalVirtual struct { e *eval - ref ast.Ref - plugged ast.Ref - pos int bindings *bindings rterm *ast.Term rbindings *bindings + ref ast.Ref + plugged ast.Ref + pos int } func (e evalVirtual) eval(iter unifyIterator) error { @@ -2430,14 +2554,14 @@ func (e evalVirtual) eval(iter unifyIterator) error { type evalVirtualPartial struct { e *eval - ref ast.Ref - plugged ast.Ref - pos int ir *ast.IndexResult bindings *bindings rterm *ast.Term rbindings *bindings empty *ast.Term + ref ast.Ref + plugged ast.Ref + pos int } type evalVirtualPartialCacheHint struct { @@ -2573,8 +2697,11 @@ func (e evalVirtualPartial) evalAllRulesNoCache(rules []*ast.Rule) (*ast.Term, e var visitedRefs []ast.Ref + child := evalPool.Get() + defer evalPool.Put(child) + for _, rule := range rules { - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) err := child.eval(func(*eval) error { child.traceExit(rule) @@ -2607,8 +2734,10 @@ func wrapInObjects(leaf *ast.Term, ref ast.Ref) *ast.Term { } func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Rule, result *ast.Term, unknown bool, visitedRefs *[]ast.Ref) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) var defined bool @@ -2700,7 +2829,10 @@ func (e *eval) biunifyDynamicRef(pos int, a, b ast.Ref, b1, b2 *bindings, iter u } func (e evalVirtualPartial) evalOneRulePostUnify(iter unifyIterator, rule *ast.Rule) error { - child := e.e.child(rule.Body) + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) child.traceEnter(rule) var defined bool @@ -2784,8 +2916,10 @@ func (e evalVirtualPartial) partialEvalSupport(iter unifyIterator) error { } func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, _ ast.Ref) (bool, error) { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) e.e.saveStack.PushQuery(nil) @@ -3148,13 +3282,13 @@ func (e evalVirtualPartial) reduce(rule *ast.Rule, b *bindings, result *ast.Term type evalVirtualComplete struct { e *eval - ref ast.Ref - plugged ast.Ref - pos int ir *ast.IndexResult bindings *bindings rterm *ast.Term rbindings *bindings + ref ast.Ref + plugged ast.Ref + pos int } func (e evalVirtualComplete) eval(iter unifyIterator) error { @@ -3263,8 +3397,10 @@ func (e evalVirtualComplete) evalValue(iter unifyIterator, findOne bool) error { } func (e evalVirtualComplete) evalValueRule(iter unifyIterator, rule *ast.Rule, prev *ast.Term, findOne bool) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.findOne = findOne child.traceEnter(rule) var result *ast.Term @@ -3299,9 +3435,11 @@ func (e evalVirtualComplete) evalValueRule(iter unifyIterator, rule *ast.Rule, p } func (e evalVirtualComplete) partialEval(iter unifyIterator) error { + child := evalPool.Get() + defer evalPool.Put(child) for _, rule := range e.ir.Rules { - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) err := child.eval(func(child *eval) error { @@ -3364,8 +3502,10 @@ func (e evalVirtualComplete) partialEvalSupport(iter unifyIterator) error { } func (e evalVirtualComplete) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) (bool, error) { + child := evalPool.Get() + defer evalPool.Put(child) - child := e.e.child(rule.Body) + e.e.child(rule.Body, child) child.traceEnter(rule) e.e.saveStack.PushQuery(nil) @@ -3420,13 +3560,13 @@ func (e evalVirtualComplete) evalTerm(iter unifyIterator, term *ast.Term, termbi type evalTerm struct { e *eval - ref ast.Ref - pos int bindings *bindings term *ast.Term termbindings *bindings rterm *ast.Term rbindings *bindings + ref ast.Ref + pos int } func (e evalTerm) eval(iter unifyIterator) error { @@ -3479,31 +3619,27 @@ func (e evalTerm) enumerate(iter unifyIterator) error { case *ast.Array: for i := 0; i < v.Len(); i++ { k := ast.InternedIntNumberTerm(i) - err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + if err := handleErr(e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { return e.next(iter, k) - }) - - if err := handleErr(err); err != nil { + })); err != nil { return err } } case ast.Object: - if err := v.Iter(func(k, _ *ast.Term) error { - err := e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, func() error { + for _, k := range v.Keys() { + if err := handleErr(e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, func() error { return e.next(iter, e.termbindings.Plug(k)) - }) - return handleErr(err) - }); err != nil { - return err + })); err != nil { + return err + } } case ast.Set: - if err := v.Iter(func(elem *ast.Term) error { - err := e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, func() error { + for _, elem := range v.Slice() { + if err := handleErr(e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, func() error { return e.next(iter, e.termbindings.Plug(elem)) - }) - return handleErr(err) - }); err != nil { - return err + })); err != nil { + return err + } } } @@ -3606,7 +3742,11 @@ func (e evalEvery) eval(iter unifyIterator) error { ).SetLocation(e.Domain.Location), ) - domain := e.e.closure(generator) + domain := evalPool.Get() + defer evalPool.Put(domain) + + e.e.closure(generator, domain) + all := true // all generator evaluations yield one successful body evaluation domain.traceEnter(e.expr) @@ -3617,7 +3757,11 @@ func (e evalEvery) eval(iter unifyIterator) error { // This would do extra work, like iterating needlessly if domain was a large array. return nil } - body := child.closure(e.Body) + + body := evalPool.Get() + defer evalPool.Put(body) + + child.closure(e.Body, body) body.findOne = true body.traceEnter(e.Body) done := false @@ -3744,10 +3888,12 @@ func applyCopyPropagation(p *copypropagation.CopyPropagator, instr *Instrumentat return result } +func nonGroundKey(k, _ *ast.Term) bool { + return !k.IsGround() +} + func nonGroundKeys(a ast.Object) bool { - return a.Until(func(k, _ *ast.Term) bool { - return !k.IsGround() - }) + return a.Until(nonGroundKey) } func plugKeys(a ast.Object, b *bindings) ast.Object { diff --git a/v1/topdown/object.go b/v1/topdown/object.go index c3706b6d34..11671da5f3 100644 --- a/v1/topdown/object.go +++ b/v1/topdown/object.go @@ -50,9 +50,6 @@ func builtinObjectUnionN(_ BuiltinContext, operands []*ast.Term, iter func(*ast. return builtins.NewOperandElementErr(1, arr, arr.Elem(i).Value, "object") } mergewithOverwriteInPlace(result, o, frozenKeys) - if err != nil { - return err - } } return iter(ast.NewTerm(result)) diff --git a/v1/topdown/providers.go b/v1/topdown/providers.go index 980cbb5c0e..dd84026e4b 100644 --- a/v1/topdown/providers.go +++ b/v1/topdown/providers.go @@ -119,9 +119,6 @@ func builtinAWSSigV4SignReq(_ BuiltinContext, operands []*ast.Term, iter func(*a } signingTimestamp = time.Unix(0, ts) - if err != nil { - return err - } // Make sure our required keys exist! // This check is stricter than required, but better to break here than downstream. diff --git a/v1/topdown/strings.go b/v1/topdown/strings.go index b7124af730..8d6c753e6d 100644 --- a/v1/topdown/strings.go +++ b/v1/topdown/strings.go @@ -15,6 +15,7 @@ import ( "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/util" ) func builtinAnyPrefixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { @@ -386,9 +387,9 @@ func builtinSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) e return err } elems := strings.Split(string(s), string(d)) - arr := make([]*ast.Term, len(elems)) + arr := util.NewPtrSlice[ast.Term](len(elems)) for i := range elems { - arr[i] = ast.StringTerm(elems[i]) + arr[i].Value = ast.String(elems[i]) } return iter(ast.ArrayTerm(arr...)) } @@ -438,14 +439,8 @@ func builtinReplaceN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term } oldnewArr = append(oldnewArr, string(keyVal), string(strVal)) } - if err != nil { - return err - } - - r := strings.NewReplacer(oldnewArr...) - replaced := r.Replace(string(s)) - return iter(ast.StringTerm(replaced)) + return iter(ast.StringTerm(strings.NewReplacer(oldnewArr...).Replace(string(s)))) } func builtinTrim(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { diff --git a/v1/topdown/strings_bench_test.go b/v1/topdown/strings_bench_test.go index c9632d8e47..c2a0e677cb 100644 --- a/v1/topdown/strings_bench_test.go +++ b/v1/topdown/strings_bench_test.go @@ -113,3 +113,36 @@ func generateBulkStartsWithInput() map[string]interface{} { "prefixes": prefixes, } } + +func BenchmarkSplit(b *testing.B) { + bctx := BuiltinContext{} + operands := []*ast.Term{ + ast.StringTerm("a.b.c.d.e"), + ast.StringTerm("."), + } + + exp := eqIter(ast.ArrayTerm( + ast.StringTerm("a"), + ast.StringTerm("b"), + ast.StringTerm("c"), + ast.StringTerm("d"), + ast.StringTerm("e"), + )) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if err := builtinSplit(bctx, operands, exp); err != nil { + b.Fatal(err) + } + } +} + +func eqIter(a *ast.Term) func(*ast.Term) error { + return func(b *ast.Term) error { + if !a.Equal(b) { + return fmt.Errorf("expected %v equal to %v", a, b) + } + return nil + } +} diff --git a/v1/topdown/tokens_test.go b/v1/topdown/tokens_test.go index c618c7dd32..3ab5dbe165 100644 --- a/v1/topdown/tokens_test.go +++ b/v1/topdown/tokens_test.go @@ -238,14 +238,11 @@ func TestTopDownJWTEncodeSignES256(t *testing.T) { input1 string input2 string input3 string - err string }{ "https://tools.ietf.org/html/rfc7515#appendix-A.3", "`" + es256Hdr + "`", "`" + examplePayload + "`", "`" + ecKey + "`", - - "", } type test struct { note string @@ -361,14 +358,11 @@ func TestTopDownJWTEncodeSignES512(t *testing.T) { input1 string input2 string input3 string - err string }{ "https://tools.ietf.org/html/rfc7515#appendix-A.4", "`" + es512Hdr + "`", "`" + examplePayload + "`", "`" + ecKey + "`", - - "", } type test struct { note string diff --git a/v1/topdown/walk.go b/v1/topdown/walk.go index 5c7f6ba0b0..f5dcf5c9f1 100644 --- a/v1/topdown/walk.go +++ b/v1/topdown/walk.go @@ -8,9 +8,7 @@ import ( "github.com/open-policy-agent/opa/v1/ast" ) -var ( - emptyArr = ast.ArrayTerm() -) +var emptyArr = ast.ArrayTerm() func evalWalk(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { input := operands[0] @@ -18,9 +16,9 @@ func evalWalk(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error if pathIsWildcard(operands) { // When the path assignment is a wildcard: walk(input, [_, value]) // we may skip the path construction entirely, and simply return - // same pointer in each iteration. This is a much more efficient + // same pointer in each iteration. This is a *much* more efficient // path when only the values are needed. - return walkNoPath(input, iter) + return walkNoPath(ast.ArrayTerm(emptyArr, input), iter) } filter := getOutputPath(operands) @@ -58,12 +56,11 @@ func walk(filter, path *ast.Array, input *ast.Term, iter func(*ast.Term) error) } } case ast.Object: - return v.Iter(func(k, v *ast.Term) error { - if err := walk(filter, pathAppend(path, k), v, iter); err != nil { + for _, k := range v.Keys() { + if err := walk(filter, pathAppend(path, k), v.Get(k), iter); err != nil { return err } - return nil - }) + } case ast.Set: for _, elem := range v.Slice() { if err := walk(filter, pathAppend(path, elem), elem, iter); err != nil { @@ -76,24 +73,37 @@ func walk(filter, path *ast.Array, input *ast.Term, iter func(*ast.Term) error) } func walkNoPath(input *ast.Term, iter func(*ast.Term) error) error { - if err := iter(ast.ArrayTerm(emptyArr, input)); err != nil { + // Note: the path array is embedded in the input from the start here + // in order to avoid an extra allocation per iteration. This leads to + // a little convoluted code below in order to extract and set the value, + // but since walk is commonly used to traverse large data structures, + // the performance gain is worth it. + if err := iter(input); err != nil { return err } - switch v := input.Value.(type) { + inputArray := input.Value.(*ast.Array) + value := inputArray.Get(ast.InternedIntNumberTerm(1)).Value + + switch v := value.(type) { case ast.Object: - return v.Iter(func(_, v *ast.Term) error { - return walkNoPath(v, iter) - }) + for _, k := range v.Keys() { + inputArray.Set(1, v.Get(k)) + if err := walkNoPath(input, iter); err != nil { + return err + } + } case *ast.Array: for i := 0; i < v.Len(); i++ { - if err := walkNoPath(v.Elem(i), iter); err != nil { + inputArray.Set(1, v.Elem(i)) + if err := walkNoPath(input, iter); err != nil { return err } } case ast.Set: for _, elem := range v.Slice() { - if err := walkNoPath(elem, iter); err != nil { + inputArray.Set(1, elem) + if err := walkNoPath(input, iter); err != nil { return err } } diff --git a/v1/util/performance.go b/v1/util/performance.go new file mode 100644 index 0000000000..03dc7d0601 --- /dev/null +++ b/v1/util/performance.go @@ -0,0 +1,24 @@ +package util + +import "slices" + +// NewPtrSlice returns a slice of pointers to T with length n, +// with only 2 allocations performed no matter the size of n. +// See: +// https://gist.github.com/CAFxX/e96e8a5c3841d152f16d266a1fe7f8bd#slices-of-pointers +func NewPtrSlice[T any](n int) []*T { + return GrowPtrSlice[T](nil, n) +} + +// GrowPtrSlice appends n elements to the slice, each pointing to +// a newly-allocated T. The resulting slice has length equal to len(s)+n. +// +// It performs at most 2 allocations, regardless of n. +func GrowPtrSlice[T any](s []*T, n int) []*T { + s = slices.Grow(s, n) + p := make([]T, n) + for i := 0; i < n; i++ { + s = append(s, &p[i]) + } + return s +} diff --git a/v1/util/performance_test.go b/v1/util/performance_test.go new file mode 100644 index 0000000000..179845ef7d --- /dev/null +++ b/v1/util/performance_test.go @@ -0,0 +1,17 @@ +package util + +import "testing" + +type testStruct struct { + foo int +} + +func BenchmarkNewPtrSlice(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + s := NewPtrSlice[testStruct](100) + for j := 0; j < 100; j++ { + s[j].foo = j + } + } +}