// Copyright 2016 The OPA Authors. All rights reserved. // Use of this source code is governed by an Apache2 // license that can be found in the LICENSE file. package ast import ( "bytes" "encoding/json" "errors" "fmt" "io" "math" "net/url" "slices" "strconv" "strings" "sync" "unicode" "github.com/cespare/xxhash/v2" astJSON "github.com/open-policy-agent/opa/v1/ast/json" "github.com/open-policy-agent/opa/v1/ast/location" "github.com/open-policy-agent/opa/v1/util" ) // maxBindingsEstimate is the cap for binding count estimates in comprehensions. // This value aligns with maxLinearScan in topdown/bindings.go. const maxBindingsEstimate = 16 // EstimateBodyBindingCount returns an estimate of the number of bindings needed // for evaluating a comprehension body. It uses the body length as a heuristic, // capped at maxBindingsEstimate. func EstimateBodyBindingCount(body Body) (estimate int) { return min(len(body), maxBindingsEstimate) } var ( NullValue Value = Null{} errFindNotFound = errors.New("find: not found") ) // Location records a position in source code. type Location = location.Location // NewLocation returns a new Location object. func NewLocation(text []byte, file string, row int, col int) *Location { return location.NewLocation(text, file, row, col) } // Value declares the common interface for all Term values. Every kind of Term value // in the language is represented as a type that implements this interface: // // - Null, Boolean, Number, String // - Object, Array, Set // - Variables, References // - Array, Set, and Object Comprehensions // - Calls // - Template Strings type Value interface { Compare(other Value) int // Compare returns <0, 0, or >0 if this Value is less than, equal to, or greater than other, respectively. Find(path Ref) (Value, error) // Find returns value referred to by path or an error if path is not found. Hash() int // Returns hash code of the value. IsGround() bool // IsGround returns true if this value is not a variable or contains no variables. String() string // String returns a human readable string representation of the value. StringLengther // All Values must be able to report their string length during optimization. } // InterfaceToValue converts a native Go value x to a Value. func InterfaceToValue(x any) (Value, error) { switch x := x.(type) { case Value: return x, nil case nil: return NullValue, nil case bool: return InternedValue(x), nil case json.Number: if interned := InternedIntNumberTermFromString(string(x)); interned != nil { return interned.Value, nil } return Number(x), nil case int: return InternedValueOr(x, newIntNumberValue), nil case int64: return InternedValueOr(x, newInt64NumberValue), nil case uint64: return InternedValueOr(x, newUint64NumberValue), nil case float64: return floatNumber(x), nil case string: return String(x), nil 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].Value = e } return NewArray(r...), nil case []string: r := util.NewPtrSlice[Term](len(x)) for i, e := range x { r[i].Value = String(e) } return NewArray(r...), nil case map[string]any: kvs := util.NewPtrSlice[Term](len(x) * 2) idx := 0 for k, v := range x { kvs[idx].Value = String(k) v, err := InterfaceToValue(v) if err != nil { return nil, err } kvs[idx+1].Value = v idx += 2 } 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 { r.Insert(StringTerm(k), StringTerm(v)) } return r, nil default: ptr := util.Reference(x) if err := util.RoundTrip(ptr); err != nil { return nil, fmt.Errorf("ast: interface conversion: %w", err) } return InterfaceToValue(*ptr) } } // ValueFromReader returns an AST value from a JSON serialized value in the reader. func ValueFromReader(r io.Reader) (Value, error) { var x any if err := util.NewJSONDecoder(r).Decode(&x); err != nil { return nil, err } return InterfaceToValue(x) } // As converts v into a Go native type referred to by x. func As(v Value, x any) error { return util.NewJSONDecoder(strings.NewReader(v.String())).Decode(x) } // Resolver defines the interface for resolving references to native Go values. type Resolver interface { Resolve(Ref) (any, error) } // ValueResolver defines the interface for resolving references to AST values. type ValueResolver interface { Resolve(Ref) (Value, error) } // UnknownValueErr indicates a ValueResolver was unable to resolve a reference // because the reference refers to an unknown value. type UnknownValueErr struct{} func (UnknownValueErr) Error() string { return "unknown value" } // IsUnknownValueErr returns true if the err is an UnknownValueErr. func IsUnknownValueErr(err error) bool { _, ok := err.(UnknownValueErr) return ok } type illegalResolver struct{} func (illegalResolver) Resolve(ref Ref) (any, error) { return nil, fmt.Errorf("illegal value: %v", ref) } // ValueToInterface returns the Go representation of an AST value. The AST // value should not contain any values that require evaluation (e.g., vars, // comprehensions, etc.) func ValueToInterface(v Value, resolver Resolver) (any, error) { return valueToInterface(v, resolver, JSONOpt{}) } func valueToInterface(v Value, resolver Resolver, opt JSONOpt) (any, error) { switch v := v.(type) { case Null: return nil, nil case Boolean: return bool(v), nil case Number: return json.Number(v), nil case String: return string(v), nil case *Array: buf := []any{} for i := range v.Len() { x1, err := valueToInterface(v.Elem(i).Value, resolver, opt) if err != nil { return nil, err } buf = append(buf, x1) } return buf, nil case *object: buf := make(map[string]any, v.Len()) err := v.Iter(func(k, v *Term) error { ki, err := valueToInterface(k.Value, resolver, opt) if err != nil { return err } var str string var ok bool if str, ok = ki.(string); !ok { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(ki); err != nil { return err } str = strings.TrimSpace(buf.String()) } vi, err := valueToInterface(v.Value, resolver, opt) if err != nil { return err } buf[str] = vi return nil }) if err != nil { return nil, err } return buf, nil case *lazyObj: if opt.CopyMaps { return valueToInterface(v.force(), resolver, opt) } return v.native, nil case Set: buf := []any{} iter := func(x *Term) error { x1, err := valueToInterface(x.Value, resolver, opt) if err != nil { return err } buf = append(buf, x1) return nil } var err error if opt.SortSets { err = v.Sorted().Iter(iter) } else { err = v.Iter(iter) } if err != nil { return nil, err } return buf, nil case Ref: return resolver.Resolve(v) default: return nil, fmt.Errorf("%v requires evaluation", TypeName(v)) } } // JSON returns the JSON representation of v. The value must not contain any // refs or terms that require evaluation (e.g., vars, comprehensions, etc.) func JSON(v Value) (any, error) { return JSONWithOpt(v, JSONOpt{}) } // JSONOpt defines parameters for AST to JSON conversion. type JSONOpt struct { SortSets bool // sort sets before serializing (this makes conversion more expensive) CopyMaps bool // enforces copying of map[string]any read from the store } // JSONWithOpt returns the JSON representation of v. The value must not contain any // refs or terms that require evaluation (e.g., vars, comprehensions, etc.) func JSONWithOpt(v Value, opt JSONOpt) (any, error) { return valueToInterface(v, illegalResolver{}, opt) } // MustJSON returns the JSON representation of v. The value must not contain any // refs or terms that require evaluation (e.g., vars, comprehensions, etc.) If // the conversion fails, this function will panic. This function is mostly for // test purposes. func MustJSON(v Value) any { r, err := JSON(v) if err != nil { panic(err) } return r } // MustInterfaceToValue converts a native Go value x to a Value. If the // conversion fails, this function will panic. This function is mostly for test // purposes. func MustInterfaceToValue(x any) Value { v, err := InterfaceToValue(x) if err != nil { panic(err) } return v } // Term is an argument to a function. type Term struct { Value Value `json:"value"` // the value of the Term as represented in Go Location *Location `json:"location,omitempty"` // the location of the Term in the source } // NewTerm returns a new Term object. func NewTerm(v Value) *Term { return &Term{ Value: v, } } // SetLocation updates the term's Location and returns the term itself. func (term *Term) SetLocation(loc *Location) *Term { term.Location = loc return term } // Loc returns the Location of term. func (term *Term) Loc() *Location { if term == nil { return nil } return term.Location } // SetLoc sets the location on term. func (term *Term) SetLoc(loc *Location) { term.SetLocation(loc) } // Copy returns a deep copy of term. func (term *Term) Copy() *Term { if term == nil { return nil } cpy := *term switch v := term.Value.(type) { case Null, Boolean, Number, String, Var: cpy.Value = v case Ref: cpy.Value = v.Copy() case *Array: cpy.Value = v.Copy() case Set: cpy.Value = v.Copy() case *object: cpy.Value = v.Copy() case *ArrayComprehension: cpy.Value = v.Copy() case *ObjectComprehension: cpy.Value = v.Copy() case *SetComprehension: cpy.Value = v.Copy() case *TemplateString: cpy.Value = v.Copy() case Call: cpy.Value = v.Copy() } return &cpy } // Equal returns true if this term equals the other term. Equality is // defined for each kind of term, and does not compare the Location. func (term *Term) Equal(other *Term) bool { if term == nil && other != nil { return false } if term != nil && other == nil { return false } if term == other { return true } return ValueEqual(term.Value, other.Value) } // Get returns a value referred to by name from the term. func (term *Term) Get(name *Term) *Term { switch v := term.Value.(type) { case *object: return v.Get(name) case *Array: return v.Get(name) case interface { Get(*Term) *Term }: return v.Get(name) case Set: if v.Contains(name) { return name } } return nil } // Hash returns the hash code of the Term's Value. Its Location // is ignored. func (term *Term) Hash() int { return term.Value.Hash() } // IsGround returns true if this term's Value is ground. func (term *Term) IsGround() bool { return term.Value.IsGround() } // termJSON is used to serialize Term to JSON without map allocation. type termJSON struct { Location *Location `json:"location,omitempty"` Type string `json:"type"` Value Value `json:"value"` } // MarshalJSON returns the JSON encoding of the term. // // Specialized marshalling logic is required to include a type hint for Value. func (term *Term) MarshalJSON() ([]byte, error) { d := termJSON{ Type: ValueName(term.Value), Value: term.Value, } jsonOptions := astJSON.GetOptions().MarshalOptions if jsonOptions.IncludeLocation.Term { d.Location = term.Location } return json.Marshal(d) } func (term *Term) String() string { return term.Value.String() } // UnmarshalJSON parses the byte array and stores the result in term. // Specialized unmarshalling is required to handle Value and Location. func (term *Term) UnmarshalJSON(bs []byte) error { v := map[string]any{} if err := util.UnmarshalJSON(bs, &v); err != nil { return err } val, err := unmarshalValue(v) if err != nil { return err } term.Value = val if loc, ok := v["location"].(map[string]any); ok { term.Location = &Location{} err := unmarshalLocation(term.Location, loc) if err != nil { return err } } return nil } // Vars returns a VarSet with variables contained in this term. func (term *Term) Vars() VarSet { vis := NewVarVisitor() vis.Walk(term) return vis.vars } // IsConstant returns true if the AST value is constant. // Note that this is only a shallow check as we currently don't have a real // notion of constant "vars" in the AST implementation. Meaning that while we could // derive that a reference to a constant value is also constant, we currently don't. func IsConstant(v Value) bool { switch v.(type) { case Null, Boolean, Number, String: return true case Var, Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Call: return false } found := false vis := GenericVisitor{ func(x any) bool { switch x.(type) { case Var, Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Call: found = true return true } return false }, } vis.Walk(v) return !found } // IsComprehension returns true if the supplied value is a comprehension. func IsComprehension(x Value) bool { switch x.(type) { case *ArrayComprehension, *ObjectComprehension, *SetComprehension: return true } return false } // ContainsRefs returns true if the Value v contains refs. func ContainsRefs(v any) bool { found := false WalkRefs(v, func(Ref) bool { found = true return found }) return found } // ContainsComprehensions returns true if the Value v contains comprehensions. func ContainsComprehensions(v any) bool { found := false WalkClosures(v, func(x any) bool { switch x.(type) { case *ArrayComprehension, *ObjectComprehension, *SetComprehension: found = true return found } return found }) return found } // ContainsClosures returns true if the Value v contains closures. func ContainsClosures(v any) bool { found := false WalkClosures(v, func(x any) bool { switch x.(type) { case *ArrayComprehension, *ObjectComprehension, *SetComprehension, *Every: found = true return found } return found }) return found } // IsScalar returns true if the AST value is a scalar. func IsScalar(v Value) bool { switch v.(type) { case String, Number, Boolean, Null: return true } return false } // Null represents the null value defined by JSON. type Null struct{} // NullTerm creates a new Term with a Null value. func NullTerm() *Term { return &Term{Value: NullValue} } // Equal returns true if the other term Value is also Null. func (Null) Equal(other Value) bool { switch other.(type) { case Null: return true default: return false } } // Compare compares null to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (Null) Compare(other Value) int { if _, ok := other.(Null); ok { return 0 } return -1 } // Find returns the current value or a not found error. func (Null) Find(path Ref) (Value, error) { if len(path) == 0 { return NullValue, nil } return nil, errFindNotFound } // Hash returns the hash code for the Value. func (Null) Hash() int { return 0 } // IsGround always returns true. func (Null) IsGround() bool { return true } func (Null) String() string { return "null" } // Boolean represents a boolean value defined by JSON. type Boolean bool // BooleanTerm creates a new Term with a Boolean value. func BooleanTerm(b bool) *Term { return &Term{Value: internedBooleanValue(b)} } // Equal returns true if the other Value is a Boolean and is equal. func (bol Boolean) Equal(other Value) bool { switch other := other.(type) { case Boolean: return bol == other default: return false } } // Compare compares bol to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (bol Boolean) Compare(other Value) int { switch other := other.(type) { case Boolean: if bol == other { return 0 } if !bol { return -1 } return 1 case Null: return 1 } return -1 } // Find returns the current value or a not found error. func (bol Boolean) Find(path Ref) (Value, error) { if len(path) == 0 { return InternedTerm(bool(bol)).Value, nil } return nil, errFindNotFound } // Hash returns the hash code for the Value. func (bol Boolean) Hash() int { if bol { return 1 } return 0 } // IsGround always returns true. func (Boolean) IsGround() bool { return true } func (bol Boolean) String() string { return strconv.FormatBool(bool(bol)) } // Number represents a numeric value as defined by JSON. type Number json.Number // NumberTerm creates a new Term with a Number value. func NumberTerm(n json.Number) *Term { return &Term{Value: Number(n)} } // IntNumberTerm creates a new Term with an integer Number value. // For values between -1 and 512, returns a cached Term to reduce allocations. func IntNumberTerm(i int) *Term { return internedIntNumberTerm(i) } // UIntNumberTerm creates a new Term with an unsigned integer Number value. func UIntNumberTerm(u uint64) *Term { return &Term{Value: newUint64NumberValue(u)} } // FloatNumberTerm creates a new Term with a floating point Number value. func FloatNumberTerm(f float64) *Term { s := strconv.FormatFloat(f, 'g', -1, 64) return &Term{Value: Number(s)} } // Equal returns true if the other Value is a Number and is equal. func (num Number) Equal(other Value) bool { if other, ok := other.(Number); ok { return NumberCompare(num, other) == 0 } return false } // Compare compares num to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (num Number) Compare(other Value) int { // Optimize for the common case, as calling Compare allocates on heap. if otherNum, yes := other.(Number); yes { return NumberCompare(num, otherNum) } return Compare(num, other) } // Find returns the current value or a not found error. func (num Number) Find(path Ref) (Value, error) { if len(path) == 0 { return num, nil } return nil, errFindNotFound } // Hash returns the hash code for the Value. func (num Number) Hash() int { if len(num) < 4 { if i, err := strconv.Atoi(string(num)); err == nil { return i } } if f, ok := num.Float64(); ok { return int(f) } return int(xxhash.Sum64String(string(num))) } // Int returns the int representation of num if possible. func (num Number) Int() (int, bool) { i64, ok := num.Int64() return int(i64), ok } // Int64 returns the int64 representation of num if possible. func (num Number) Int64() (int64, bool) { i, err := json.Number(num).Int64() if err != nil { return 0, false } return i, true } // Float64 returns the float64 representation of num if possible. func (num Number) Float64() (float64, bool) { f, err := json.Number(num).Float64() if err != nil { return 0, false } return f, true } // IsGround always returns true. func (Number) IsGround() bool { return true } // MarshalJSON returns JSON encoded bytes representing num. func (num Number) MarshalJSON() ([]byte, error) { return json.Marshal(json.Number(num)) } func (num Number) String() string { return string(num) } func newIntNumberValue(i int) Value { return Number(strconv.Itoa(i)) } func newInt64NumberValue(i int64) Value { return Number(strconv.FormatInt(i, 10)) } func newUint64NumberValue(u uint64) Value { return Number(strconv.FormatUint(u, 10)) } func floatNumber(f float64) Number { return Number(strconv.FormatFloat(f, 'g', -1, 64)) } // String represents a string value as defined by JSON. type String string // StringTerm creates a new Term with a String value. func StringTerm(s string) *Term { return &Term{Value: String(s)} } // Equal returns true if the other Value is a String and is equal. func (str String) Equal(other Value) bool { switch other := other.(type) { case String: return str == other default: return false } } // Compare compares str to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (str String) Compare(other Value) int { // Optimize for the common case of one string being compared to another by // using a direct comparison of values. This avoids the allocation performed // when calling Compare and its any argument conversion. if otherStr, ok := other.(String); ok { if str == otherStr { return 0 } if str < otherStr { return -1 } return 1 } return Compare(str, other) } // Find returns the current value or a not found error. func (str String) Find(path Ref) (Value, error) { if len(path) == 0 { return str, nil } return nil, errFindNotFound } // IsGround always returns true. func (String) IsGround() bool { return true } func (str String) String() string { return strconv.Quote(string(str)) } // Hash returns the hash code for the Value. func (str String) Hash() int { return int(xxhash.Sum64String(string(str))) } type TemplateString struct { Parts []Node `json:"parts"` MultiLine bool `json:"multi_line"` } func (ts *TemplateString) Copy() *TemplateString { cpy := &TemplateString{MultiLine: ts.MultiLine, Parts: make([]Node, len(ts.Parts))} for i, p := range ts.Parts { switch v := p.(type) { case *Expr: cpy.Parts[i] = v.Copy() case *Term: cpy.Parts[i] = v.Copy() } } return cpy } func (ts *TemplateString) Equal(other Value) bool { if o, ok := other.(*TemplateString); ok && ts.MultiLine == o.MultiLine && len(ts.Parts) == len(o.Parts) { for i, p := range ts.Parts { switch v := p.(type) { case *Expr: if ope, ok := o.Parts[i].(*Expr); !ok || !v.Equal(ope) { return false } case *Term: if opt, ok := o.Parts[i].(*Term); !ok || !v.Equal(opt) { return false } default: return false } } return true } return false } func (ts *TemplateString) Compare(other Value) int { if ots, ok := other.(*TemplateString); ok { if ts.MultiLine != ots.MultiLine { if !ts.MultiLine { return -1 } return 1 } if len(ts.Parts) != len(ots.Parts) { return len(ts.Parts) - len(ots.Parts) } for i := range ts.Parts { if cmp := Compare(ts.Parts[i], ots.Parts[i]); cmp != 0 { return cmp } } return 0 } return Compare(ts, other) } func (ts *TemplateString) Find(path Ref) (Value, error) { if len(path) == 0 { return ts, nil } return nil, errFindNotFound } func (ts *TemplateString) Hash() int { hash := 0 for _, p := range ts.Parts { switch x := p.(type) { case *Expr: hash += x.Hash() case *Term: hash += x.Value.Hash() default: panic(fmt.Sprintf("invalid template part type %T", p)) } } return hash } func (*TemplateString) IsGround() bool { return false } func (ts *TemplateString) String() string { buf, _ := ts.AppendText(make([]byte, 0, ts.StringLength())) return util.ByteSliceToString(buf) } func TemplateStringTerm(multiLine bool, parts ...Node) *Term { return &Term{Value: &TemplateString{MultiLine: multiLine, Parts: parts}} } // EscapeTemplateStringStringPart escapes unescaped left curly braces in s - i.e "{" becomes "\{". // The internal representation of string terms within a template string does **NOT** // treat '{' as special, but expects code dealing with template strings to escape them when // required, such as when serializing the complete template string. Code that programmatically // constructs template strings should not pre-escape left curly braces in string term parts. // // // TODO(anders): a future optimization would be to combine this with the other escaping done // // for strings (e.g. escaping quotes, backslashes, and JSON control characters) in a single operation // // to avoid multiple passes and allocations over the same string. That's currently done by // // strconv.Quote, so we would need to re-implement that logic in code of our own. // // NOTE(anders): I would love to come up with a better name for this component than // // "TemplateStringStringPart".. func EscapeTemplateStringStringPart(s string) string { numUnescaped := countUnescapedLeftCurly(s) if numUnescaped == 0 { return s } return util.ByteSliceToString(AppendEscapedTemplateStringStringPart(make([]byte, 0, len(s)+numUnescaped), s)) } func AppendEscapedTemplateStringStringPart(buf []byte, s string) []byte { if s[0] == '{' { buf = append(buf, '\\', s[0]) } else { buf = append(buf, s[0]) } for i := 1; i < len(s); i++ { if s[i] == '{' && s[i-1] != '\\' { buf = append(buf, '\\', s[i]) } else { buf = append(buf, s[i]) } } return buf } func countUnescapedLeftCurly(s string) (n int) { // Note(anders): while not the functions I'd intuitively reach for to solve this, // they are hands down the fastest option here, as they're done in assembly, which // performs about an order of magnitude better than a manual loop in Go. if n = strings.Count(s, "{"); n > 0 { n -= strings.Count(s, `\{`) } return n } // Var represents a variable as defined by the language. type Var string // VarTerm creates a new Term with a Variable value. func VarTerm(v string) *Term { return &Term{Value: InternedVarValue(v)} } // Equal returns true if the other Value is a Variable and has the same value // (name). func (v Var) Equal(other Value) bool { switch other := other.(type) { case Var: return v == other default: return false } } // Compare compares v to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (v Var) Compare(other Value) int { if otherVar, ok := other.(Var); ok { return strings.Compare(string(v), string(otherVar)) } return Compare(v, other) } // Find returns the current value or a not found error. func (v Var) Find(path Ref) (Value, error) { if len(path) == 0 { return v, nil } return nil, errFindNotFound } // Hash returns the hash code for the Value. func (v Var) Hash() int { return int(xxhash.Sum64String(string(v))) } // IsGround always returns false. func (Var) IsGround() bool { return false } // IsWildcard returns true if this is a wildcard variable. func (v Var) IsWildcard() bool { return strings.HasPrefix(string(v), WildcardPrefix) } // IsGenerated returns true if this variable was generated during compilation. func (v Var) IsGenerated() bool { return strings.HasPrefix(string(v), "__local") } func (v Var) String() string { // Special case for wildcard so that string representation is parseable. The // parser mangles wildcard variables to make their names unique and uses an // illegal variable name character (WildcardPrefix) to avoid conflicts. When // we serialize the variable here, we need to make sure it's parseable. if v.IsWildcard() { return WildcardString } return string(v) } // Ref represents a reference as defined by the language. type Ref []*Term // EmptyRef returns a new, empty reference. func EmptyRef() Ref { return Ref([]*Term{}) } // PtrRef returns a new reference against the head for the pointer // s. Path components in the pointer are unescaped. func PtrRef(head *Term, s string) (Ref, error) { s = strings.Trim(s, "/") if s == "" { return Ref{head}, nil } parts := strings.Split(s, "/") if maxLen := math.MaxInt32; len(parts) >= maxLen { return nil, fmt.Errorf("path too long: %s, %d > %d (max)", s, len(parts), maxLen) } ref := make(Ref, uint(len(parts))+1) ref[0] = head for i := range parts { var err error parts[i], err = url.PathUnescape(parts[i]) if err != nil { return nil, err } ref[i+1] = StringTerm(parts[i]) } return ref, nil } // RefTerm creates a new Term with a Ref value. func RefTerm(r ...*Term) *Term { return &Term{Value: Ref(r)} } // Append returns a copy of ref with the term appended to the end. func (ref Ref) Append(term *Term) Ref { n := len(ref) dst := make(Ref, n+1) copy(dst, ref) dst[n] = term return dst } // Insert returns a copy of the ref with x inserted at pos. If pos < len(ref), // existing elements are shifted to the right. If pos > len(ref)+1 this // function panics. func (ref Ref) Insert(x *Term, pos int) Ref { switch { case pos == len(ref): return ref.Append(x) case pos > len(ref)+1: panic("illegal index") } cpy := make(Ref, len(ref)+1) copy(cpy, ref[:pos]) cpy[pos] = x copy(cpy[pos+1:], ref[pos:]) return cpy } // Extend returns a copy of ref with the terms from other appended. The head of // other will be converted to a string. func (ref Ref) Extend(other Ref) Ref { offset := len(ref) dst := make(Ref, offset+len(other)) copy(dst, ref) head := other[0].Copy() head.Value = String(head.Value.(Var)) dst[offset] = head copy(dst[offset+1:], other[1:]) return dst } // Concat returns a ref with the terms appended. func (ref Ref) Concat(terms []*Term) Ref { if len(terms) == 0 { return ref } cpy := make(Ref, len(ref)+len(terms)) copy(cpy, ref) copy(cpy[len(ref):], terms) return cpy } // Dynamic returns the offset of the first non-constant operand of ref. func (ref Ref) Dynamic() int { switch ref[0].Value.(type) { case Call: return 0 } for i := 1; i < len(ref); i++ { if !IsConstant(ref[i].Value) { return i } } return -1 } // Copy returns a deep copy of ref. func (ref Ref) Copy() Ref { return termSliceCopy(ref) } // CopyNonGround returns a new ref with deep copies of the non-ground parts and shallow // copies of the ground parts. This is a *much* cheaper operation than Copy for operations // that only intend to modify (e.g. plug) the non-ground parts. The head element of the ref // is always shallow copied. func (ref Ref) CopyNonGround() Ref { cpy := make(Ref, len(ref)) cpy[0] = ref[0] for i := 1; i < len(ref); i++ { if ref[i].Value.IsGround() { cpy[i] = ref[i] } else { cpy[i] = ref[i].Copy() } } return cpy } // Equal returns true if ref is equal to other. func (ref Ref) Equal(other Value) bool { switch o := other.(type) { case Ref: if len(ref) == len(o) { for i := range ref { if !ref[i].Equal(o[i]) { return false } } return true } } return false } // Compare compares ref to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (ref Ref) Compare(other Value) int { if o, ok := other.(Ref); ok { return termSliceCompare(ref, o) } return Compare(ref, other) } // Find returns the current value or a "not found" error. func (ref Ref) Find(path Ref) (Value, error) { if len(path) == 0 { return ref, nil } return nil, errFindNotFound } // Hash returns the hash code for the Value. func (ref Ref) Hash() int { return termSliceHash(ref) } // HasPrefix returns true if the other ref is a prefix of this ref. func (ref Ref) HasPrefix(other Ref) bool { if len(other) > len(ref) { return false } for i := range other { if !ref[i].Equal(other[i]) { return false } } return true } // ConstantPrefix returns the constant portion of the ref starting from the head. func (ref Ref) ConstantPrefix() Ref { i := ref.Dynamic() if i < 0 { return ref } return ref[:i] } // StringPrefix returns the string portion of the ref starting from the head. func (ref Ref) StringPrefix() Ref { for i := 1; i < len(ref); i++ { switch ref[i].Value.(type) { case String: // pass default: // cut off return ref[:i] } } return ref } // GroundPrefix returns the ground portion of the ref starting from the head. By // definition, the head of the reference is always ground. func (ref Ref) GroundPrefix() Ref { for i := range ref { if i > 0 && !ref[i].IsGround() { return ref[:i] } } return ref } // DynamicSuffix returns the dynamic portion of the ref. // If the ref is not dynamic, nil is returned. func (ref Ref) DynamicSuffix() Ref { i := ref.Dynamic() if i < 0 { return nil } return ref[i:] } // IsGround returns true if all of the parts of the Ref are ground. func (ref Ref) IsGround() bool { if len(ref) < 2 { return true } return termSliceIsGround(ref[1:]) } // IsNested returns true if this ref contains other Refs. func (ref Ref) IsNested() bool { for _, x := range ref { if _, ok := x.Value.(Ref); ok { return true } } return false } // Ptr returns a slash-separated path string for this ref. If the ref // contains non-string terms this function returns an error. Path // components are escaped. func (ref Ref) Ptr() (string, error) { buf := &strings.Builder{} tail := ref[1:] l := max(len(tail)-1, 0) // number of '/' to add for i := range tail { str, ok := tail[i].Value.(String) if !ok { return "", errors.New("invalid path value type") } l += len(str) } buf.Grow(l) for i := range tail { if i > 0 { buf.WriteByte('/') } str := string(tail[i].Value.(String)) // Sadly, the url package does not expose an appender for this. buf.WriteString(url.PathEscape(str)) } return buf.String(), nil } // IsVarCompatibleString returns true if s is a valid variable name. String s is a valid variable // name if it starts with a letter (a-z or A-Z) or underscore (_) and is followed by // letters (a-z or A-Z), digits (0-9), and underscores. func IsVarCompatibleString(s string) bool { l := len(s) if l == 0 { return false } // not exactly easy on the eyes, but often orders of magnitude faster // than using a compiled regex (see benchmarks in term_bench_test.go) is_letter := func(c byte) bool { return (c > 96 && c < 123) || (c > 64 && c < 91) } is_digit := func(c byte) bool { return c > 47 && c < 58 } // first character must be a letter or underscore c := s[0] if !(is_letter(c) || c == 95) { return false } // remaining characters must be letters, digits, or underscores for i := 1; i < l; i++ { if c = s[i]; !(is_letter(c) || is_digit(c) || c == 95) { return false } } return true } func (ref Ref) String() string { l := len(ref) // First check for zero-alloc options, as making the buffer for AppendText // always costs an allocation. if l == 0 { return "" } if l == 1 { if s, ok := ref[0].Value.(String); ok { // Ref head should normally be a Var, but if for some reason // it's a string, don't quote it. return string(s) } return ref[0].Value.String() } if name, ok := BuiltinNameFromRef(ref); ok { return name } buf, _ := ref.AppendText(make([]byte, 0, ref.StringLength())) return util.ByteSliceToString(buf) } // OutputVars returns a VarSet containing variables that would be bound by evaluating // this expression in isolation. func (ref Ref) OutputVars() VarSet { vis := NewVarVisitor().WithParams(VarVisitorParams{SkipRefHead: true}) vis.WalkRef(ref) return vis.Vars() } func (ref Ref) toArray() *Array { terms := make([]*Term, 0, len(ref)) for _, term := range ref { if _, ok := term.Value.(String); ok { terms = append(terms, term) } else { terms = append(terms, InternedTerm(term.Value.String())) } } return NewArray(terms...) } // QueryIterator defines the interface for querying AST documents with references. type QueryIterator func(map[Var]Value, Value) error // ArrayTerm creates a new Term with an Array value. func ArrayTerm(a ...*Term) *Term { return NewTerm(NewArray(a...)) } // NewArray creates an Array with the terms provided. The array will // use the provided term slice. func NewArray(a ...*Term) *Array { hs := make([]int, len(a)) for i, e := range a { hs[i] = e.Value.Hash() } arr := &Array{elems: a, hashs: hs, ground: termSliceIsGround(a)} arr.rehash() return arr } // NewArrayWithCapacity returns a new empty Array with the given capacity pre-allocated. func NewArrayWithCapacity(capacity int) *Array { return &Array{ elems: make([]*Term, 0, capacity), hashs: make([]int, 0, capacity), ground: true, } } // Array represents an array as defined by the language. Arrays are similar to the // same types as defined by JSON with the exception that they can contain Vars // and References. type Array struct { elems []*Term hashs []int // element hashes hash int ground bool } // Copy returns a deep copy of arr. func (arr *Array) Copy() *Array { return &Array{ elems: termSliceCopy(arr.elems), hashs: slices.Clone(arr.hashs), hash: arr.hash, ground: arr.ground, } } // Equal returns true if arr is equal to other. func (arr *Array) Equal(other Value) bool { if arr == other { return true } if other, ok := other.(*Array); ok && len(arr.elems) == len(other.elems) { for i := range arr.elems { if !arr.elems[i].Equal(other.elems[i]) { return false } } return true } return false } // Compare compares arr to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (arr *Array) Compare(other Value) int { if b, ok := other.(*Array); ok { return termSliceCompare(arr.elems, b.elems) } sortA := sortOrder(arr) sortB := sortOrder(other) if sortA < sortB { return -1 } else if sortB < sortA { return 1 } return Compare(arr, other) } // Find returns the value at the index or an out-of-range error. func (arr *Array) Find(path Ref) (Value, error) { if len(path) == 0 { return arr, nil } num, ok := path[0].Value.(Number) if !ok { return nil, errFindNotFound } i, ok := num.Int() if !ok || i < 0 || i >= arr.Len() { return nil, errFindNotFound } term := arr.Elem(i) // Using Find on scalar values costs an allocation (type -> Value conversion) // and since we already have the Value here, we can avoid that. if len(path) == 1 && IsScalar(term.Value) { return term.Value, nil } return term.Value.Find(path[1:]) } // Get returns the element at pos or nil if not possible. func (arr *Array) Get(pos *Term) *Term { num, ok := pos.Value.(Number) if !ok { return nil } if i, ok := num.Int(); ok && i >= 0 && i < len(arr.elems) { return arr.elems[i] } return nil } // Sorted returns a new Array that contains the sorted elements of arr. func (arr *Array) Sorted() *Array { cpy := make([]*Term, len(arr.elems)) for i := range cpy { cpy[i] = arr.elems[i] } slices.SortFunc(cpy, TermValueCompare) a := NewArray(cpy...) a.hashs = arr.hashs return a } // Hash returns the hash code for the Value. func (arr *Array) Hash() int { return arr.hash } // IsGround returns true if all of the Array elements are ground. func (arr *Array) IsGround() bool { return arr.ground } // MarshalJSON returns JSON encoded bytes representing arr. func (arr *Array) MarshalJSON() ([]byte, error) { if len(arr.elems) == 0 { return []byte(`[]`), nil } return json.Marshal(arr.elems) } func (arr *Array) String() string { buf, _ := arr.AppendText(make([]byte, 0, arr.StringLength())) return util.ByteSliceToString(buf) } // Len returns the number of elements in the array. func (arr *Array) Len() int { return len(arr.elems) } // Elem returns the element i of arr. func (arr *Array) Elem(i int) *Term { return arr.elems[i] } // Set sets the element i of arr. func (arr *Array) Set(i int, v *Term) { arr.set(i, v) } // rehash updates the cached hash of arr. func (arr *Array) rehash() { arr.hash = 0 for _, h := range arr.hashs { arr.hash += h } } // set sets the element i of arr. func (arr *Array) set(i int, v *Term) { arr.ground = arr.ground && v.IsGround() arr.elems[i] = v arr.hashs[i] = v.Value.Hash() arr.rehash() } // Slice returns a slice of arr starting from i index to j. -1 // indicates the end of the array. The returned value array is not a // copy and any modifications to either of arrays may be reflected to // the other. func (arr *Array) Slice(i, j int) *Array { var elems []*Term var hashs []int if j == -1 { elems = arr.elems[i:] hashs = arr.hashs[i:] } else { elems = arr.elems[i:j] hashs = arr.hashs[i:j] } // If arr is ground, the slice is, too. // If it's not, the slice could still be. gr := arr.ground || termSliceIsGround(elems) s := &Array{elems: elems, hashs: hashs, ground: gr} s.rehash() return s } // Iter calls f on each element in arr. If f returns an error, // iteration stops and the return value is the error. func (arr *Array) Iter(f func(*Term) error) error { for i := range arr.elems { if err := f(arr.elems[i]); err != nil { return err } } return nil } // Until calls f on each element in arr. If f returns true, iteration stops. func (arr *Array) Until(f func(*Term) bool) bool { return slices.ContainsFunc(arr.elems, f) } // Foreach calls f on each element in arr. func (arr *Array) Foreach(f func(*Term)) { for _, term := range arr.elems { f(term) } } // Append appends a term to arr, returning the appended array. func (arr *Array) Append(v *Term) *Array { cpy := *arr cpy.elems = append(arr.elems, v) cpy.hashs = append(arr.hashs, v.Value.Hash()) cpy.hash = arr.hash + v.Value.Hash() cpy.ground = arr.ground && v.IsGround() return &cpy } // Set represents a set as defined by the language. type Set interface { Value Len() int Copy() Set Diff(Set) Set Intersect(Set) Set Union(Set) Set Add(*Term) Iter(func(*Term) error) error Until(func(*Term) bool) bool Foreach(func(*Term)) Contains(*Term) bool Map(func(*Term) (*Term, error)) (Set, error) Reduce(*Term, func(*Term, *Term) (*Term, error)) (*Term, error) Sorted() *Array Slice() []*Term } // NewSet returns a new Set containing t. func NewSet(t ...*Term) Set { s := newset(len(t)) for _, term := range t { s.insert(term, false) } return s } // NewSetWithCapacity returns a new empty Set with the given capacity pre-allocated. func NewSetWithCapacity(capacity int) Set { return newset(capacity) } func newset(n int) *set { var keys []*Term if n > 0 { keys = make([]*Term, 0, n) } return &set{ elems: make(map[int]*Term, n), keys: keys, hash: 0, ground: true, sortGuard: sync.Once{}, } } // SetTerm returns a new Term representing a set containing terms t. func SetTerm(t ...*Term) *Term { set := NewSet(t...) return &Term{ Value: set, } } type set struct { elems map[int]*Term keys []*Term hash int ground bool // Prevents race condition around sorting. // We can avoid (the allocation cost of) using a pointer here as all // methods of `set` use a pointer receiver, and the `sync.Once` value // is never copied. sortGuard sync.Once } // Copy returns a deep copy of s. func (s *set) Copy() Set { cpy := &set{ hash: s.hash, ground: s.ground, sortGuard: sync.Once{}, elems: make(map[int]*Term, len(s.elems)), keys: make([]*Term, 0, len(s.keys)), } for hash := range s.elems { cpy.elems[hash] = s.elems[hash].Copy() cpy.keys = append(cpy.keys, cpy.elems[hash]) } return cpy } // IsGround returns true if all terms in s are ground. func (s *set) IsGround() bool { return s.ground } // Hash returns a hash code for s. func (s *set) Hash() int { return s.hash } func (s *set) String() string { buf, _ := s.AppendText(make([]byte, 0, s.StringLength())) return util.ByteSliceToString(buf) } func (s *set) sortedKeys() []*Term { s.sortGuard.Do(func() { slices.SortFunc(s.keys, TermValueCompare) }) return s.keys } // Compare compares s to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (s *set) Compare(other Value) int { o1 := sortOrder(s) o2 := sortOrder(other) if o1 < o2 { return -1 } else if o1 > o2 { return 1 } t := other.(*set) return termSliceCompare(s.sortedKeys(), t.sortedKeys()) } // Find returns the set or dereferences the element itself. func (s *set) Find(path Ref) (Value, error) { if len(path) == 0 { return s, nil } if !s.Contains(path[0]) { return nil, errFindNotFound } return path[0].Value.Find(path[1:]) } // Diff returns elements in s that are not in other. func (s *set) Diff(other Set) Set { if s.Compare(other) == 0 { return NewSet() } result := newset(len(s.keys)) for _, term := range s.keys { if !other.Contains(term) { result.insert(term, false) } } return result } // Intersect returns the set containing elements in both s and other. func (s *set) Intersect(other Set) Set { o := other.(*set) n, m := s.Len(), o.Len() ss := s so := o if m < n { ss = o so = s n = m } result := newset(n) for _, term := range ss.keys { if so.Contains(term) { result.insert(term, false) } } return result } // Union returns the set containing all elements of s and other. func (s *set) Union(other Set) Set { o := other.(*set) // Pre-allocate with max size - avoids over-allocation for overlapping sets // while only requiring one potential grow for disjoint sets. r := newset(max(len(s.keys), len(o.keys))) for _, term := range s.keys { r.insert(term, false) } for _, term := range o.keys { r.insert(term, false) } return r } // Add updates s to include t. func (s *set) Add(t *Term) { s.insert(t, true) } // Iter calls f on each element in s. If f returns an error, iteration stops // and the return value is the error. func (s *set) Iter(f func(*Term) error) error { for _, term := range s.sortedKeys() { if err := f(term); err != nil { return err } } return nil } // Until calls f on each element in s. If f returns true, iteration stops. func (s *set) Until(f func(*Term) bool) bool { return slices.ContainsFunc(s.sortedKeys(), f) } // Foreach calls f on each element in s. func (s *set) Foreach(f func(*Term)) { for _, term := range s.sortedKeys() { f(term) } } // Map returns a new Set obtained by applying f to each value in s. func (s *set) Map(f func(*Term) (*Term, error)) (Set, error) { mapped := make([]*Term, 0, len(s.keys)) for _, x := range s.sortedKeys() { term, err := f(x) if err != nil { return nil, err } mapped = append(mapped, term) } return NewSet(mapped...), nil } // Reduce returns a Term produced by applying f to each value in s. The first // argument to f is the reduced value (starting with i) and the second argument // to f is the element in s. func (s *set) Reduce(i *Term, f func(*Term, *Term) (*Term, error)) (*Term, error) { err := s.Iter(func(x *Term) error { var err error i, err = f(i, x) if err != nil { return err } return nil }) return i, err } // Contains returns true if t is in s. func (s *set) Contains(t *Term) bool { return s.get(t) != nil } // Len returns the number of elements in the set. func (s *set) Len() int { return len(s.keys) } // MarshalJSON returns JSON encoded bytes representing s. func (s *set) MarshalJSON() ([]byte, error) { if s.keys == nil { return []byte(`[]`), nil } return json.Marshal(s.sortedKeys()) } // Sorted returns an Array that contains the sorted elements of s. func (s *set) Sorted() *Array { cpy := make([]*Term, len(s.keys)) copy(cpy, s.sortedKeys()) return NewArray(cpy...) } // Slice returns a slice of terms contained in the set. func (s *set) Slice() []*Term { return s.sortedKeys() } // NOTE(philipc): We assume a many-readers, single-writer model here. // This method should NOT be used concurrently, or else we risk data races. func (s *set) insert(x *Term, resetSortGuard bool) { hash := x.Hash() insertHash := hash for curr, ok := s.elems[insertHash]; ok; { if KeyHashEqual(curr.Value, x.Value) { return } insertHash++ curr, ok = s.elems[insertHash] } s.elems[insertHash] = x // O(1) insertion, but we'll have to re-sort the keys later. s.keys = append(s.keys, x) if resetSortGuard { // Reset the sync.Once instance. // See https://github.com/golang/go/issues/25955 for why we do it this way. // Note that this will always be the case when external code calls insert via // Add, or otherwise. Internal code may however benefit from not having to // re-create this pointer when it's known not to be needed. s.sortGuard = sync.Once{} } s.hash += hash s.ground = s.ground && x.IsGround() } func (s *set) get(x *Term) *Term { if len(s.elems) == 0 { return nil } hash := x.Hash() for curr, ok := s.elems[hash]; ok; { // Pointer equality check first if curr == x { return curr } if KeyHashEqual(curr.Value, x.Value) { return curr } hash++ curr, ok = s.elems[hash] } return nil } // Object represents an object as defined by the language. type Object interface { Value Len() int Get(*Term) *Term Copy() Object Insert(*Term, *Term) Iter(func(*Term, *Term) error) error Until(func(*Term, *Term) bool) bool Foreach(func(*Term, *Term)) Map(func(*Term, *Term) (*Term, *Term, error)) (Object, error) Diff(other Object) Object Intersect(other Object) [][3]*Term Merge(other Object) (Object, bool) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) Filter(filter Object) (Object, error) Keys() []*Term KeysIterator() ObjectKeysIterator get(k *Term) *objectElem // To prevent external implementations } // NewObject creates a new Object with t. func NewObject(t ...[2]*Term) Object { obj := newobject(len(t)) for i := range t { obj.insert(t[i][0], t[i][1], false) } return obj } // NewObjectWithCapacity returns a new empty Object with the given capacity pre-allocated. func NewObjectWithCapacity(capacity int) Object { return newobject(capacity) } // ObjectTerm creates a new Term with an Object value. func ObjectTerm(o ...[2]*Term) *Term { return &Term{Value: NewObject(o...)} } func LazyObject(blob map[string]any) Object { return &lazyObj{native: blob, cache: map[string]Value{}} } type lazyObj struct { strict Object cache map[string]Value native map[string]any } func (l *lazyObj) force() Object { if l.strict == nil { l.strict = MustInterfaceToValue(l.native).(Object) // NOTE(jf): a possible performance improvement here would be to check how many // entries have been realized to AST in the cache, and if some threshold compared to the // total number of keys is exceeded, realize the remaining entries and set l.strict to l.cache. l.cache = map[string]Value{} // We don't need the cache anymore; drop it to free up memory. } return l.strict } func (l *lazyObj) Compare(other Value) int { o1 := sortOrder(l) o2 := sortOrder(other) if o1 < o2 { return -1 } else if o2 < o1 { return 1 } return l.force().Compare(other) } func (l *lazyObj) Copy() Object { return l } func (l *lazyObj) Diff(other Object) Object { return l.force().Diff(other) } func (l *lazyObj) Intersect(other Object) [][3]*Term { return l.force().Intersect(other) } func (l *lazyObj) Iter(f func(*Term, *Term) error) error { return l.force().Iter(f) } func (l *lazyObj) Until(f func(*Term, *Term) bool) bool { // NOTE(sr): there could be benefits in not forcing here -- if we abort because // `f` returns true, we could save us from converting the rest of the object. return l.force().Until(f) } func (l *lazyObj) Foreach(f func(*Term, *Term)) { l.force().Foreach(f) } func (l *lazyObj) Filter(filter Object) (Object, error) { return l.force().Filter(filter) } func (l *lazyObj) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { return l.force().Map(f) } func (l *lazyObj) MarshalJSON() ([]byte, error) { return l.force().(*object).MarshalJSON() } func (l *lazyObj) Merge(other Object) (Object, bool) { return l.force().Merge(other) } func (l *lazyObj) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { return l.force().MergeWith(other, conflictResolver) } func (l *lazyObj) Len() int { return len(l.native) } func (l *lazyObj) String() string { return l.force().String() } // get is merely there to implement the Object interface -- `get` there serves the // purpose of prohibiting external implementations. It's never called for lazyObj. func (*lazyObj) get(*Term) *objectElem { return nil } func (l *lazyObj) Get(k *Term) *Term { if l.strict != nil { return l.strict.Get(k) } if s, ok := k.Value.(String); ok { if v, ok := l.cache[string(s)]; ok { return NewTerm(v) } if val, ok := l.native[string(s)]; ok { var converted Value switch val := val.(type) { case map[string]any: converted = LazyObject(val) default: converted = MustInterfaceToValue(val) } l.cache[string(s)] = converted return NewTerm(converted) } } return nil } func (l *lazyObj) Insert(k, v *Term) { l.force().Insert(k, v) } func (*lazyObj) IsGround() bool { return true } func (l *lazyObj) Hash() int { return l.force().Hash() } func (l *lazyObj) Keys() []*Term { if l.strict != nil { return l.strict.Keys() } ret := make([]*Term, 0, len(l.native)) for k := range l.native { ret = append(ret, StringTerm(k)) } slices.SortFunc(ret, TermValueCompare) return ret } func (l *lazyObj) KeysIterator() ObjectKeysIterator { return &lazyObjKeysIterator{keys: l.Keys()} } type lazyObjKeysIterator struct { current int keys []*Term } func (ki *lazyObjKeysIterator) Next() (*Term, bool) { if ki.current == len(ki.keys) { return nil, false } ki.current++ return ki.keys[ki.current-1], true } func (l *lazyObj) Find(path Ref) (Value, error) { if l.strict != nil { return l.strict.Find(path) } if len(path) == 0 { return l, nil } if p0, ok := path[0].Value.(String); ok { if v, ok := l.cache[string(p0)]; ok { return v.Find(path[1:]) } if v, ok := l.native[string(p0)]; ok { var converted Value switch v := v.(type) { case map[string]any: converted = LazyObject(v) default: converted = MustInterfaceToValue(v) } l.cache[string(p0)] = converted return converted.Find(path[1:]) } } return nil, errFindNotFound } type object struct { elems map[int]*objectElem keys []*objectElem ground int // number of key and value grounds. Counting is required to support insert's key-value replace. hash int sortGuard sync.Once // Prevents race condition around sorting. } func newobject(n int) *object { var keys []*objectElem if n > 0 { keys = make([]*objectElem, 0, n) } return &object{ elems: make(map[int]*objectElem, n), keys: keys, sortGuard: sync.Once{}, } } type objectElem struct { key *Term value *Term next *objectElem } // Item is a helper for constructing an tuple containing two Terms // representing a key/value pair in an Object. func Item(key, value *Term) [2]*Term { return [2]*Term{key, value} } func (obj *object) sortedKeys() []*objectElem { obj.sortGuard.Do(func() { slices.SortFunc(obj.keys, func(a, b *objectElem) int { return a.key.Value.Compare(b.key.Value) }) }) return obj.keys } // Compare compares obj to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (obj *object) Compare(other Value) int { if x, ok := other.(*lazyObj); ok { other = x.force() } o1 := sortOrder(obj) o2 := sortOrder(other) if o1 < o2 { return -1 } else if o2 < o1 { return 1 } a := obj b := other.(*object) // Ensure that keys are in canonical sorted order before use! akeys := a.sortedKeys() bkeys := b.sortedKeys() minLen := len(akeys) if len(b.keys) < len(akeys) { minLen = len(bkeys) } for i := range minLen { keysCmp := Compare(akeys[i].key, bkeys[i].key) if keysCmp < 0 { return -1 } if keysCmp > 0 { return 1 } valA := akeys[i].value valB := bkeys[i].value valCmp := Compare(valA, valB) if valCmp != 0 { return valCmp } } if len(akeys) < len(bkeys) { return -1 } if len(bkeys) < len(akeys) { return 1 } return 0 } // Find returns the value at the key or undefined. func (obj *object) Find(path Ref) (Value, error) { if len(path) == 0 { return obj, nil } term := obj.Get(path[0]) if term == nil { return nil, errFindNotFound } // Using Find on scalar values costs an allocation (type -> Value conversion) // and since we already have the Value here, we can avoid that. if len(path) == 1 && IsScalar(term.Value) { return term.Value, nil } return term.Value.Find(path[1:]) } func (obj *object) Insert(k, v *Term) { obj.insert(k, v, true) } // Get returns the value of k in obj if k exists, otherwise nil. func (obj *object) Get(k *Term) *Term { if len(obj.elems) == 0 { return nil } hash := k.Hash() for curr := obj.elems[hash]; curr != nil; curr = curr.next { // Pointer equality check always fastest, and not too unlikely with interning. if curr.key == k { return curr.value } if KeyHashEqual(curr.key.Value, k.Value) { return curr.value } } return nil } func KeyHashEqual(x, y Value) bool { switch x := x.(type) { case Null, Boolean, String, Var: return x == y case Number: if y, ok := y.(Number); ok { return x.Equal(y) } } return Compare(x, y) == 0 } // Hash returns the hash code for the Value. func (obj *object) Hash() int { return obj.hash } // IsGround returns true if all of the Object key/value pairs are ground. func (obj *object) IsGround() bool { return obj.ground == 2*len(obj.keys) } // Copy returns a deep copy of obj. func (obj *object) Copy() Object { cpy, _ := obj.Map(func(k, v *Term) (*Term, *Term, error) { return k.Copy(), v.Copy(), nil }) cpy.(*object).hash = obj.hash return cpy } // Diff returns a new Object that contains only the key/value pairs that exist in obj. func (obj *object) Diff(other Object) Object { r := newobject(obj.Len()) for _, node := range obj.sortedKeys() { if other.Get(node.key) == nil { r.insert(node.key, node.value, false) } } return r } // Intersect returns a slice of term triplets that represent the intersection of keys // between obj and other. For each intersecting key, the values from obj and other are included // as the last two terms in the triplet (respectively). func (obj *object) Intersect(other Object) [][3]*Term { r := [][3]*Term{} obj.Foreach(func(k, v *Term) { if v2 := other.Get(k); v2 != nil { r = append(r, [3]*Term{k, v, v2}) } }) return r } // Iter calls the function f for each key-value pair in the object. If f // returns an error, iteration stops and the error is returned. func (obj *object) Iter(f func(*Term, *Term) error) error { for _, node := range obj.sortedKeys() { if err := f(node.key, node.value); err != nil { return err } } return nil } // Until calls f for each key-value pair in the object. If f returns // true, iteration stops and Until returns true. Otherwise, return // false. func (obj *object) Until(f func(*Term, *Term) bool) bool { for _, node := range obj.sortedKeys() { if f(node.key, node.value) { return true } } return false } // Foreach calls f for each key-value pair in the object. func (obj *object) Foreach(f func(*Term, *Term)) { for _, node := range obj.sortedKeys() { f(node.key, node.value) } } // Map returns a new Object constructed by mapping each element in the object // using the function f. If f returns an error, the error is returned by Map. // If f return a nil key, the element is skipped. func (obj *object) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { cpy := newobject(obj.Len()) for _, node := range obj.sortedKeys() { k, v, err := f(node.key, node.value) if err != nil { return nil, err } if k != nil { cpy.insert(k, v, false) } } return cpy, nil } // Keys returns the keys of obj. func (obj *object) Keys() []*Term { keys := make([]*Term, len(obj.keys)) for i, elem := range obj.sortedKeys() { keys[i] = elem.key } return keys } // Returns an iterator over the obj's keys. func (obj *object) KeysIterator() ObjectKeysIterator { return newobjectKeysIterator(obj) } // MarshalJSON returns JSON encoded bytes representing obj. func (obj *object) MarshalJSON() ([]byte, error) { sl := make([][2]*Term, obj.Len()) for i, node := range obj.sortedKeys() { sl[i] = Item(node.key, node.value) } return json.Marshal(sl) } // Merge returns a new Object containing the non-overlapping keys of obj and other. If there are // overlapping keys between obj and other, the values of associated with the keys are merged. Only // objects can be merged with other objects. If the values cannot be merged, the second turn value // will be false. func (obj *object) Merge(other Object) (Object, bool) { return obj.MergeWith(other, func(v1, v2 *Term) (*Term, bool) { obj1, ok1 := v1.Value.(Object) obj2, ok2 := v2.Value.(Object) if !ok1 || !ok2 { return nil, true } obj3, ok := obj1.Merge(obj2) if !ok { return nil, true } return NewTerm(obj3), false }) } // MergeWith returns a new Object containing the merged keys of obj and other. // If there are overlapping keys between obj and other, the conflictResolver // is called. The conflictResolver can return a merged value and a boolean // indicating if the merge has failed and should stop. func (obj *object) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { // Might overallocate assuming no conflicts is the common case, // but that's typically faster than iterating over each object twice. result := newobject(obj.Len() + other.Len()) stop := obj.Until(func(k, v *Term) bool { v2 := other.Get(k) // The key didn't exist in other, keep the original value if v2 == nil { result.insert(k, v, false) return false } // The key exists in both, resolve the conflict if possible merged, stop := conflictResolver(v, v2) if !stop { result.insert(k, merged, false) } return stop }) if stop { return nil, false } // Copy in any values from other for keys that don't exist in obj other.Foreach(func(k, v *Term) { if v2 := obj.Get(k); v2 == nil { result.insert(k, v, false) } }) return result, true } // Filter returns a new object from values in obj where the keys are // found in filter. Array indices for values can be specified as // number strings. func (obj *object) Filter(filter Object) (Object, error) { filtered, err := filterObject(obj, filter) if err != nil { return nil, err } return filtered.(Object), nil } // Len returns the number of elements in the object. func (obj *object) Len() int { return len(obj.keys) } func (obj *object) String() string { buf, _ := obj.AppendText(make([]byte, 0, obj.StringLength())) return util.ByteSliceToString(buf) } func (*object) get(*Term) *objectElem { return nil } // NOTE(philipc): We assume a many-readers, single-writer model here. // This method should NOT be used concurrently, or else we risk data races. func (obj *object) insert(k, v *Term, resetSortGuard bool) { hash := k.Hash() head := obj.elems[hash] for curr := head; curr != nil; curr = curr.next { if KeyHashEqual(curr.key.Value, k.Value) { if curr.value.IsGround() { obj.ground-- } if v.IsGround() { obj.ground++ } // Update hash based on the new value curr.value = v obj.elems[hash] = curr obj.hash = 0 for ehash := range obj.elems { obj.hash += ehash + obj.elems[ehash].value.Hash() } return } } obj.elems[hash] = &objectElem{key: k, value: v, next: head} // O(1) insertion, but we'll have to re-sort the keys later. obj.keys = append(obj.keys, obj.elems[hash]) if resetSortGuard { // Reset the sync.Once instance. // See https://github.com/golang/go/issues/25955 for why we do it this way. // Note that this will always be the case when external code calls insert via // Add, or otherwise. Internal code may however benefit from not having to // re-create this when it's known not to be needed. obj.sortGuard = sync.Once{} } obj.hash += hash + v.Hash() if k.IsGround() { obj.ground++ } if v.IsGround() { obj.ground++ } } func filterObject(o Value, filter Value) (Value, error) { if (Null{}).Equal(filter) { return o, nil } filteredObj, ok := filter.(*object) if !ok { return nil, fmt.Errorf("invalid filter value %q, expected an object", filter) } switch v := o.(type) { case String, Number, Boolean, Null: return o, nil case *Array: values := make([]*Term, 0, v.Len()) for i := range v.Len() { subFilter := filteredObj.Get(InternedIntegerString(i)) if subFilter != nil { filteredValue, err := filterObject(v.Elem(i).Value, subFilter.Value) if err != nil { return nil, err } values = append(values, NewTerm(filteredValue)) } } return NewArray(values...), nil case Set: terms := make([]*Term, 0, v.Len()) for _, t := range v.Slice() { if filteredObj.Get(t) != nil { filteredValue, err := filterObject(t.Value, filteredObj.Get(t).Value) if err != nil { return nil, err } terms = append(terms, NewTerm(filteredValue)) } } return NewSet(terms...), nil case *object: values := NewObject() iterObj := v other := filteredObj if v.Len() < filteredObj.Len() { iterObj = filteredObj other = v } err := iterObj.Iter(func(key *Term, _ *Term) error { if other.Get(key) != nil { filteredValue, err := filterObject(v.Get(key).Value, filteredObj.Get(key).Value) if err != nil { return err } values.Insert(key, NewTerm(filteredValue)) } return nil }) return values, err default: return nil, fmt.Errorf("invalid object value type %q", v) } } // NOTE(philipc): The only way to get an ObjectKeyIterator should be // from an Object. This ensures that the iterator can have implementation- // specific details internally, with no contracts except to the very // limited interface. type ObjectKeysIterator interface { Next() (*Term, bool) } type objectKeysIterator struct { obj *object numKeys int index int } func newobjectKeysIterator(o *object) ObjectKeysIterator { return &objectKeysIterator{ obj: o, numKeys: o.Len(), index: 0, } } func (oki *objectKeysIterator) Next() (*Term, bool) { if oki.index == oki.numKeys || oki.numKeys == 0 { return nil, false } oki.index++ return oki.obj.sortedKeys()[oki.index-1].key, true } // ArrayComprehension represents an array comprehension as defined in the language. type ArrayComprehension struct { Term *Term `json:"term"` Body Body `json:"body"` } // ArrayComprehensionTerm creates a new Term with an ArrayComprehension value. func ArrayComprehensionTerm(term *Term, body Body) *Term { return &Term{ Value: &ArrayComprehension{ Term: term, Body: body, }, } } // Copy returns a deep copy of ac. func (ac *ArrayComprehension) Copy() *ArrayComprehension { cpy := *ac cpy.Body = ac.Body.Copy() cpy.Term = ac.Term.Copy() return &cpy } // Equal returns true if ac is equal to other. func (ac *ArrayComprehension) Equal(other Value) bool { return Compare(ac, other) == 0 } // Compare compares ac to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (ac *ArrayComprehension) Compare(other Value) int { return Compare(ac, other) } // Find returns the current value or a not found error. func (ac *ArrayComprehension) Find(path Ref) (Value, error) { if len(path) == 0 { return ac, nil } return nil, errFindNotFound } // Hash returns the hash code of the Value. func (ac *ArrayComprehension) Hash() int { return ac.Term.Hash() + ac.Body.Hash() } // IsGround returns true if the Term and Body are ground. func (ac *ArrayComprehension) IsGround() bool { return ac.Term.IsGround() && ac.Body.IsGround() } func (ac *ArrayComprehension) String() string { buf, _ := ac.AppendText(make([]byte, 0, ac.StringLength())) return util.ByteSliceToString(buf) } // ObjectComprehension represents an object comprehension as defined in the language. type ObjectComprehension struct { Key *Term `json:"key"` Value *Term `json:"value"` Body Body `json:"body"` } // ObjectComprehensionTerm creates a new Term with an ObjectComprehension value. func ObjectComprehensionTerm(key, value *Term, body Body) *Term { return &Term{ Value: &ObjectComprehension{ Key: key, Value: value, Body: body, }, } } // Copy returns a deep copy of oc. func (oc *ObjectComprehension) Copy() *ObjectComprehension { cpy := *oc cpy.Body = oc.Body.Copy() cpy.Key = oc.Key.Copy() cpy.Value = oc.Value.Copy() return &cpy } // Equal returns true if oc is equal to other. func (oc *ObjectComprehension) Equal(other Value) bool { return Compare(oc, other) == 0 } // Compare compares oc to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (oc *ObjectComprehension) Compare(other Value) int { return Compare(oc, other) } // Find returns the current value or a not found error. func (oc *ObjectComprehension) Find(path Ref) (Value, error) { if len(path) == 0 { return oc, nil } return nil, errFindNotFound } // Hash returns the hash code of the Value. func (oc *ObjectComprehension) Hash() int { return oc.Key.Hash() + oc.Value.Hash() + oc.Body.Hash() } // IsGround returns true if the Key, Value and Body are ground. func (oc *ObjectComprehension) IsGround() bool { return oc.Key.IsGround() && oc.Value.IsGround() && oc.Body.IsGround() } func (oc *ObjectComprehension) String() string { buf, _ := oc.AppendText(make([]byte, 0, oc.StringLength())) return util.ByteSliceToString(buf) } // SetComprehension represents a set comprehension as defined in the language. type SetComprehension struct { Term *Term `json:"term"` Body Body `json:"body"` } // SetComprehensionTerm creates a new Term with an SetComprehension value. func SetComprehensionTerm(term *Term, body Body) *Term { return &Term{ Value: &SetComprehension{ Term: term, Body: body, }, } } // Copy returns a deep copy of sc. func (sc *SetComprehension) Copy() *SetComprehension { cpy := *sc cpy.Body = sc.Body.Copy() cpy.Term = sc.Term.Copy() return &cpy } // Equal returns true if sc is equal to other. func (sc *SetComprehension) Equal(other Value) bool { return Compare(sc, other) == 0 } // Compare compares sc to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (sc *SetComprehension) Compare(other Value) int { return Compare(sc, other) } // Find returns the current value or a not found error. func (sc *SetComprehension) Find(path Ref) (Value, error) { if len(path) == 0 { return sc, nil } return nil, errFindNotFound } // Hash returns the hash code of the Value. func (sc *SetComprehension) Hash() int { return sc.Term.Hash() + sc.Body.Hash() } // IsGround returns true if the Term and Body are ground. func (sc *SetComprehension) IsGround() bool { return sc.Term.IsGround() && sc.Body.IsGround() } func (sc *SetComprehension) String() string { buf, _ := sc.AppendText(make([]byte, 0, sc.StringLength())) return util.ByteSliceToString(buf) } // Call represents as function call in the language. type Call []*Term // CallTerm returns a new Term with a Call value defined by terms. The first // term is the operator and the rest are operands. func CallTerm(terms ...*Term) *Term { return NewTerm(Call(terms)) } // Copy returns a deep copy of c. func (c Call) Copy() Call { return termSliceCopy(c) } // Compare compares c to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (c Call) Compare(other Value) int { return Compare(c, other) } // Find returns the current value or a not found error. func (Call) Find(Ref) (Value, error) { return nil, errFindNotFound } // Hash returns the hash code for the Value. func (c Call) Hash() int { return termSliceHash(c) } // IsGround returns true if the Value is ground. func (c Call) IsGround() bool { return termSliceIsGround(c) } // MakeExpr returns a new Expr from this call. func (c Call) MakeExpr(output *Term) *Expr { terms := []*Term(c) return NewExpr(append(terms, output)) } func (c Call) Operator() Ref { if len(c) == 0 { return nil } return c[0].Value.(Ref) } func (c Call) Operands() []*Term { if len(c) < 1 { return nil } return c[1:] } func (c Call) String() string { buf, _ := c.AppendText(make([]byte, 0, c.StringLength())) return util.ByteSliceToString(buf) } func termSliceCopy(a []*Term) []*Term { cpy := make([]*Term, len(a)) for i := range a { cpy[i] = a[i].Copy() } return cpy } func termSliceEqual(a, b []*Term) bool { if len(a) == len(b) { for i := range a { if !a[i].Equal(b[i]) { return false } } return true } return false } func termSliceHash(a []*Term) int { var hash int for _, v := range a { hash += v.Value.Hash() } return hash } func termSliceIsGround(a []*Term) bool { for _, v := range a { if !v.IsGround() { return false } } return true } // Detect when String() need to use expensive JSON‐escaped form func isControlOrBackslash(r rune) bool { return r == '\\' || unicode.IsControl(r) } // NOTE(tsandall): The unmarshalling errors in these functions are not // helpful for callers because they do not identify the source of the // unmarshalling error. Because OPA doesn't accept JSON describing ASTs // from callers, this is acceptable (for now). If that changes in the future, // the error messages should be revisited. The current approach focuses // on the happy path and treats all errors the same. If better error // reporting is needed, the error paths will need to be fleshed out. func unmarshalBody(b []any) (Body, error) { buf := Body{} for _, e := range b { if m, ok := e.(map[string]any); ok { expr := &Expr{} if err := unmarshalExpr(expr, m); err == nil { buf = append(buf, expr) continue } } goto unmarshal_error } return buf, nil unmarshal_error: return nil, errors.New("ast: unable to unmarshal body") } func unmarshalExpr(expr *Expr, v map[string]any) error { if x, ok := v["negated"]; ok { if b, ok := x.(bool); ok { expr.Negated = b } else { return fmt.Errorf("ast: unable to unmarshal negated field with type: %T (expected true or false)", v["negated"]) } } if generatedRaw, ok := v["generated"]; ok { if b, ok := generatedRaw.(bool); ok { expr.Generated = b } else { return fmt.Errorf("ast: unable to unmarshal generated field with type: %T (expected true or false)", v["generated"]) } } if err := unmarshalExprIndex(expr, v); err != nil { return err } switch ts := v["terms"].(type) { case map[string]any: t, err := unmarshalTerm(ts) if err != nil { return err } expr.Terms = t case []any: terms, err := unmarshalTermSlice(ts) if err != nil { return err } expr.Terms = terms default: return fmt.Errorf(`ast: unable to unmarshal terms field with type: %T (expected {"value": ..., "type": ...} or [{"value": ..., "type": ...}, ...])`, v["terms"]) } if x, ok := v["with"]; ok { if sl, ok := x.([]any); ok { ws := make([]*With, len(sl)) for i := range sl { var err error ws[i], err = unmarshalWith(sl[i]) if err != nil { return err } } expr.With = ws } } if loc, ok := v["location"].(map[string]any); ok { expr.Location = &Location{} if err := unmarshalLocation(expr.Location, loc); err != nil { return err } } return nil } func unmarshalLocation(loc *Location, v map[string]any) error { if x, ok := v["file"]; ok { if s, ok := x.(string); ok { loc.File = s } else { return fmt.Errorf("ast: unable to unmarshal file field with type: %T (expected string)", v["file"]) } } if x, ok := v["row"]; ok { if n, ok := x.(json.Number); ok { i64, err := n.Int64() if err != nil { return err } loc.Row = int(i64) } else { return fmt.Errorf("ast: unable to unmarshal row field with type: %T (expected number)", v["row"]) } } if x, ok := v["col"]; ok { if n, ok := x.(json.Number); ok { i64, err := n.Int64() if err != nil { return err } loc.Col = int(i64) } else { return fmt.Errorf("ast: unable to unmarshal col field with type: %T (expected number)", v["col"]) } } return nil } func unmarshalExprIndex(expr *Expr, v map[string]any) error { if x, ok := v["index"]; ok { if n, ok := x.(json.Number); ok { i, err := n.Int64() if err == nil { expr.Index = int(i) return nil } } } return fmt.Errorf("ast: unable to unmarshal index field with type: %T (expected integer)", v["index"]) } func unmarshalTerm(m map[string]any) (*Term, error) { var term Term v, err := unmarshalValue(m) if err != nil { return nil, err } term.Value = v if loc, ok := m["location"].(map[string]any); ok { term.Location = &Location{} if err := unmarshalLocation(term.Location, loc); err != nil { return nil, err } } return &term, nil } func unmarshalTermSlice(s []any) ([]*Term, error) { buf := []*Term{} for _, x := range s { if m, ok := x.(map[string]any); ok { t, err := unmarshalTerm(m) if err == nil { buf = append(buf, t) continue } return nil, err } return nil, errors.New("ast: unable to unmarshal term") } return buf, nil } func unmarshalTermSliceValue(d map[string]any) ([]*Term, error) { if s, ok := d["value"].([]any); ok { return unmarshalTermSlice(s) } return nil, errors.New(`ast: unable to unmarshal term (expected {"value": [...], "type": ...} where type is one of: ref, array, or set)`) } func unmarshalWith(i any) (*With, error) { if m, ok := i.(map[string]any); ok { tgt, _ := m["target"].(map[string]any) target, err := unmarshalTerm(tgt) if err == nil { val, _ := m["value"].(map[string]any) value, err := unmarshalTerm(val) if err == nil { return &With{ Target: target, Value: value, }, nil } return nil, err } return nil, err } return nil, errors.New(`ast: unable to unmarshal with modifier (expected {"target": {...}, "value": {...}})`) } func unmarshalValue(d map[string]any) (Value, error) { v := d["value"] switch d["type"] { case "null": return NullValue, nil case "boolean": if b, ok := v.(bool); ok { return Boolean(b), nil } case "number": if n, ok := v.(json.Number); ok { return Number(n), nil } case "string": if s, ok := v.(string); ok { return String(s), nil } case "var": if s, ok := v.(string); ok { return Var(s), nil } case "ref": if s, err := unmarshalTermSliceValue(d); err == nil { return Ref(s), nil } case "array": if s, err := unmarshalTermSliceValue(d); err == nil { return NewArray(s...), nil } case "set": if s, err := unmarshalTermSliceValue(d); err == nil { return NewSet(s...), nil } case "object": if s, ok := v.([]any); ok { buf := NewObject() for _, x := range s { if i, ok := x.([]any); ok && len(i) == 2 { p, err := unmarshalTermSlice(i) if err == nil { buf.Insert(p[0], p[1]) continue } } goto unmarshal_error } return buf, nil } case "arraycomprehension", "setcomprehension": if m, ok := v.(map[string]any); ok { t, ok := m["term"].(map[string]any) if !ok { goto unmarshal_error } term, err := unmarshalTerm(t) if err != nil { goto unmarshal_error } b, ok := m["body"].([]any) if !ok { goto unmarshal_error } body, err := unmarshalBody(b) if err != nil { goto unmarshal_error } if d["type"] == "arraycomprehension" { return &ArrayComprehension{Term: term, Body: body}, nil } return &SetComprehension{Term: term, Body: body}, nil } case "objectcomprehension": if m, ok := v.(map[string]any); ok { k, ok := m["key"].(map[string]any) if !ok { goto unmarshal_error } key, err := unmarshalTerm(k) if err != nil { goto unmarshal_error } v, ok := m["value"].(map[string]any) if !ok { goto unmarshal_error } value, err := unmarshalTerm(v) if err != nil { goto unmarshal_error } b, ok := m["body"].([]any) if !ok { goto unmarshal_error } body, err := unmarshalBody(b) if err != nil { goto unmarshal_error } return &ObjectComprehension{Key: key, Value: value, Body: body}, nil } case "call": if s, err := unmarshalTermSliceValue(d); err == nil { return Call(s), nil } } unmarshal_error: return nil, errors.New("ast: unable to unmarshal term") }