func TestEventEqual(t *testing.T) { a := ast.NewValueMap() a.Put(ast.String("foo"), ast.Number("1")) b := ast.NewValueMap() b.Put(ast.String("foo"), ast.Number("2")) tests := []struct { a *Event b *Event equal bool }{ {&Event{}, &Event{}, true}, {&Event{Op: EvalOp}, &Event{Op: EnterOp}, false}, {&Event{QueryID: 1}, &Event{QueryID: 2}, false}, {&Event{ParentID: 1}, &Event{ParentID: 2}, false}, {&Event{Node: ast.MustParseBody("true")}, &Event{Node: ast.MustParseBody("false")}, false}, {&Event{Node: ast.MustParseBody("true")[0]}, &Event{Node: ast.MustParseBody("false")[0]}, false}, {&Event{Node: ast.MustParseRule("p :- true")}, &Event{Node: ast.MustParseRule("p :- false")}, false}, {&Event{Node: "foo"}, &Event{Node: "foo"}, false}, // test some unsupported node type } for _, tc := range tests { if tc.a.Equal(tc.b) != tc.equal { var s string if tc.equal { s = "==" } else { s = "!=" } t.Errorf("Expected %v %v %v", tc.a, s, tc.b) } } }
func evalSubstring(t *Topdown, expr *ast.Expr, iter Iterator) error { ops := expr.Terms.([]*ast.Term) base, err := ValueToString(ops[1].Value, t) if err != nil { return errors.Wrapf(err, "%v: base value must be a string", ast.Substring.Name) } startIndex, err := ValueToInt(ops[2].Value, t) if err != nil { return errors.Wrapf(err, "%v: start index must be a number", ast.Substring.Name) } l, err := ValueToInt(ops[3].Value, t) if err != nil { return errors.Wrapf(err, "%v: length must be a number", ast.Substring.Name) } var s ast.String if l < 0 { s = ast.String(base[startIndex:]) } else { s = ast.String(base[startIndex : startIndex+l]) } undo, err := evalEqUnify(t, s, ops[4].Value, nil, iter) t.Unbind(undo) return err }
func evalFormatInt(t *Topdown, expr *ast.Expr, iter Iterator) error { ops := expr.Terms.([]*ast.Term) input, err := ValueToJSONNumber(ops[1].Value, t) if err != nil { return errors.Wrapf(err, "%v: input must be a number", ast.FormatInt.Name) } i, _ := jsonNumberToFloat(input).Int(nil) base, err := ValueToInt(ops[2].Value, t) if err != nil { return errors.Wrapf(err, "%v: base must be an integer", ast.FormatInt.Name) } var format string switch base { case 2: format = "%b" case 8: format = "%o" case 10: format = "%d" case 16: format = "%x" default: return errors.Wrapf(err, "%v: base must be one of 2, 8, 10, 16", ast.FormatInt.Name) } s := ast.String(fmt.Sprintf(format, i)) undo, err := evalEqUnify(t, s, ops[3].Value, nil, iter) t.Unbind(undo) return err }
func evalUpper(t *Topdown, expr *ast.Expr, iter Iterator) error { ops := expr.Terms.([]*ast.Term) orig, err := ValueToString(ops[1].Value, t) if err != nil { return errors.Wrapf(err, "%v: original value must be a string", ast.Upper.Name) } s := ast.String(strings.ToUpper(orig)) undo, err := evalEqUnify(t, s, ops[2].Value, nil, iter) t.Unbind(undo) return err }
func evalConcat(t *Topdown, expr *ast.Expr, iter Iterator) error { ops := expr.Terms.([]*ast.Term) join, err := ValueToString(ops[1].Value, t) if err != nil { return errors.Wrapf(err, "%v: join value must be a string", ast.Concat.Name) } sl, err := ValueToStrings(ops[2].Value, t) if err != nil { return errors.Wrapf(err, "%v: input value must be array of strings", ast.Concat.Name) } s := ast.String(strings.Join(sl, join)) undo, err := evalEqUnify(t, s, ops[3].Value, nil, iter) t.Unbind(undo) return err }
func loadExpectedBindings(input string) []*ast.ValueMap { var data []map[string]interface{} if err := util.UnmarshalJSON([]byte(input), &data); err != nil { panic(err) } var expected []*ast.ValueMap for _, bindings := range data { buf := ast.NewValueMap() for k, v := range bindings { switch v := v.(type) { case string: buf.Put(ast.Var(k), ast.String(v)) case json.Number: buf.Put(ast.Var(k), ast.Number(v)) default: panic("unreachable") } } expected = append(expected, buf) } return expected }
func iterStorage(ctx context.Context, store Store, txn Transaction, nonGround, ground ast.Ref, bindings *ast.ValueMap, iter func(*ast.ValueMap, interface{})) error { if len(nonGround) == 0 { path, err := NewPathForRef(ground) if err != nil { return err } node, err := store.Read(ctx, txn, path) if err != nil { if IsNotFound(err) { return nil } return err } iter(bindings, node) return nil } head := nonGround[0] tail := nonGround[1:] headVar, isVar := head.Value.(ast.Var) if !isVar || len(ground) == 0 { ground = append(ground, head) return iterStorage(ctx, store, txn, tail, ground, bindings, iter) } path, err := NewPathForRef(ground) if err != nil { return err } node, err := store.Read(ctx, txn, path) if err != nil { if IsNotFound(err) { return nil } return err } switch node := node.(type) { case map[string]interface{}: for key := range node { ground = append(ground, ast.StringTerm(key)) cpy := bindings.Copy() cpy.Put(headVar, ast.String(key)) err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) if err != nil { return err } ground = ground[:len(ground)-1] } case []interface{}: for i := range node { idx := ast.IntNumberTerm(i) ground = append(ground, idx) cpy := bindings.Copy() cpy.Put(headVar, idx.Value) err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) if err != nil { return err } ground = ground[:len(ground)-1] } } return nil }