// evalTermSingleValue evaluates and prints terms in cases where the term evaluates to a // single value, e.g., "1", true, [1,2,"foo"], [x | x = a[i], a = [1,2,3]], etc. Ground terms // and comprehensions always evaluate to a single value. To handle references, this function // still executes the query, except it does so by rewriting the body to assign the term // to a variable. This allows the REPL to obtain the result even if the term is false. func (r *REPL) evalTermSingleValue(ctx context.Context, compiler *ast.Compiler, request ast.Value, body ast.Body) error { term := body[0].Terms.(*ast.Term) outputVar := ast.Wildcard body = ast.NewBody(ast.Equality.Expr(term, outputVar)) t := topdown.New(ctx, body, compiler, r.store, r.txn) t.Request = request var buf *topdown.BufferTracer if r.explain != explainOff { buf = topdown.NewBufferTracer() t.Tracer = buf } var result interface{} isTrue := false err := topdown.Eval(t, func(t *topdown.Topdown) error { p := t.Locals.Get(outputVar.Value) v, err := topdown.ValueToInterface(p, t) if err != nil { return err } result = v isTrue = true return nil }) if buf != nil { r.printTrace(ctx, compiler, *buf) } if err != nil { return err } if isTrue { r.printJSON(result) } else if !r.undefinedDisabled { r.printUndefined() } return nil }
// evalTermMultiValue evaluates and prints terms in cases where the term may evaluate to multiple // ground values, e.g., a[i], [servers[x]], etc. func (r *REPL) evalTermMultiValue(ctx context.Context, compiler *ast.Compiler, request ast.Value, body ast.Body) error { // Mangle the expression in the same way we do for evalTermSingleValue. When handling the // evaluation result below, we will ignore this variable. term := body[0].Terms.(*ast.Term) outputVar := ast.Wildcard body = ast.NewBody(ast.Equality.Expr(term, outputVar)) t := topdown.New(ctx, body, compiler, r.store, r.txn) t.Request = request var buf *topdown.BufferTracer if r.explain != explainOff { buf = topdown.NewBufferTracer() t.Tracer = buf } vars := map[string]struct{}{} results := []map[string]interface{}{} resultKey := string(term.Location.Text) // Do not include the value of the input term if the input term was a set reference. E.g., // for "p[x]", the value users are interested in is "x" not p[x] which is always defined // as true. includeValue := !r.isSetReference(compiler, term) err := topdown.Eval(t, func(t *topdown.Topdown) error { result := map[string]interface{}{} var err error t.Locals.Iter(func(k, v ast.Value) bool { if k, ok := k.(ast.Var); ok { if k.IsWildcard() || k.Equal(outputVar.Value) { return false } x, e := topdown.ValueToInterface(v, t) if e != nil { err = e return true } s := string(k) result[s] = x vars[s] = struct{}{} } return false }) if err != nil { return err } if includeValue { p := topdown.PlugTerm(term, t.Binding) v, err := topdown.ValueToInterface(p.Value, t) if err != nil { return err } result[resultKey] = v } results = append(results, result) return nil }) if buf != nil { r.printTrace(ctx, compiler, *buf) } if err != nil { return err } if len(results) > 0 { keys := []string{} for v := range vars { keys = append(keys, v) } sort.Strings(keys) if includeValue { keys = append(keys, resultKey) } r.printResults(keys, results) } else if !r.undefinedDisabled { r.printUndefined() } return nil }