package eval
import (
"big"
"go/ast"
"go/token"
"log"
"reflect"
"sort"
"unsafe"
)
type Type interface {
compat(o Type, conv bool) bool
lit() Type
isBoolean() bool
isInteger() bool
isFloat() bool
isIdeal() bool
Zero() Value
String() string
Pos() token.Position
}
type BoundedType interface {
Type
minVal() *big.Rat
maxVal() *big.Rat
}
var universePos = token.Position{"<universe>", 0, 0, 0}
type typeArrayMapEntry struct {
key []Type
v interface{}
next *typeArrayMapEntry
}
type typeArrayMap map[uintptr]*typeArrayMapEntry
func hashTypeArray(key []Type) uintptr {
hash := uintptr(0)
for _, t := range key {
hash = hash * 33
if t == nil {
continue
}
addr := reflect.NewValue(t).(*reflect.PtrValue).Get()
hash ^= addr
}
return hash
}
func newTypeArrayMap() typeArrayMap { return make(map[uintptr]*typeArrayMapEntry) }
func (m typeArrayMap) Get(key []Type) interface{} {
ent, ok := m[hashTypeArray(key)]
if !ok {
return nil
}
nextEnt:
for ; ent != nil; ent = ent.next {
if len(key) != len(ent.key) {
continue
}
for i := 0; i < len(key); i++ {
if key[i] != ent.key[i] {
continue nextEnt
}
}
return ent.v
}
return nil
}
func (m typeArrayMap) Put(key []Type, v interface{}) interface{} {
hash := hashTypeArray(key)
ent := m[hash]
new := &typeArrayMapEntry{key, v, ent}
m[hash] = new
return v
}
type commonType struct{}
func (commonType) isBoolean() bool { return false }
func (commonType) isInteger() bool { return false }
func (commonType) isFloat() bool { return false }
func (commonType) isIdeal() bool { return false }
func (commonType) Pos() token.Position { return token.Position{} }
type boolType struct {
commonType
}
var BoolType = universe.DefineType("bool", universePos, &boolType{})
func (t *boolType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*boolType)
return ok
}
func (t *boolType) lit() Type { return t }
func (t *boolType) isBoolean() bool { return true }
func (boolType) String() string {
return "<bool>"
}
func (t *boolType) Zero() Value {
res := boolV(false)
return &res
}
type uintType struct {
commonType
Bits uint
Ptr bool
name string
}
var (
Uint8Type = universe.DefineType("uint8", universePos, &uintType{commonType{}, 8, false, "uint8"})
Uint16Type = universe.DefineType("uint16", universePos, &uintType{commonType{}, 16, false, "uint16"})
Uint32Type = universe.DefineType("uint32", universePos, &uintType{commonType{}, 32, false, "uint32"})
Uint64Type = universe.DefineType("uint64", universePos, &uintType{commonType{}, 64, false, "uint64"})
UintType = universe.DefineType("uint", universePos, &uintType{commonType{}, 0, false, "uint"})
UintptrType = universe.DefineType("uintptr", universePos, &uintType{commonType{}, 0, true, "uintptr"})
)
func (t *uintType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*uintType)
return ok && t == t2
}
func (t *uintType) lit() Type { return t }
func (t *uintType) isInteger() bool { return true }
func (t *uintType) String() string { return "<" + t.name + ">" }
func (t *uintType) Zero() Value {
switch t.Bits {
case 0:
if t.Ptr {
res := uintptrV(0)
return &res
} else {
res := uintV(0)
return &res
}
case 8:
res := uint8V(0)
return &res
case 16:
res := uint16V(0)
return &res
case 32:
res := uint32V(0)
return &res
case 64:
res := uint64V(0)
return &res
}
panic("unexpected uint bit count")
}
func (t *uintType) minVal() *big.Rat { return big.NewRat(0, 1) }
func (t *uintType) maxVal() *big.Rat {
bits := t.Bits
if bits == 0 {
if t.Ptr {
bits = uint(8 * unsafe.Sizeof(uintptr(0)))
} else {
bits = uint(8 * unsafe.Sizeof(uint(0)))
}
}
numer := big.NewInt(1)
numer.Lsh(numer, bits)
numer.Sub(numer, idealOne)
return new(big.Rat).SetInt(numer)
}
type intType struct {
commonType
Bits uint
name string
}
var (
Int8Type = universe.DefineType("int8", universePos, &intType{commonType{}, 8, "int8"})
Int16Type = universe.DefineType("int16", universePos, &intType{commonType{}, 16, "int16"})
Int32Type = universe.DefineType("int32", universePos, &intType{commonType{}, 32, "int32"})
Int64Type = universe.DefineType("int64", universePos, &intType{commonType{}, 64, "int64"})
IntType = universe.DefineType("int", universePos, &intType{commonType{}, 0, "int"})
)
func (t *intType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*intType)
return ok && t == t2
}
func (t *intType) lit() Type { return t }
func (t *intType) isInteger() bool { return true }
func (t *intType) String() string { return "<" + t.name + ">" }
func (t *intType) Zero() Value {
switch t.Bits {
case 8:
res := int8V(0)
return &res
case 16:
res := int16V(0)
return &res
case 32:
res := int32V(0)
return &res
case 64:
res := int64V(0)
return &res
case 0:
res := intV(0)
return &res
}
panic("unexpected int bit count")
}
func (t *intType) minVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(int(0)))
}
numer := big.NewInt(-1)
numer.Lsh(numer, bits-1)
return new(big.Rat).SetInt(numer)
}
func (t *intType) maxVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(int(0)))
}
numer := big.NewInt(1)
numer.Lsh(numer, bits-1)
numer.Sub(numer, idealOne)
return new(big.Rat).SetInt(numer)
}
type idealIntType struct {
commonType
}
var IdealIntType Type = &idealIntType{}
func (t *idealIntType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*idealIntType)
return ok
}
func (t *idealIntType) lit() Type { return t }
func (t *idealIntType) isInteger() bool { return true }
func (t *idealIntType) isIdeal() bool { return true }
func (t *idealIntType) String() string { return "ideal integer" }
func (t *idealIntType) Zero() Value { return &idealIntV{idealZero} }
type floatType struct {
commonType
Bits uint
name string
}
var (
Float32Type = universe.DefineType("float32", universePos, &floatType{commonType{}, 32, "float32"})
Float64Type = universe.DefineType("float64", universePos, &floatType{commonType{}, 64, "float64"})
FloatType = universe.DefineType("float", universePos, &floatType{commonType{}, 0, "float"})
)
func (t *floatType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*floatType)
return ok && t == t2
}
func (t *floatType) lit() Type { return t }
func (t *floatType) isFloat() bool { return true }
func (t *floatType) String() string { return "<" + t.name + ">" }
func (t *floatType) Zero() Value {
switch t.Bits {
case 32:
res := float32V(0)
return &res
case 64:
res := float64V(0)
return &res
case 0:
res := floatV(0)
return &res
}
panic("unexpected float bit count")
}
var maxFloat32Val *big.Rat
var maxFloat64Val *big.Rat
var minFloat32Val *big.Rat
var minFloat64Val *big.Rat
func (t *floatType) minVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(float(0)))
}
switch bits {
case 32:
return minFloat32Val
case 64:
return minFloat64Val
}
log.Crashf("unexpected floating point bit count: %d", bits)
panic("unreachable")
}
func (t *floatType) maxVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(float(0)))
}
switch bits {
case 32:
return maxFloat32Val
case 64:
return maxFloat64Val
}
log.Crashf("unexpected floating point bit count: %d", bits)
panic("unreachable")
}
type idealFloatType struct {
commonType
}
var IdealFloatType Type = &idealFloatType{}
func (t *idealFloatType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*idealFloatType)
return ok
}
func (t *idealFloatType) lit() Type { return t }
func (t *idealFloatType) isFloat() bool { return true }
func (t *idealFloatType) isIdeal() bool { return true }
func (t *idealFloatType) String() string { return "ideal float" }
func (t *idealFloatType) Zero() Value { return &idealFloatV{big.NewRat(0, 1)} }
type stringType struct {
commonType
}
var StringType = universe.DefineType("string", universePos, &stringType{})
func (t *stringType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*stringType)
return ok
}
func (t *stringType) lit() Type { return t }
func (t *stringType) String() string { return "<string>" }
func (t *stringType) Zero() Value {
res := stringV("")
return &res
}
type ArrayType struct {
commonType
Len int64
Elem Type
}
var arrayTypes = make(map[int64]map[Type]*ArrayType)
func NewArrayType(len int64, elem Type) *ArrayType {
ts, ok := arrayTypes[len]
if !ok {
ts = make(map[Type]*ArrayType)
arrayTypes[len] = ts
}
t, ok := ts[elem]
if !ok {
t = &ArrayType{commonType{}, len, elem}
ts[elem] = t
}
return t
}
func (t *ArrayType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*ArrayType)
if !ok {
return false
}
return t.Len == t2.Len && t.Elem.compat(t2.Elem, conv)
}
func (t *ArrayType) lit() Type { return t }
func (t *ArrayType) String() string { return "[]" + t.Elem.String() }
func (t *ArrayType) Zero() Value {
res := arrayV(make([]Value, t.Len))
for i := int64(0); i < t.Len; i++ {
res[i] = t.Elem.Zero()
}
return &res
}
type StructField struct {
Name string
Type Type
Anonymous bool
}
type StructType struct {
commonType
Elems []StructField
}
var structTypes = newTypeArrayMap()
func NewStructType(fields []StructField) *StructType {
fts := make([]Type, len(fields))
for i, f := range fields {
fts[i] = f.Type
}
tMapI := structTypes.Get(fts)
if tMapI == nil {
tMapI = structTypes.Put(fts, make(map[string]*StructType))
}
tMap := tMapI.(map[string]*StructType)
key := ""
for _, f := range fields {
if f.Anonymous {
key += "!"
}
key += f.Name + " "
}
t, ok := tMap[key]
if !ok {
t = &StructType{commonType{}, fields}
tMap[key] = t
}
return t
}
func (t *StructType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*StructType)
if !ok {
return false
}
if len(t.Elems) != len(t2.Elems) {
return false
}
for i, e := range t.Elems {
e2 := t2.Elems[i]
if e.Anonymous != e2.Anonymous ||
(!e.Anonymous && e.Name != e2.Name) ||
!e.Type.compat(e2.Type, conv) {
return false
}
}
return true
}
func (t *StructType) lit() Type { return t }
func (t *StructType) String() string {
s := "struct {"
for i, f := range t.Elems {
if i > 0 {
s += "; "
}
if !f.Anonymous {
s += f.Name + " "
}
s += f.Type.String()
}
return s + "}"
}
func (t *StructType) Zero() Value {
res := structV(make([]Value, len(t.Elems)))
for i, f := range t.Elems {
res[i] = f.Type.Zero()
}
return &res
}
type PtrType struct {
commonType
Elem Type
}
var ptrTypes = make(map[Type]*PtrType)
func NewPtrType(elem Type) *PtrType {
t, ok := ptrTypes[elem]
if !ok {
t = &PtrType{commonType{}, elem}
ptrTypes[elem] = t
}
return t
}
func (t *PtrType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*PtrType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv)
}
func (t *PtrType) lit() Type { return t }
func (t *PtrType) String() string { return "*" + t.Elem.String() }
func (t *PtrType) Zero() Value { return &ptrV{nil} }
type FuncType struct {
commonType
In []Type
Variadic bool
Out []Type
builtin string
}
var funcTypes = newTypeArrayMap()
var variadicFuncTypes = newTypeArrayMap()
var (
capType = &FuncType{builtin: "cap"}
closeType = &FuncType{builtin: "close"}
closedType = &FuncType{builtin: "closed"}
lenType = &FuncType{builtin: "len"}
makeType = &FuncType{builtin: "make"}
newType = &FuncType{builtin: "new"}
panicType = &FuncType{builtin: "panic"}
printType = &FuncType{builtin: "print"}
printlnType = &FuncType{builtin: "println"}
)
func NewFuncType(in []Type, variadic bool, out []Type) *FuncType {
inMap := funcTypes
if variadic {
inMap = variadicFuncTypes
}
outMapI := inMap.Get(in)
if outMapI == nil {
outMapI = inMap.Put(in, newTypeArrayMap())
}
outMap := outMapI.(typeArrayMap)
tI := outMap.Get(out)
if tI != nil {
return tI.(*FuncType)
}
t := &FuncType{commonType{}, in, variadic, out, ""}
outMap.Put(out, t)
return t
}
func (t *FuncType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*FuncType)
if !ok {
return false
}
if len(t.In) != len(t2.In) || t.Variadic != t2.Variadic || len(t.Out) != len(t2.Out) {
return false
}
for i := range t.In {
if !t.In[i].compat(t2.In[i], conv) {
return false
}
}
for i := range t.Out {
if !t.Out[i].compat(t2.Out[i], conv) {
return false
}
}
return true
}
func (t *FuncType) lit() Type { return t }
func typeListString(ts []Type, ns []*ast.Ident) string {
s := ""
for i, t := range ts {
if i > 0 {
s += ", "
}
if ns != nil && ns[i] != nil {
s += ns[i].Name() + " "
}
if t == nil {
s += "<none>"
} else {
s += t.String()
}
}
return s
}
func (t *FuncType) String() string {
if t.builtin != "" {
return "built-in function " + t.builtin
}
args := typeListString(t.In, nil)
if t.Variadic {
if len(args) > 0 {
args += ", "
}
args += "..."
}
s := "func(" + args + ")"
if len(t.Out) > 0 {
s += " (" + typeListString(t.Out, nil) + ")"
}
return s
}
func (t *FuncType) Zero() Value { return &funcV{nil} }
type FuncDecl struct {
Type *FuncType
Name *ast.Ident
InNames []*ast.Ident
OutNames []*ast.Ident
}
func (t *FuncDecl) String() string {
s := "func"
if t.Name != nil {
s += " " + t.Name.Name()
}
s += funcTypeString(t.Type, t.InNames, t.OutNames)
return s
}
func funcTypeString(ft *FuncType, ins []*ast.Ident, outs []*ast.Ident) string {
s := "("
s += typeListString(ft.In, ins)
if ft.Variadic {
if len(ft.In) > 0 {
s += ", "
}
s += "..."
}
s += ")"
if len(ft.Out) > 0 {
s += " (" + typeListString(ft.Out, outs) + ")"
}
return s
}
type InterfaceType struct {
commonType
methods []IMethod
}
type IMethod struct {
Name string
Type *FuncType
}
var interfaceTypes = newTypeArrayMap()
func NewInterfaceType(methods []IMethod, embeds []*InterfaceType) *InterfaceType {
nMethods := len(methods)
for _, e := range embeds {
nMethods += len(e.methods)
}
allMethods := make([]IMethod, nMethods)
for i, m := range methods {
allMethods[i] = m
}
n := len(methods)
for _, e := range embeds {
for _, m := range e.methods {
allMethods[n] = m
n++
}
}
sort.Sort(iMethodSorter(allMethods))
mts := make([]Type, len(allMethods))
for i, m := range methods {
mts[i] = m.Type
}
tMapI := interfaceTypes.Get(mts)
if tMapI == nil {
tMapI = interfaceTypes.Put(mts, make(map[string]*InterfaceType))
}
tMap := tMapI.(map[string]*InterfaceType)
key := ""
for _, m := range allMethods {
key += m.Name + " "
}
t, ok := tMap[key]
if !ok {
t = &InterfaceType{commonType{}, allMethods}
tMap[key] = t
}
return t
}
type iMethodSorter []IMethod
func (s iMethodSorter) Less(a, b int) bool { return s[a].Name < s[b].Name }
func (s iMethodSorter) Swap(a, b int) { s[a], s[b] = s[b], s[a] }
func (s iMethodSorter) Len() int { return len(s) }
func (t *InterfaceType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*InterfaceType)
if !ok {
return false
}
if len(t.methods) != len(t2.methods) {
return false
}
for i, e := range t.methods {
e2 := t2.methods[i]
if e.Name != e2.Name || !e.Type.compat(e2.Type, conv) {
return false
}
}
return true
}
func (t *InterfaceType) lit() Type { return t }
func (t *InterfaceType) String() string {
s := "interface {"
for i, m := range t.methods {
if i > 0 {
s += "; "
}
s += m.Name + funcTypeString(m.Type, nil, nil)
}
return s + "}"
}
func (t *InterfaceType) implementedBy(o Type) (*IMethod, bool) {
if len(t.methods) == 0 {
return nil, true
}
if it, ok := o.lit().(*InterfaceType); ok {
o = it
}
switch o := o.(type) {
case *NamedType:
for _, tm := range t.methods {
sm, ok := o.methods[tm.Name]
if !ok || sm.decl.Type != tm.Type {
return &tm, false
}
}
return nil, true
case *InterfaceType:
var ti, oi int
for ti < len(t.methods) && oi < len(o.methods) {
tm, om := &t.methods[ti], &o.methods[oi]
switch {
case tm.Name == om.Name:
if tm.Type != om.Type {
return tm, false
}
ti++
oi++
case tm.Name > om.Name:
oi++
default:
return tm, false
}
}
if ti < len(t.methods) {
return &t.methods[ti], false
}
return nil, true
}
return &t.methods[0], false
}
func (t *InterfaceType) Zero() Value { return &interfaceV{} }
type SliceType struct {
commonType
Elem Type
}
var sliceTypes = make(map[Type]*SliceType)
func NewSliceType(elem Type) *SliceType {
t, ok := sliceTypes[elem]
if !ok {
t = &SliceType{commonType{}, elem}
sliceTypes[elem] = t
}
return t
}
func (t *SliceType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*SliceType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv)
}
func (t *SliceType) lit() Type { return t }
func (t *SliceType) String() string { return "[]" + t.Elem.String() }
func (t *SliceType) Zero() Value {
return &sliceV{Slice{nil, 0, 0}}
}
type MapType struct {
commonType
Key Type
Elem Type
}
var mapTypes = make(map[Type]map[Type]*MapType)
func NewMapType(key Type, elem Type) *MapType {
ts, ok := mapTypes[key]
if !ok {
ts = make(map[Type]*MapType)
mapTypes[key] = ts
}
t, ok := ts[elem]
if !ok {
t = &MapType{commonType{}, key, elem}
ts[elem] = t
}
return t
}
func (t *MapType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*MapType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv) && t.Key.compat(t2.Key, conv)
}
func (t *MapType) lit() Type { return t }
func (t *MapType) String() string { return "map[" + t.Key.String() + "] " + t.Elem.String() }
func (t *MapType) Zero() Value {
return &mapV{nil}
}
type Method struct {
decl *FuncDecl
fn Func
}
type NamedType struct {
token.Position
Name string
Def Type
incomplete bool
methods map[string]Method
}
func NewNamedType(name string) *NamedType {
return &NamedType{token.Position{}, name, nil, true, make(map[string]Method)}
}
func (t *NamedType) Complete(def Type) {
if !t.incomplete {
log.Crashf("cannot complete already completed NamedType %+v", *t)
}
if ndef, ok := def.(*NamedType); ok {
def = ndef.Def
}
t.Def = def
t.incomplete = false
}
func (t *NamedType) compat(o Type, conv bool) bool {
t2, ok := o.(*NamedType)
if ok {
if conv {
return t.Def.compat(t2.Def, conv)
} else {
return t == t2
}
}
return o.compat(t.Def, conv)
}
func (t *NamedType) lit() Type { return t.Def.lit() }
func (t *NamedType) isBoolean() bool { return t.Def.isBoolean() }
func (t *NamedType) isInteger() bool { return t.Def.isInteger() }
func (t *NamedType) isFloat() bool { return t.Def.isFloat() }
func (t *NamedType) isIdeal() bool { return false }
func (t *NamedType) String() string { return t.Name }
func (t *NamedType) Zero() Value { return t.Def.Zero() }
type MultiType struct {
commonType
Elems []Type
}
var multiTypes = newTypeArrayMap()
func NewMultiType(elems []Type) *MultiType {
if t := multiTypes.Get(elems); t != nil {
return t.(*MultiType)
}
t := &MultiType{commonType{}, elems}
multiTypes.Put(elems, t)
return t
}
func (t *MultiType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*MultiType)
if !ok {
return false
}
if len(t.Elems) != len(t2.Elems) {
return false
}
for i := range t.Elems {
if !t.Elems[i].compat(t2.Elems[i], conv) {
return false
}
}
return true
}
var EmptyType Type = NewMultiType([]Type{})
func (t *MultiType) lit() Type { return t }
func (t *MultiType) String() string {
if len(t.Elems) == 0 {
return "<none>"
}
return typeListString(t.Elems, nil)
}
func (t *MultiType) Zero() Value {
res := make([]Value, len(t.Elems))
for i, t := range t.Elems {
res[i] = t.Zero()
}
return multiV(res)
}
func init() {
numer := big.NewInt(0xffffff)
numer.Lsh(numer, 127-23)
maxFloat32Val = new(big.Rat).SetInt(numer)
numer.SetInt64(0x1fffffffffffff)
numer.Lsh(numer, 1023-52)
maxFloat64Val = new(big.Rat).SetInt(numer)
minFloat32Val = new(big.Rat).Neg(maxFloat32Val)
minFloat64Val = new(big.Rat).Neg(maxFloat64Val)
universe.defs["byte"] = universe.defs["uint8"]
universe.DefineConst("cap", universePos, capType, nil)
universe.DefineConst("close", universePos, closeType, nil)
universe.DefineConst("closed", universePos, closedType, nil)
universe.DefineConst("len", universePos, lenType, nil)
universe.DefineConst("make", universePos, makeType, nil)
universe.DefineConst("new", universePos, newType, nil)
universe.DefineConst("panic", universePos, panicType, nil)
universe.DefineConst("print", universePos, printType, nil)
universe.DefineConst("println", universePos, printlnType, nil)
}