Files
moxa/interp/type.go
2019-03-22 10:27:02 +01:00

594 lines
14 KiB
Go

package interp
import (
"reflect"
"strconv"
)
// Cat defines interpreter type categories
type Cat uint
// Types for go language
const (
NilT Cat = iota
AliasT
ArrayT
BinT
BinPkgT
BoolT
BuiltinT
ByteT
ChanT
Complex64T
Complex128T
ErrorT
Float32T
Float64T
FuncT
InterfaceT
IntT
Int8T
Int16T
Int32T
Int64T
MapT
PtrT
RuneT
SrcPkgT
StringT
StructT
UintT
Uint8T
Uint16T
Uint32T
Uint64T
UintptrT
ValueT
MaxT
)
var cats = [...]string{
NilT: "NilT",
AliasT: "AliasT",
ArrayT: "ArrayT",
BinT: "BinT",
BinPkgT: "BinPkgT",
ByteT: "ByteT",
BoolT: "BoolT",
BuiltinT: "BuiltinT",
ChanT: "ChanT",
Complex64T: "Complex64T",
Complex128T: "Complex128T",
ErrorT: "ErrorT",
Float32T: "Float32",
Float64T: "Float64T",
FuncT: "FuncT",
InterfaceT: "InterfaceT",
IntT: "IntT",
Int8T: "Int8T",
Int16T: "Int16T",
Int32T: "Int32T",
Int64T: "Int64T",
MapT: "MapT",
PtrT: "PtrT",
RuneT: "RuneT",
SrcPkgT: "SrcPkgT",
StringT: "StringT",
StructT: "StructT",
UintT: "UintT",
Uint8T: "Uint8T",
Uint16T: "Uint16T",
Uint32T: "Uint32T",
Uint64T: "Uint64T",
UintptrT: "UintptrT",
ValueT: "ValueT",
}
func (c Cat) String() string {
if c < Cat(len(cats)) {
return cats[c]
}
return "Cat(" + strconv.Itoa(int(c)) + ")"
}
// StructField type defines a field in a struct
type StructField struct {
name string
embed bool
typ *Type
}
// Type defines the internal representation of types in the interpreter
type Type struct {
cat Cat // Type category
field []StructField // Array of struct fields if StrucT or InterfaceT
key *Type // Type of key element if MapT or nil
val *Type // Type of value element if ChanT, MapT, PtrT, AliasT or ArrayT
arg []*Type // Argument types if FuncT or nil
ret []*Type // Return types if FuncT or nil
method []*Node // Associated methods or nil
name string // name of type within its package for a defined type
pkgPath string // for a defined type, the package import path
size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
variadic bool // true if type is variadic
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
node *Node // root AST node of type definition
scope *Scope // type declaration scope (in case of re-parse incomplete type)
}
// nodeType returns a type definition for the corresponding AST subtree
func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) {
var err CfgError
if n.typ != nil && !n.typ.incomplete {
return n.typ, err
}
var t = &Type{node: n, scope: scope}
switch n.kind {
case Address, StarExpr:
t.cat = PtrT
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
case ArrayType:
t.cat = ArrayT
if len(n.child) > 1 {
// An array size is defined
var ok bool
if t.size, ok = n.child[0].val.(int); !ok {
if sym, _, ok := scope.lookup(n.child[0].ident); ok {
// Resolve symbol to get size value
if sym.typ != nil && sym.typ.cat == IntT {
if t.size, ok = sym.val.(int); !ok {
t.incomplete = true
}
} else {
t.incomplete = true
}
} else {
t.incomplete = true
}
}
t.val, err = nodeType(interp, scope, n.child[1])
t.incomplete = t.incomplete || t.val.incomplete
} else {
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
}
case BasicLit:
switch n.val.(type) {
case bool:
t.cat = BoolT
t.name = "bool"
case byte:
t.cat = ByteT
t.name = "byte"
t.untyped = true
case float32:
t.cat = Float32T
t.name = "float32"
t.untyped = true
case float64:
t.cat = Float64T
t.name = "float64"
t.untyped = true
case int:
t.cat = IntT
t.name = "int"
t.untyped = true
case rune:
t.cat = RuneT
t.name = "rune"
t.untyped = true
case string:
t.cat = StringT
t.name = "string"
t.untyped = true
default:
err = n.cfgError("missign support for type %T", n.val)
}
case UnaryExpr:
t, err = nodeType(interp, scope, n.child[0])
case BinaryExpr:
t, err = nodeType(interp, scope, n.child[0])
if err != nil {
return nil, err
}
if t.untyped {
var t1 *Type
t1, err = nodeType(interp, scope, n.child[1])
if !(t1.untyped && isInt(t1) && isFloat(t)) {
t = t1
}
}
case CallExpr, CompositeLitExpr:
t, err = nodeType(interp, scope, n.child[0])
case ChanType:
t.cat = ChanT
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
case Ellipsis:
t, err = nodeType(interp, scope, n.child[0])
t.variadic = true
case FuncLit:
t, err = nodeType(interp, scope, n.child[2])
case FuncType:
t.cat = FuncT
// Handle input parameters
for _, arg := range n.child[0].child {
cl := len(arg.child) - 1
typ, err := nodeType(interp, scope, arg.child[cl])
if err != nil {
return nil, err
}
t.arg = append(t.arg, typ)
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
t.arg = append(t.arg, typ)
}
t.incomplete = t.incomplete || typ.incomplete
}
if len(n.child) == 2 {
// Handle returned values
for _, ret := range n.child[1].child {
cl := len(ret.child) - 1
typ, err := nodeType(interp, scope, ret.child[cl])
if err != nil {
return nil, err
}
t.ret = append(t.ret, typ)
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
t.ret = append(t.ret, typ)
}
t.incomplete = t.incomplete || typ.incomplete
}
}
case Ident:
if sym, _, found := scope.lookup(n.ident); found {
t = sym.typ
} else {
t.incomplete = true
}
case InterfaceType:
t.cat = InterfaceT
for _, field := range n.child[0].child {
typ, err := nodeType(interp, scope, field.child[1])
if err != nil {
return nil, err
}
t.field = append(t.field, StructField{name: field.child[0].ident, typ: typ})
t.incomplete = t.incomplete || typ.incomplete
}
case MapType:
t.cat = MapT
if t.key, err = nodeType(interp, scope, n.child[0]); err != nil {
return nil, err
}
if t.val, err = nodeType(interp, scope, n.child[1]); err != nil {
return nil, err
}
t.incomplete = t.key.incomplete || t.val.incomplete
case SelectorExpr:
pkg, name := n.child[0].ident, n.child[1].ident
if sym, _, found := scope.lookup(pkg); found {
if sym.typ == nil {
t.incomplete = true
break
}
switch sym.typ.cat {
case BinPkgT:
pkg := interp.binValue[sym.path]
if v, ok := pkg[name]; ok {
t.cat = ValueT
t.rtype = v.Type()
if isBinType(v) {
t.rtype = t.rtype.Elem()
}
} else {
t.incomplete = true
}
case SrcPkgT:
spkg := interp.scope[pkg]
if st, ok := spkg.sym[name]; ok && st.kind == Typ {
t = st.typ
}
}
} else {
err = n.cfgError("undefined package: %s", pkg)
}
// TODO: handle pkgsrc types
case StructType:
t.cat = StructT
for _, c := range n.child[0].child {
if len(c.child) == 1 {
typ, err := nodeType(interp, scope, c.child[0])
if err != nil {
return nil, err
}
t.field = append(t.field, StructField{name: c.child[0].ident, embed: true, typ: typ})
t.incomplete = t.incomplete || typ.incomplete
} else {
l := len(c.child)
typ, err := nodeType(interp, scope, c.child[l-1])
if err != nil {
return nil, err
}
t.incomplete = t.incomplete || typ.incomplete
for _, d := range c.child[:l-1] {
t.field = append(t.field, StructField{name: d.ident, typ: typ})
}
}
}
default:
err = n.cfgError("type definition not implemented: %s", n.kind)
}
return t, err
}
var zeroValues [MaxT]reflect.Value
func init() {
zeroValues[BoolT] = reflect.ValueOf(false)
zeroValues[ByteT] = reflect.ValueOf(byte(0))
zeroValues[Complex64T] = reflect.ValueOf(complex64(0))
zeroValues[Complex128T] = reflect.ValueOf(complex128(0))
zeroValues[ErrorT] = reflect.ValueOf(new(error)).Elem()
zeroValues[Float32T] = reflect.ValueOf(float32(0))
zeroValues[Float64T] = reflect.ValueOf(float64(0))
zeroValues[IntT] = reflect.ValueOf(int(0))
zeroValues[Int8T] = reflect.ValueOf(int8(0))
zeroValues[Int16T] = reflect.ValueOf(int16(0))
zeroValues[Int32T] = reflect.ValueOf(int32(0))
zeroValues[Int64T] = reflect.ValueOf(int64(0))
zeroValues[RuneT] = reflect.ValueOf(rune(0))
zeroValues[StringT] = reflect.ValueOf("")
zeroValues[UintT] = reflect.ValueOf(uint(0))
zeroValues[Uint8T] = reflect.ValueOf(uint8(0))
zeroValues[Uint16T] = reflect.ValueOf(uint16(0))
zeroValues[Uint32T] = reflect.ValueOf(uint32(0))
zeroValues[Uint64T] = reflect.ValueOf(uint64(0))
zeroValues[UintptrT] = reflect.ValueOf(uintptr(0))
}
// if type is incomplete, re-parse it.
func (t *Type) finalize() (*Type, error) {
var err CfgError
if t.incomplete {
t, err = nodeType(t.node.interp, t.scope, t.node)
t.node.typ = t
if t.incomplete && err == nil {
err = t.node.cfgError("incomplete type")
}
}
return t, err
}
// id returns a unique type identificator string
func (t *Type) id() string {
// TODO: if res is nil, build identity from String()
res := ""
if t.cat == ValueT {
res = t.rtype.PkgPath() + "." + t.rtype.Name()
} else {
res = t.pkgPath + "." + t.name
}
return res
}
// zero instantiates and return a zero value object for the given type during execution
func (t *Type) zero() (v reflect.Value, err error) {
if t, err = t.finalize(); err != nil {
return v, err
}
switch t.cat {
case AliasT:
v, err = t.val.zero()
case ArrayT, StructT:
v = reflect.New(t.TypeOf()).Elem()
case ValueT:
v = reflect.New(t.rtype).Elem()
default:
v = zeroValues[t.cat]
}
return v, err
}
// fieldIndex returns the field index from name in a struct, or -1 if not found
func (t *Type) fieldIndex(name string) int {
if t.cat == PtrT {
return t.val.fieldIndex(name)
}
for i, field := range t.field {
if name == field.name {
return i
}
}
return -1
}
// fieldSeq returns the field type from the list of field indexes
func (t *Type) fieldSeq(seq []int) *Type {
ft := t
for _, i := range seq {
if ft.cat == PtrT {
ft = ft.val
}
ft = ft.field[i].typ
}
return ft
}
// lookupField returns a list of indices, i.e. a path to access a field in a struct object
func (t *Type) lookupField(name string) []int {
var index []int
if fi := t.fieldIndex(name); fi < 0 {
for i, f := range t.field {
if f.typ.cat != StructT {
continue
}
if index2 := f.typ.lookupField(name); len(index2) > 0 {
index = append([]int{i}, index2...)
break
}
}
} else {
index = append(index, fi)
}
return index
}
// getMethod returns a pointer to the method definition
func (t *Type) getMethod(name string) *Node {
for _, m := range t.method {
if name == m.ident {
return m
}
}
return nil
}
// lookupMethod returns a pointer to method definition associated to type t
// and the list of indices to access the right struct field, in case of a promoted method
func (t *Type) lookupMethod(name string) (*Node, []int) {
if t.cat == PtrT {
return t.val.lookupMethod(name)
}
var index []int
m := t.getMethod(name)
if m == nil {
for i, f := range t.field {
if f.embed {
if n, index2 := f.typ.lookupMethod(name); n != nil {
index = append([]int{i}, index2...)
return n, index
}
}
}
}
return m, index
}
func exportName(s string) string {
if canExport(s) {
return s
}
return "X" + s
}
// TypeOf returns the reflection type of dynamic interpreter type t.
func (t *Type) TypeOf() reflect.Type {
if t.rtype != nil {
return t.rtype
}
switch t.cat {
case ArrayT:
if t.size > 0 {
t.rtype = reflect.ArrayOf(t.size, t.val.TypeOf())
} else {
t.rtype = reflect.SliceOf(t.val.TypeOf())
}
case ChanT:
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.TypeOf())
case ErrorT:
t.rtype = reflect.TypeOf(new(error)).Elem()
case FuncT:
in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret))
for i, v := range t.arg {
in[i] = v.TypeOf()
}
for i, v := range t.ret {
out[i] = v.TypeOf()
}
t.rtype = reflect.FuncOf(in, out, false)
case InterfaceT:
t.rtype = reflect.TypeOf(new(interface{})).Elem()
case MapT:
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
case PtrT:
t.rtype = reflect.PtrTo(t.val.TypeOf())
case StructT:
var fields []reflect.StructField
for _, f := range t.field {
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.TypeOf()}
fields = append(fields, field)
}
t.rtype = reflect.StructOf(fields)
default:
if z, _ := t.zero(); z.IsValid() {
t.rtype = z.Type()
}
}
return t.rtype
}
func isInt(t *Type) bool {
switch t.TypeOf().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true
}
return false
}
func isUint(t *Type) bool {
switch t.TypeOf().Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true
}
return false
}
func isFloat(t *Type) bool {
switch t.TypeOf().Kind() {
case reflect.Float32, reflect.Float64:
return true
}
return false
}
func isByteArray(t *Type) bool {
r := t.TypeOf()
k := r.Kind()
return (k == reflect.Array || k == reflect.Slice) && r.Elem().Kind() == reflect.Uint8
}
func isFloat32(t *Type) bool { return t.TypeOf().Kind() == reflect.Float32 }
func isFloat64(t *Type) bool { return t.TypeOf().Kind() == reflect.Float64 }
func isUntypedNumber(t *Type) bool { return t.untyped && (isInt(t) || isFloat(t)) }
func isNumber(t *Type) bool { return isInt(t) || isFloat(t) }
func isString(t *Type) bool { return t.TypeOf().Kind() == reflect.String }