Files
Johan Fylling 5464b005e8 Bumping golangci-lint to v1.59.1 (#6817)
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
2024-06-19 15:13:43 +02:00

1204 lines
26 KiB
Go

// Copyright 2017 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 types declares data types for Rego values and helper functions to
// operate on these types.
package types
import (
"encoding/json"
"fmt"
"sort"
"strings"
"github.com/open-policy-agent/opa/util"
)
// Sprint returns the string representation of the type.
func Sprint(x Type) string {
if x == nil {
return "???"
}
return x.String()
}
// Type represents a type of a term in the language.
type Type interface {
String() string
typeMarker() string
json.Marshaler
}
func (Null) typeMarker() string { return typeNull }
func (Boolean) typeMarker() string { return typeBoolean }
func (Number) typeMarker() string { return typeNumber }
func (String) typeMarker() string { return typeString }
func (*Array) typeMarker() string { return typeArray }
func (*Object) typeMarker() string { return typeObject }
func (*Set) typeMarker() string { return typeSet }
func (Any) typeMarker() string { return typeAny }
func (Function) typeMarker() string { return typeFunction }
// Null represents the null type.
type Null struct{}
// NewNull returns a new Null type.
func NewNull() Null {
return Null{}
}
// NamedType represents a type alias with an arbitrary name and description.
// This is useful for generating documentation for built-in functions.
type NamedType struct {
Name, Descr string
Type Type
}
func (n *NamedType) typeMarker() string { return n.Type.typeMarker() }
func (n *NamedType) String() string { return n.Name + ": " + n.Type.String() }
func (n *NamedType) MarshalJSON() ([]byte, error) {
var obj map[string]interface{}
switch x := n.Type.(type) {
case interface{ toMap() map[string]interface{} }:
obj = x.toMap()
default:
obj = map[string]interface{}{
"type": n.Type.typeMarker(),
}
}
obj["name"] = n.Name
if n.Descr != "" {
obj["description"] = n.Descr
}
return json.Marshal(obj)
}
func (n *NamedType) Description(d string) *NamedType {
n.Descr = d
return n
}
// Named returns the passed type as a named type.
// Named types are only valid at the top level of built-in functions.
// Note that nested named types cause panic.
func Named(name string, t Type) *NamedType {
return &NamedType{
Type: t,
Name: name,
}
}
// MarshalJSON returns the JSON encoding of t.
func (t Null) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"type": t.typeMarker(),
})
}
func unwrap(t Type) Type {
switch t := t.(type) {
case *NamedType:
return t.Type
default:
return t
}
}
func (t Null) String() string {
return typeNull
}
// Boolean represents the boolean type.
type Boolean struct{}
// B represents an instance of the boolean type.
var B = NewBoolean()
// NewBoolean returns a new Boolean type.
func NewBoolean() Boolean {
return Boolean{}
}
// MarshalJSON returns the JSON encoding of t.
func (t Boolean) MarshalJSON() ([]byte, error) {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
return json.Marshal(repr)
}
func (t Boolean) String() string {
return t.typeMarker()
}
// String represents the string type.
type String struct{}
// S represents an instance of the string type.
var S = NewString()
// NewString returns a new String type.
func NewString() String {
return String{}
}
// MarshalJSON returns the JSON encoding of t.
func (t String) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"type": t.typeMarker(),
})
}
func (String) String() string {
return typeString
}
// Number represents the number type.
type Number struct{}
// N represents an instance of the number type.
var N = NewNumber()
// NewNumber returns a new Number type.
func NewNumber() Number {
return Number{}
}
// MarshalJSON returns the JSON encoding of t.
func (t Number) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"type": t.typeMarker(),
})
}
func (Number) String() string {
return typeNumber
}
// Array represents the array type.
type Array struct {
static []Type // static items
dynamic Type // dynamic items
}
// NewArray returns a new Array type.
func NewArray(static []Type, dynamic Type) *Array {
return &Array{
static: static,
dynamic: dynamic,
}
}
// MarshalJSON returns the JSON encoding of t.
func (t *Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.toMap())
}
func (t *Array) toMap() map[string]interface{} {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
if len(t.static) != 0 {
repr["static"] = t.static
}
if t.dynamic != nil {
repr["dynamic"] = t.dynamic
}
return repr
}
func (t *Array) String() string {
prefix := "array"
buf := []string{}
for _, tpe := range t.static {
buf = append(buf, Sprint(tpe))
}
repr := prefix
if len(buf) > 0 {
repr += "<" + strings.Join(buf, ", ") + ">"
}
if t.dynamic != nil {
repr += "[" + t.dynamic.String() + "]"
}
return repr
}
// Dynamic returns the type of the array's dynamic elements.
func (t *Array) Dynamic() Type {
return t.dynamic
}
// Len returns the number of static array elements.
func (t *Array) Len() int {
return len(t.static)
}
// Select returns the type of element at the zero-based pos.
func (t *Array) Select(pos int) Type {
if pos >= 0 {
if len(t.static) > pos {
return t.static[pos]
}
if t.dynamic != nil {
return t.dynamic
}
}
return nil
}
// Set represents the set type.
type Set struct {
of Type
}
// NewSet returns a new Set type.
func NewSet(of Type) *Set {
return &Set{
of: of,
}
}
func (t *Set) Of() Type {
return t.of
}
// MarshalJSON returns the JSON encoding of t.
func (t *Set) MarshalJSON() ([]byte, error) {
return json.Marshal(t.toMap())
}
func (t *Set) toMap() map[string]interface{} {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
if t.of != nil {
repr["of"] = t.of
}
return repr
}
func (t *Set) String() string {
prefix := typeSet
return prefix + "[" + Sprint(t.of) + "]"
}
// StaticProperty represents a static object property.
type StaticProperty struct {
Key interface{}
Value Type
}
// NewStaticProperty returns a new StaticProperty object.
func NewStaticProperty(key interface{}, value Type) *StaticProperty {
return &StaticProperty{
Key: key,
Value: value,
}
}
// MarshalJSON returns the JSON encoding of p.
func (p *StaticProperty) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"key": p.Key,
"value": p.Value,
})
}
// DynamicProperty represents a dynamic object property.
type DynamicProperty struct {
Key Type
Value Type
}
// NewDynamicProperty returns a new DynamicProperty object.
func NewDynamicProperty(key, value Type) *DynamicProperty {
return &DynamicProperty{
Key: key,
Value: value,
}
}
// MarshalJSON returns the JSON encoding of p.
func (p *DynamicProperty) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"key": p.Key,
"value": p.Value,
})
}
func (p *DynamicProperty) String() string {
return fmt.Sprintf("%s: %s", Sprint(p.Key), Sprint(p.Value))
}
// Object represents the object type.
type Object struct {
static []*StaticProperty // constant properties
dynamic *DynamicProperty // dynamic properties
}
// NewObject returns a new Object type.
func NewObject(static []*StaticProperty, dynamic *DynamicProperty) *Object {
sort.Slice(static, func(i, j int) bool {
cmp := util.Compare(static[i].Key, static[j].Key)
return cmp == -1
})
return &Object{
static: static,
dynamic: dynamic,
}
}
func (t *Object) String() string {
prefix := "object"
buf := make([]string, 0, len(t.static))
for _, p := range t.static {
buf = append(buf, fmt.Sprintf("%v: %v", p.Key, Sprint(p.Value)))
}
repr := prefix
if len(buf) > 0 {
repr += "<" + strings.Join(buf, ", ") + ">"
}
if t.dynamic != nil {
repr += "[" + t.dynamic.String() + "]"
}
return repr
}
// DynamicValue returns the type of the object's dynamic elements.
func (t *Object) DynamicValue() Type {
if t.dynamic == nil {
return nil
}
return t.dynamic.Value
}
// DynamicProperties returns the type of the object's dynamic elements.
func (t *Object) DynamicProperties() *DynamicProperty {
return t.dynamic
}
// StaticProperties returns the type of the object's static elements.
func (t *Object) StaticProperties() []*StaticProperty {
return t.static
}
// Keys returns the keys of the object's static elements.
func (t *Object) Keys() []interface{} {
sl := make([]interface{}, 0, len(t.static))
for _, p := range t.static {
sl = append(sl, p.Key)
}
return sl
}
// MarshalJSON returns the JSON encoding of t.
func (t *Object) MarshalJSON() ([]byte, error) {
return json.Marshal(t.toMap())
}
func (t *Object) toMap() map[string]interface{} {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
if len(t.static) != 0 {
repr["static"] = t.static
}
if t.dynamic != nil {
repr["dynamic"] = t.dynamic
}
return repr
}
// Select returns the type of the named property.
func (t *Object) Select(name interface{}) Type {
pos := sort.Search(len(t.static), func(x int) bool {
return util.Compare(t.static[x].Key, name) >= 0
})
if pos < len(t.static) && util.Compare(t.static[pos].Key, name) == 0 {
return t.static[pos].Value
}
if t.dynamic != nil {
if Contains(t.dynamic.Key, TypeOf(name)) {
return t.dynamic.Value
}
}
return nil
}
func (t *Object) Merge(other Type) *Object {
if otherObj, ok := other.(*Object); ok {
return mergeObjects(t, otherObj)
}
var typeK Type
var typeV Type
dynProps := t.DynamicProperties()
if dynProps != nil {
typeK = Or(Keys(other), dynProps.Key)
typeV = Or(Values(other), dynProps.Value)
dynProps = NewDynamicProperty(typeK, typeV)
} else {
typeK = Keys(other)
typeV = Values(other)
if typeK != nil && typeV != nil {
dynProps = NewDynamicProperty(typeK, typeV)
}
}
return NewObject(t.StaticProperties(), dynProps)
}
func mergeObjects(a, b *Object) *Object {
var dynamicProps *DynamicProperty
if a.dynamic != nil && b.dynamic != nil {
typeK := Or(a.dynamic.Key, b.dynamic.Key)
var typeV Type
aObj, aIsObj := a.dynamic.Value.(*Object)
bObj, bIsObj := b.dynamic.Value.(*Object)
if aIsObj && bIsObj {
typeV = mergeObjects(aObj, bObj)
} else {
typeV = Or(a.dynamic.Value, b.dynamic.Value)
}
dynamicProps = NewDynamicProperty(typeK, typeV)
} else if a.dynamic != nil {
dynamicProps = a.dynamic
} else {
dynamicProps = b.dynamic
}
staticPropsMap := make(map[interface{}]Type)
for _, sp := range a.static {
staticPropsMap[sp.Key] = sp.Value
}
for _, sp := range b.static {
currV := staticPropsMap[sp.Key]
if currV != nil {
currVObj, currVIsObj := currV.(*Object)
spVObj, spVIsObj := sp.Value.(*Object)
if currVIsObj && spVIsObj {
staticPropsMap[sp.Key] = mergeObjects(currVObj, spVObj)
} else {
staticPropsMap[sp.Key] = Or(currV, sp.Value)
}
} else {
staticPropsMap[sp.Key] = sp.Value
}
}
staticProps := make([]*StaticProperty, 0, len(staticPropsMap))
for k, v := range staticPropsMap {
staticProps = append(staticProps, NewStaticProperty(k, v))
}
return NewObject(staticProps, dynamicProps)
}
// Any represents a dynamic type.
type Any []Type
// A represents the superset of all types.
var A = NewAny()
// NewAny returns a new Any type.
func NewAny(of ...Type) Any {
sl := make(Any, len(of))
copy(sl, of)
sort.Sort(typeSlice(sl))
return sl
}
// Contains returns true if t is a superset of other.
func (t Any) Contains(other Type) bool {
if _, ok := other.(*Function); ok {
return false
}
// Note(philipc): We used to do this as a linear search.
// Since this is always sorted, we can use a binary search instead.
i := sort.Search(len(t), func(i int) bool {
return Compare(t[i], other) >= 0
})
if i < len(t) && Compare(t[i], other) == 0 {
// x is present at t[i]
return true
}
return len(t) == 0
}
// MarshalJSON returns the JSON encoding of t.
func (t Any) MarshalJSON() ([]byte, error) {
return json.Marshal(t.toMap())
}
func (t Any) toMap() map[string]interface{} {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
if len(t) != 0 {
repr["of"] = []Type(t)
}
return repr
}
// Merge return a new Any type that is the superset of t and other.
func (t Any) Merge(other Type) Any {
if otherAny, ok := other.(Any); ok {
return t.Union(otherAny)
}
if t.Contains(other) {
return t
}
cpy := make(Any, len(t)+1)
idx := sort.Search(len(t), func(i int) bool {
return Compare(t[i], other) >= 0
})
copy(cpy, t[:idx])
cpy[idx] = other
copy(cpy[idx+1:], t[idx:])
return cpy
}
// Union returns a new Any type that is the union of the two Any types.
// Note(philipc): The two Any slices MUST be sorted before running Union,
// or else this method will fail to merge the two slices correctly.
func (t Any) Union(other Any) Any {
lenT := len(t)
lenOther := len(other)
// Return the more general (blank) Any type if present.
if lenT == 0 {
return t
}
if lenOther == 0 {
return other
}
// Prealloc the output list.
maxLen := lenT
if lenT < lenOther {
maxLen = lenOther
}
merged := make(Any, 0, maxLen)
// Note(philipc): Create a merged slice, doing the minimum number of
// comparisons along the way. We treat this as a problem of merging two
// sorted lists that might have duplicates. This specifically saves us
// from cases where one list might be *much* longer than the other.
// Algorithm:
// Assume:
// - List A
// - List B
// - List Output
// - Idx_a, Idx_b
// Procedure:
// - While Idx_a < len(A) and Idx_b < len(B)
// - Compare head(A) and head(B)
// - Cases:
// - A < B: Append head(A) to Output, advance Idx_a
// - A == B: Append head(A) to Output, advance Idx_a, Idx_b
// - A > B: Append head(B) to Output, advance Idx_b
// - Return output
idxA := 0
idxB := 0
for idxA < lenT || idxB < lenOther {
// Early-exit cases:
if idxA == lenT {
// Ran out of elements in t. Copy over what's left from other.
merged = append(merged, other[idxB:]...)
break
} else if idxB == lenOther {
// Ran out of elements in other. Copy over what's left from t.
merged = append(merged, t[idxA:]...)
break
}
// Normal selection of next element to merge:
switch Compare(t[idxA], other[idxB]) {
// A < B:
case -1:
merged = append(merged, t[idxA])
idxA++
// A == B:
case 0:
merged = append(merged, t[idxA])
idxA++
idxB++
// A > B:
case 1:
merged = append(merged, other[idxB])
idxB++
}
}
return merged
}
func (t Any) String() string {
prefix := "any"
if len(t) == 0 {
return prefix
}
buf := make([]string, len(t))
for i := range t {
buf[i] = Sprint(t[i])
}
return prefix + "<" + strings.Join(buf, ", ") + ">"
}
// Function represents a function type.
type Function struct {
args []Type
result Type
variadic Type
}
// Args returns an argument list.
func Args(x ...Type) []Type {
return x
}
// Void returns true if the function has no return value. This function returns
// false if x is not a function.
func Void(x Type) bool {
f, ok := x.(*Function)
return ok && f.Result() == nil
}
// Arity returns the number of arguments in the function signature or zero if x
// is not a function. If the type is unknown, this function returns -1.
func Arity(x Type) int {
if x == nil {
return -1
}
f, ok := x.(*Function)
if !ok {
return 0
}
return len(f.FuncArgs().Args)
}
// NewFunction returns a new Function object of the given argument and result types.
func NewFunction(args []Type, result Type) *Function {
return &Function{
args: args,
result: result,
}
}
// NewVariadicFunction returns a new Function object. This function sets the
// variadic bit on the signature. Non-void variadic functions are not currently
// supported.
func NewVariadicFunction(args []Type, varargs Type, result Type) *Function {
if result != nil {
panic("illegal value: non-void variadic functions not supported")
}
return &Function{
args: args,
variadic: varargs,
result: nil,
}
}
// FuncArgs returns the function's arguments.
func (t *Function) FuncArgs() FuncArgs {
return FuncArgs{Args: t.Args(), Variadic: unwrap(t.variadic)}
}
// NamedFuncArgs returns the function's arguments, with a name and
// description if available.
func (t *Function) NamedFuncArgs() FuncArgs {
args := make([]Type, len(t.args))
copy(args, t.args)
return FuncArgs{Args: args, Variadic: t.variadic}
}
// Args returns the function's arguments as a slice, ignoring variadic arguments.
// Deprecated: Use FuncArgs instead.
func (t *Function) Args() []Type {
cpy := make([]Type, len(t.args))
for i := range t.args {
cpy[i] = unwrap(t.args[i])
}
return cpy
}
// Result returns the function's result type.
func (t *Function) Result() Type {
return unwrap(t.result)
}
// Result returns the function's result type, without stripping name and description.
func (t *Function) NamedResult() Type {
return t.result
}
func (t *Function) String() string {
return fmt.Sprintf("%v => %v", t.FuncArgs(), Sprint(t.Result()))
}
// MarshalJSON returns the JSON encoding of t.
func (t *Function) MarshalJSON() ([]byte, error) {
repr := map[string]interface{}{
"type": t.typeMarker(),
}
if len(t.args) > 0 {
repr["args"] = t.args
}
if t.result != nil {
repr["result"] = t.result
}
if t.variadic != nil {
repr["variadic"] = t.variadic
}
return json.Marshal(repr)
}
// UnmarshalJSON decodes the JSON serialized function declaration.
func (t *Function) UnmarshalJSON(bs []byte) error {
tpe, err := Unmarshal(bs)
if err != nil {
return err
}
f, ok := tpe.(*Function)
if !ok {
return fmt.Errorf("invalid type")
}
*t = *f
return nil
}
// Union returns a new function representing the union of t and other. Functions
// must have the same arity to be unioned.
func (t *Function) Union(other *Function) *Function {
if other == nil {
return t
}
if t == nil {
return other
}
a := t.Args()
b := other.Args()
if len(a) != len(b) {
return nil
}
aIsVariadic := t.FuncArgs().Variadic != nil
bIsVariadic := other.FuncArgs().Variadic != nil
if aIsVariadic && !bIsVariadic {
return nil
} else if bIsVariadic && !aIsVariadic {
return nil
}
args := make([]Type, len(a))
for i := range a {
args[i] = Or(a[i], b[i])
}
result := NewFunction(args, Or(t.Result(), other.Result()))
result.variadic = Or(t.FuncArgs().Variadic, other.FuncArgs().Variadic)
return result
}
// FuncArgs represents the arguments that can be passed to a function.
type FuncArgs struct {
Args []Type `json:"args,omitempty"`
Variadic Type `json:"variadic,omitempty"`
}
func (a FuncArgs) String() string {
buf := make([]string, 0, len(a.Args)+1)
for i := range a.Args {
buf = append(buf, Sprint(a.Args[i]))
}
if a.Variadic != nil {
buf = append(buf, Sprint(a.Variadic)+"...")
}
return "(" + strings.Join(buf, ", ") + ")"
}
// Arg returns the nth argument's type.
func (a FuncArgs) Arg(x int) Type {
if x < len(a.Args) {
return a.Args[x]
}
return a.Variadic
}
// Compare returns -1, 0, 1 based on comparison between a and b.
func Compare(a, b Type) int {
a, b = unwrap(a), unwrap(b)
x := typeOrder(a)
y := typeOrder(b)
if x > y {
return 1
} else if x < y {
return -1
}
switch a.(type) {
case nil, Null, Boolean, Number, String:
return 0
case *Array:
arrA := a.(*Array)
arrB := b.(*Array)
if arrA.dynamic != nil && arrB.dynamic == nil {
return 1
} else if arrB.dynamic != nil && arrA.dynamic == nil {
return -1
}
if arrB.dynamic != nil && arrA.dynamic != nil {
if cmp := Compare(arrA.dynamic, arrB.dynamic); cmp != 0 {
return cmp
}
}
return typeSliceCompare(arrA.static, arrB.static)
case *Object:
objA := a.(*Object)
objB := b.(*Object)
if objA.dynamic != nil && objB.dynamic == nil {
return 1
} else if objB.dynamic != nil && objA.dynamic == nil {
return -1
}
if objA.dynamic != nil && objB.dynamic != nil {
if cmp := Compare(objA.dynamic.Key, objB.dynamic.Key); cmp != 0 {
return cmp
}
if cmp := Compare(objA.dynamic.Value, objB.dynamic.Value); cmp != 0 {
return cmp
}
}
lenStaticA := len(objA.static)
lenStaticB := len(objB.static)
minLen := lenStaticA
if lenStaticB < minLen {
minLen = lenStaticB
}
for i := 0; i < minLen; i++ {
if cmp := util.Compare(objA.static[i].Key, objB.static[i].Key); cmp != 0 {
return cmp
}
if cmp := Compare(objA.static[i].Value, objB.static[i].Value); cmp != 0 {
return cmp
}
}
if lenStaticA < lenStaticB {
return -1
} else if lenStaticB < lenStaticA {
return 1
}
return 0
case *Set:
setA := a.(*Set)
setB := b.(*Set)
if setA.of == nil && setB.of == nil {
return 0
} else if setA.of == nil {
return -1
} else if setB.of == nil {
return 1
}
return Compare(setA.of, setB.of)
case Any:
sl1 := typeSlice(a.(Any))
sl2 := typeSlice(b.(Any))
return typeSliceCompare(sl1, sl2)
case *Function:
fA := a.(*Function)
fB := b.(*Function)
if len(fA.args) < len(fB.args) {
return -1
} else if len(fA.args) > len(fB.args) {
return 1
}
for i := 0; i < len(fA.args); i++ {
if cmp := Compare(fA.args[i], fB.args[i]); cmp != 0 {
return cmp
}
}
if cmp := Compare(fA.result, fB.result); cmp != 0 {
return cmp
}
return Compare(fA.variadic, fB.variadic)
default:
panic("unreachable")
}
}
// Contains returns true if a is a superset or equal to b.
func Contains(a, b Type) bool {
if x, ok := unwrap(a).(Any); ok {
return x.Contains(b)
}
return Compare(a, b) == 0
}
// Or returns a type that represents the union of a and b. If one type is a
// superset of the other, the superset is returned unchanged.
func Or(a, b Type) Type {
a, b = unwrap(a), unwrap(b)
if a == nil {
return b
} else if b == nil {
return a
}
fA, ok1 := a.(*Function)
fB, ok2 := b.(*Function)
if ok1 && ok2 {
return fA.Union(fB)
} else if ok1 || ok2 {
return nil
}
anyA, ok1 := a.(Any)
anyB, ok2 := b.(Any)
if ok1 {
return anyA.Merge(b)
}
if ok2 {
return anyB.Merge(a)
}
if Compare(a, b) == 0 {
return a
}
return NewAny(a, b)
}
// Select returns a property or item of a.
func Select(a Type, x interface{}) Type {
switch a := unwrap(a).(type) {
case *Array:
n, ok := x.(json.Number)
if !ok {
return nil
}
pos, err := n.Int64()
if err != nil {
return nil
}
return a.Select(int(pos))
case *Object:
return a.Select(x)
case *Set:
tpe := TypeOf(x)
if Compare(a.of, tpe) == 0 {
return a.of
}
if x, ok := a.of.(Any); ok {
if x.Contains(tpe) {
return tpe
}
}
return nil
case Any:
if Compare(a, A) == 0 {
return A
}
var tpe Type
for i := range a {
// TODO(tsandall): test nil/nil
tpe = Or(Select(a[i], x), tpe)
}
return tpe
default:
return nil
}
}
// Keys returns the type of keys that can be enumerated for a. For arrays, the
// keys are always number types, for objects the keys are always string types,
// and for sets the keys are always the type of the set element.
func Keys(a Type) Type {
switch a := unwrap(a).(type) {
case *Array:
return N
case *Object:
var tpe Type
for _, k := range a.Keys() {
tpe = Or(tpe, TypeOf(k))
}
if a.dynamic != nil {
tpe = Or(tpe, a.dynamic.Key)
}
return tpe
case *Set:
return a.of
case Any:
// TODO(tsandall): ditto test
if Compare(a, A) == 0 {
return A
}
var tpe Type
for i := range a {
tpe = Or(Keys(a[i]), tpe)
}
return tpe
}
return nil
}
// Values returns the type of values that can be enumerated for a.
func Values(a Type) Type {
switch a := unwrap(a).(type) {
case *Array:
var tpe Type
for i := range a.static {
tpe = Or(tpe, a.static[i])
}
return Or(tpe, a.dynamic)
case *Object:
var tpe Type
for i := range a.static {
tpe = Or(tpe, a.static[i].Value)
}
if a.dynamic != nil {
tpe = Or(tpe, a.dynamic.Value)
}
return tpe
case *Set:
return a.of
case Any:
if Compare(a, A) == 0 {
return A
}
var tpe Type
for i := range a {
tpe = Or(Values(a[i]), tpe)
}
return tpe
}
return nil
}
// Nil returns true if a's type is unknown.
func Nil(a Type) bool {
switch a := unwrap(a).(type) {
case nil:
return true
case *Function:
for i := range a.args {
if Nil(a.args[i]) {
return true
}
}
return Nil(a.result)
case *Array:
for i := range a.static {
if Nil(a.static[i]) {
return true
}
}
if a.dynamic != nil {
return Nil(a.dynamic)
}
case *Object:
for i := range a.static {
if Nil(a.static[i].Value) {
return true
}
}
if a.dynamic != nil {
return Nil(a.dynamic.Key) || Nil(a.dynamic.Value)
}
case *Set:
return Nil(a.of)
}
return false
}
// TypeOf returns the type of the Golang native value.
func TypeOf(x interface{}) Type {
switch x := x.(type) {
case nil:
return NewNull()
case bool:
return B
case string:
return S
case json.Number:
return N
case map[string]interface{}:
// The ast.ValueToInterface() function returns ast.Object values as map[string]interface{}
// so map[string]interface{} must be handled here because the type checker uses the value
// to interface conversion when inferring object types.
static := make([]*StaticProperty, 0, len(x))
for k, v := range x {
static = append(static, NewStaticProperty(k, TypeOf(v)))
}
return NewObject(static, nil)
case map[interface{}]interface{}:
static := make([]*StaticProperty, 0, len(x))
for k, v := range x {
static = append(static, NewStaticProperty(k, TypeOf(v)))
}
return NewObject(static, nil)
case []interface{}:
static := make([]Type, len(x))
for i := range x {
static[i] = TypeOf(x[i])
}
return NewArray(static, nil)
}
panic("unreachable")
}
type typeSlice []Type
func (s typeSlice) Less(i, j int) bool { return Compare(s[i], s[j]) < 0 }
func (s typeSlice) Swap(i, j int) { x := s[i]; s[i] = s[j]; s[j] = x }
func (s typeSlice) Len() int { return len(s) }
func typeSliceCompare(a, b []Type) int {
minLen := len(a)
if len(b) < minLen {
minLen = len(b)
}
for i := 0; i < minLen; i++ {
if cmp := Compare(a[i], b[i]); cmp != 0 {
return cmp
}
}
if len(a) < len(b) {
return -1
} else if len(b) < len(a) {
return 1
}
return 0
}
func typeOrder(x Type) int {
switch unwrap(x).(type) {
case Null:
return 0
case Boolean:
return 1
case Number:
return 2
case String:
return 3
case *Array:
return 4
case *Object:
return 5
case *Set:
return 6
case Any:
return 7
case *Function:
return 8
case nil:
return -1
}
panic("unreachable")
}