From 50b5ee500c8035a1ae80f25b9f24b1a58c01f766 Mon Sep 17 00:00:00 2001 From: Anders Eknert Date: Wed, 18 Dec 2024 20:59:15 +0100 Subject: [PATCH] Reduce allocations, chapter III (#7222) My last PR for a while in the ongoing "reduce allocations in eval" quest. Motivated initially mostly to speed up `regal lint`, but most of the changes here positively impacts evaluation performance for most policies. The changes with the highest impact in this PR: * Use `sync.Pool`s to avoid the most costly allocations, includuing heavy `*eval` pointers created each time a child or closure scope is evaluated. * When tracing is disabled, avoid variable escaping to heap in `evalStep` function whose value is only read when tracing is enabled. * Save one allocation per iteration in `walkNoPath` by reusing an AST array instead of creating a new one for each call. Also a few minor fixes here and there which either fixed some correctness issue, or had a measurable (although minor) positive impact on performance. **regal lint bundle (main)** ``` BenchmarkRegalLintingItself-10 1 2015560750 ns/op 4335625360 B/op 83728460 allocs/op ``` **regal lint bundle (now)** ``` BenchmarkRegalLintingItself-10 1 1828754125 ns/op 3541027496 B/op 70080568 allocs/op ``` About 10% faster eval, with almost a gigabyte less memory allocated, and 13 million+ allocations less performed. Another topic discussed recently has been the cost of calling custom functions in hot paths. While this PR doesn't address that problem fully, the benefits of the change is still quite noticeable. A benchmark for that case specifically is also included in the PR, and the change compared to main as noted below: **main** ``` BenchmarkCustomFunctionInHotPath-10 55 18543908 ns/op 20821043 B/op 284611 allocs/op ``` **pr** ``` BenchmarkCustomFunctionInHotPath-10 73 16247587 ns/op 13048108 B/op 228406 allocs/op ``` It's worth noting however that this benchmark benefits "unfairly" by the improvements made in the `walkNoPath` function, and perhaps more so than custom function evaluation getting that much more efficient. Signed-off-by: Anders Eknert --- v1/ast/check.go | 28 +- v1/ast/policy.go | 18 +- v1/ast/term.go | 21 +- v1/ast/visit.go | 10 +- v1/rego/rego.go | 3 - v1/rego/rego_bench_test.go | 51 + v1/rego/testdata/ast.json | 7647 ++++++++++++++++++++++++++++++ v1/topdown/bindings.go | 2 +- v1/topdown/eval.go | 392 +- v1/topdown/object.go | 3 - v1/topdown/providers.go | 3 - v1/topdown/strings.go | 13 +- v1/topdown/strings_bench_test.go | 33 + v1/topdown/tokens_test.go | 6 - v1/topdown/walk.go | 42 +- v1/util/performance.go | 24 + v1/util/performance_test.go | 17 + 17 files changed, 8124 insertions(+), 189 deletions(-) create mode 100644 v1/rego/testdata/ast.json create mode 100644 v1/util/performance.go create mode 100644 v1/util/performance_test.go 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 + } + } +}