1217 lines
33 KiB
Go
1217 lines
33 KiB
Go
package interp
|
|
|
|
import (
|
|
"log"
|
|
"reflect"
|
|
"strconv"
|
|
"unicode"
|
|
)
|
|
|
|
type SymKind uint
|
|
|
|
const (
|
|
Const SymKind = iota
|
|
Typ
|
|
Var
|
|
Func
|
|
Bin
|
|
Bltn
|
|
)
|
|
|
|
var symKinds = [...]string{
|
|
Const: "Const",
|
|
Var: "Var",
|
|
Func: "Func",
|
|
Bin: "Bin",
|
|
Bltn: "Bltn",
|
|
}
|
|
|
|
func (k SymKind) String() string {
|
|
if k < SymKind(len(symKinds)) {
|
|
return symKinds[k]
|
|
}
|
|
return "SymKind(" + strconv.Itoa(int(k)) + ")"
|
|
}
|
|
|
|
// A Symbol represents an interpreter object such as type, constant, var, func, builtin or binary object
|
|
type Symbol struct {
|
|
kind SymKind
|
|
typ *Type // Type of value
|
|
node *Node // Node value if index is negative
|
|
index int // index of value in frame or -1
|
|
val interface{} // default value (used for constants)
|
|
path string // package path if typ.cat is SrcPkgT or BinPkgT
|
|
builtin BuiltinGenerator // Builtin function or nil
|
|
global bool // true if symbol is defined in global space
|
|
constant bool // true if symbol value is constant
|
|
}
|
|
|
|
// A SymMap stores symbols indexed by name
|
|
type SymMap map[string]*Symbol
|
|
|
|
// Scope type stores the list of visible symbols at current scope level
|
|
type Scope struct {
|
|
anc *Scope // Ancestor upper scope
|
|
level int // Frame level: number of frame indirections to access var during execution
|
|
size int // Frame size: number of entries to allocate during execution (package scope only)
|
|
sym SymMap // Map of symbols defined in this current scope
|
|
global bool // true if scope refers to global space (single frame for universe and package level scopes)
|
|
}
|
|
|
|
// Create a new scope and chain it to the current one
|
|
func (s *Scope) push(indirect int) *Scope {
|
|
var size int
|
|
if indirect == 0 {
|
|
size = s.size // propagate size as scopes at same level share the same frame
|
|
}
|
|
global := s.global && indirect == 0
|
|
return &Scope{anc: s, global: global, level: s.level + indirect, size: size, sym: map[string]*Symbol{}}
|
|
}
|
|
|
|
func (s *Scope) pop() *Scope {
|
|
if s.level == s.anc.level {
|
|
s.anc.size = s.size // propagate size as scopes at same level share the same frame
|
|
}
|
|
return s.anc
|
|
}
|
|
|
|
// Lookup for a symbol in the current scope, and upper ones if not found
|
|
func (s *Scope) lookup(ident string) (*Symbol, int, bool) {
|
|
level := s.level
|
|
for s != nil {
|
|
if sym, ok := s.sym[ident]; ok {
|
|
return sym, level - s.level, true
|
|
}
|
|
s = s.anc
|
|
}
|
|
return nil, 0, false
|
|
}
|
|
|
|
func (s *Scope) getType(ident string) *Type {
|
|
var t *Type
|
|
if sym, _, found := s.lookup(ident); found {
|
|
if sym.kind == Typ {
|
|
t = sym.typ
|
|
}
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Inc increments the size of the scope data frame and returns the new size
|
|
func (scope *Scope) inc(interp *Interpreter) int {
|
|
if scope.global {
|
|
interp.fsize++
|
|
scope.size = interp.fsize
|
|
} else {
|
|
scope.size++
|
|
}
|
|
return scope.size
|
|
}
|
|
|
|
// Cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
|
|
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
|
// variables. A list of nodes of init functions is returned.
|
|
// Following this pass, the CFG is ready to run
|
|
func (interp *Interpreter) Cfg(root *Node) []*Node {
|
|
scope := interp.universe
|
|
var loop, loopRestart *Node
|
|
var initNodes []*Node
|
|
var exports *BinMap
|
|
var expval *ValueMap
|
|
var iotaValue int
|
|
var pkgName string
|
|
|
|
root.Walk(func(n *Node) bool {
|
|
// Pre-order processing
|
|
switch n.kind {
|
|
case Define, AssignStmt:
|
|
if l := len(n.child); n.anc.kind == ConstDecl && l == 1 {
|
|
// Implicit iota assignment. TODO: replicate previous explicit assignment
|
|
n.child = append(n.child, &Node{anc: n, interp: interp, kind: Ident, ident: "iota"})
|
|
} else if l%2 == 1 {
|
|
// Odd number of children: remove the type node, useless for assign
|
|
i := l / 2
|
|
n.child = append(n.child[:i], n.child[i+1:]...)
|
|
}
|
|
|
|
case BlockStmt:
|
|
scope = scope.push(0)
|
|
|
|
case File:
|
|
pkgName = n.child[0].ident
|
|
if _, ok := interp.scope[pkgName]; !ok {
|
|
interp.scope[pkgName] = scope.push(0)
|
|
}
|
|
scope = interp.scope[pkgName]
|
|
scope.size = interp.fsize
|
|
if pkg, ok := interp.Exports[pkgName]; ok {
|
|
exports = pkg
|
|
expval = interp.Expval[pkgName]
|
|
} else {
|
|
exports = &BinMap{}
|
|
interp.Exports[pkgName] = exports
|
|
expval = &ValueMap{}
|
|
interp.Expval[pkgName] = expval
|
|
}
|
|
|
|
case For0, ForRangeStmt:
|
|
loop, loopRestart = n, n.child[0]
|
|
scope = scope.push(0)
|
|
|
|
case For1, For2, For3, For3a, For4:
|
|
loop, loopRestart = n, n.child[len(n.child)-1]
|
|
scope = scope.push(0)
|
|
|
|
case FuncDecl, FuncLit:
|
|
if n.child[1].ident == "init" {
|
|
initNodes = append(initNodes, n)
|
|
}
|
|
// Add a frame indirection level as we enter in a func
|
|
scope = scope.push(1)
|
|
if len(n.child[2].child) == 2 {
|
|
// allocate entries for return values at start of frame
|
|
scope.size += len(n.child[2].child[1].child)
|
|
}
|
|
|
|
case If0, If1, If2, If3:
|
|
scope = scope.push(0)
|
|
|
|
case Switch0:
|
|
// Make sure default clause is in last position
|
|
c := n.child[1].child
|
|
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
|
|
c[i], c[l] = c[l], c[i]
|
|
}
|
|
scope = scope.push(0)
|
|
|
|
case TypeSpec:
|
|
// processing already done in GTA pass
|
|
return false
|
|
|
|
case ArrayType, BasicLit, ChanType, MapType, StructType:
|
|
n.typ = nodeType(interp, scope, n)
|
|
return false
|
|
}
|
|
return true
|
|
}, func(n *Node) {
|
|
// Post-order processing
|
|
switch n.kind {
|
|
case Address:
|
|
wireChild(n)
|
|
n.typ = &Type{cat: PtrT, val: n.child[0].typ}
|
|
|
|
case Define, AssignStmt:
|
|
wireChild(n)
|
|
if n.kind == Define {
|
|
// Force definition of assigned ident in current scope
|
|
name := n.child[0].ident
|
|
scope.inc(interp)
|
|
n.child[0].val = n.child[1].val
|
|
n.child[0].typ = n.child[1].typ
|
|
n.child[0].findex = scope.size
|
|
if scope.global {
|
|
if sym, _, ok := scope.lookup(name); ok {
|
|
n.child[0].findex = sym.index
|
|
} else {
|
|
scope.sym[name] = &Symbol{index: scope.size, global: true, kind: Var}
|
|
}
|
|
} else {
|
|
scope.sym[name] = &Symbol{index: scope.size, kind: Var}
|
|
}
|
|
if n.child[1].action == GetFunc {
|
|
log.Println(n.index, "assign getFunc")
|
|
scope.sym[name].index = -1
|
|
scope.sym[name].node = n.child[1]
|
|
}
|
|
if n.child[1].kind == BasicLit {
|
|
scope.sym[name].val = n.child[1].val
|
|
}
|
|
}
|
|
n.findex = n.child[0].findex
|
|
n.val = n.child[0].val
|
|
// Propagate type
|
|
// TODO: Check that existing destination type matches source type
|
|
n.typ = n.child[0].typ
|
|
if n.child[1].action == Recv {
|
|
n.gen = nop
|
|
n.child[1].findex = n.child[0].findex // Set recv address to LHS
|
|
n.child[0].typ = n.child[1].typ.val
|
|
n.typ = n.child[0].typ
|
|
}
|
|
if sym, level, ok := scope.lookup(n.child[0].ident); ok {
|
|
sym.typ = n.typ
|
|
n.level = level
|
|
}
|
|
// If LHS is an indirection, get reference instead of value, to allow setting
|
|
if n.child[0].action == GetIndex {
|
|
if n.child[0].child[0].typ.cat == MapT {
|
|
n.gen = assignMap
|
|
n.child[0].gen = nop // skip getIndexMap
|
|
} else if n.child[0].child[0].typ.cat == PtrT {
|
|
// Handle the case where the receiver is a pointer to an object
|
|
n.child[0].gen = getPtrIndexAddr
|
|
n.gen = assignPtrField
|
|
}
|
|
} else if n.child[0].action == Star {
|
|
n.findex = n.child[0].child[0].findex
|
|
n.gen = indirectAssign
|
|
}
|
|
if n.anc.kind == ConstDecl {
|
|
iotaValue++
|
|
}
|
|
|
|
case IncDecStmt:
|
|
wireChild(n)
|
|
n.findex = n.child[0].findex
|
|
n.child[0].typ = scope.getType("int")
|
|
n.typ = n.child[0].typ
|
|
if sym, level, ok := scope.lookup(n.child[0].ident); ok {
|
|
sym.typ = n.typ
|
|
n.level = level
|
|
}
|
|
if n.child[0].action == Star {
|
|
n.findex = n.child[0].child[0].findex
|
|
n.gen = indirectInc
|
|
}
|
|
|
|
case DefineX, AssignXStmt:
|
|
wireChild(n)
|
|
l := len(n.child) - 1
|
|
if n.kind == DefineX {
|
|
// retrieve assigned value types from call signature
|
|
var types []*Type
|
|
switch n.child[l].kind {
|
|
case CallExpr:
|
|
if funtype := n.child[l].child[0].typ; funtype.cat == ValueT {
|
|
// Handle functions imported from runtime
|
|
for i := 0; i < funtype.rtype.NumOut(); i++ {
|
|
types = append(types, &Type{cat: ValueT, rtype: funtype.rtype.Out(i)})
|
|
}
|
|
} else {
|
|
types = funtype.ret
|
|
}
|
|
|
|
case IndexExpr:
|
|
types = append(types, n.child[l].child[0].typ.val, scope.getType("bool"))
|
|
n.child[l].gen = getIndexMap2
|
|
n.gen = nop
|
|
|
|
case TypeAssertExpr:
|
|
types = append(types, n.child[l].child[1].typ, scope.getType("error"))
|
|
|
|
default:
|
|
log.Fatalln(n.index, "Assign expression unsupported:", n.child[l].kind)
|
|
}
|
|
// Force definition of assigned idents in current scope
|
|
for i, c := range n.child[:l] {
|
|
//log.Println(c.ident, i, types)
|
|
c.findex = scope.inc(interp)
|
|
scope.sym[c.ident] = &Symbol{index: scope.size, global: scope.global, kind: Var}
|
|
if i < len(types) {
|
|
scope.sym[c.ident].typ = types[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
case BinaryExpr:
|
|
wireChild(n)
|
|
n.findex = scope.inc(interp)
|
|
nilSym := interp.universe.sym["nil"]
|
|
switch n.action {
|
|
case NotEqual:
|
|
n.typ = scope.getType("bool")
|
|
if n.child[0].sym == nilSym || n.child[1].sym == nilSym {
|
|
n.gen = isNotNil
|
|
}
|
|
case Equal:
|
|
n.typ = scope.getType("bool")
|
|
if n.child[0].sym == nilSym || n.child[1].sym == nilSym {
|
|
n.gen = isNil
|
|
}
|
|
case Greater, Lower:
|
|
n.typ = scope.getType("bool")
|
|
default:
|
|
n.typ = n.child[0].typ
|
|
}
|
|
|
|
case IndexExpr:
|
|
wireChild(n)
|
|
n.findex = scope.inc(interp)
|
|
n.typ = n.child[0].typ.val
|
|
n.recv = n
|
|
if n.child[0].typ.cat == MapT {
|
|
scope.size++ // Reserve an entry for getIndexMap 2nd return value
|
|
n.gen = getIndexMap
|
|
} else if n.child[0].typ.cat == ArrayT {
|
|
n.gen = getIndexArray
|
|
}
|
|
|
|
case BlockStmt:
|
|
wireChild(n)
|
|
if len(n.child) > 0 {
|
|
n.findex = n.child[len(n.child)-1].findex
|
|
}
|
|
scope = scope.pop()
|
|
|
|
case ConstDecl:
|
|
wireChild(n)
|
|
iotaValue = 0
|
|
|
|
case DeclStmt, ExprStmt, VarDecl, ParenExpr, SendStmt:
|
|
wireChild(n)
|
|
n.findex = n.child[len(n.child)-1].findex
|
|
|
|
case Break:
|
|
n.tnext = loop
|
|
|
|
case CallExpr:
|
|
wireChild(n)
|
|
n.findex = scope.inc(interp)
|
|
log.Println(n.index, "call", n.child[0].typ)
|
|
if n.child[0].sym != nil && n.child[0].sym.kind == Bltn {
|
|
// Call an internal go builtin
|
|
n.gen = n.child[0].sym.builtin
|
|
n.child[0].typ = &Type{cat: BuiltinT}
|
|
switch n.child[0].ident {
|
|
case "cap", "len":
|
|
n.typ = scope.getType("int")
|
|
case "make":
|
|
if n.typ = scope.getType(n.child[1].ident); n.typ == nil {
|
|
n.typ = nodeType(interp, scope, n.child[1])
|
|
}
|
|
n.child[1].val = n.typ
|
|
n.child[1].kind = BasicLit
|
|
}
|
|
} else if n.child[0].isType(scope) {
|
|
// Type conversion expression
|
|
n.typ = n.child[0].typ
|
|
if n.typ.cat == AliasT {
|
|
n.gen = convert
|
|
} else {
|
|
if n.child[1].typ.cat == FuncT {
|
|
// Convert type of an interpreter function to another binary type:
|
|
// generate a different callback wrapper
|
|
n.gen = convertFuncBin
|
|
} else {
|
|
n.gen = convertBin
|
|
}
|
|
}
|
|
} else if n.child[0].typ.cat == ValueT {
|
|
log.Println(n.index, "callBin", n.child[0].val)
|
|
n.gen = callBin
|
|
}
|
|
// Reserve entries in frame to store results of call
|
|
scope.size += n.fsize
|
|
if scope.global {
|
|
interp.fsize += n.fsize
|
|
scope.size = interp.fsize
|
|
} else {
|
|
scope.size += n.fsize
|
|
}
|
|
n.recv = n.child[0].recv
|
|
|
|
/*
|
|
else if n.child[0].kind == SelectorImport {
|
|
// TODO: Should process according to child type, not kind.
|
|
n.fsize = n.child[0].fsize
|
|
var rtype reflect.Type
|
|
switch t := n.child[0].val.(type) {
|
|
case reflect.Type:
|
|
rtype = n.child[0].val.(reflect.Type)
|
|
case reflect.Value:
|
|
rtype = n.child[0].val.(reflect.Value).Type()
|
|
default:
|
|
log.Printf("unexpected type %T\n", t)
|
|
}
|
|
if rtype.NumOut() > 0 {
|
|
n.typ = &Type{cat: ValueT, rtype: rtype.Out(0)}
|
|
}
|
|
n.child[0].kind = BasicLit
|
|
for i, c := range n.child[1:] {
|
|
// Wrap function defintion so it can be called from runtime
|
|
if c.kind == FuncLit {
|
|
n.child[1+i].rval = reflect.MakeFunc(rtype.In(i), c.wrapNode)
|
|
n.child[1+i].kind = Rvalue
|
|
} else if c.ident == "nil" {
|
|
n.child[1+i].rval = reflect.New(rtype.In(i)).Elem()
|
|
n.child[1+i].kind = Rvalue
|
|
} else if c.typ != nil && c.typ.cat == FuncT {
|
|
n.child[1+i].rval = reflect.MakeFunc(rtype.In(i), c.wrapNode)
|
|
n.child[1+i].kind = Rvalue
|
|
}
|
|
}
|
|
// TODO: handle multiple return value
|
|
if len(n.child) == 2 && n.child[1].fsize > 1 {
|
|
n.gen = callBinX
|
|
} else {
|
|
n.gen = callBin
|
|
}
|
|
} else if n.child[0].kind == SelectorExpr {
|
|
if n.child[0].typ.cat == ValueT {
|
|
n.gen = callBinMethod
|
|
// TODO: handle multiple return value (_test/time2.go)
|
|
n.child[0].kind = BasicLit // Temporary hack for force value() to return n.val at run
|
|
n.typ = &Type{cat: ValueT, rtype: n.child[0].typ.rtype}
|
|
n.fsize = n.child[0].fsize
|
|
} else if n.child[0].typ.cat == PtrT && n.child[0].typ.val.cat == ValueT {
|
|
n.gen = callBinMethod
|
|
n.child[0].kind = BasicLit // Temporary hack for force value() to return n.val at run
|
|
n.fsize = n.child[0].fsize
|
|
// TODO: handle type ?
|
|
} else if n.child[0].typ.cat == SrcPkgT {
|
|
n.val = n.child[0].val
|
|
if def := n.val.(*Node); def != nil {
|
|
// Reserve as many frame entries as nb of ret values for called function
|
|
// node frame index should point to the first entry
|
|
j := len(def.child[2].child) - 1
|
|
l := len(def.child[2].child[j].child) // Number of return values for def
|
|
if l == 1 {
|
|
// If def returns exactly one value, propagate its type in call node.
|
|
// Multiple return values will be handled differently through AssignX.
|
|
n.typ = scope.getType(def.child[2].child[j].child[0].child[0].ident)
|
|
}
|
|
n.fsize = l
|
|
}
|
|
} else {
|
|
// Get method and receiver path, store them in node static value for run
|
|
if methodDecl, ok := n.child[0].val.(*Node); ok {
|
|
// method is already resolved, use it
|
|
if len(methodDecl.child[2].child) > 1 {
|
|
// Allocate frame for method return values (if any)
|
|
n.fsize = len(methodDecl.child[2].child[1].child)
|
|
n.typ = methodDecl.typ.ret[0]
|
|
// TODO: handle multiple return values
|
|
} else {
|
|
n.fsize = 0
|
|
}
|
|
} else {
|
|
log.Println(n.index, "unresolve call")
|
|
// method must be resolved here due to declaration after use
|
|
}
|
|
n.child[0].findex = -1 // To force reading value from node instead of frame (methods)
|
|
}
|
|
} else if sym, _, _ := scope.lookup(n.child[0].ident); sym != nil {
|
|
if sym.typ != nil && sym.typ.cat == BinT {
|
|
n.gen = callBin
|
|
n.typ = &Type{cat: ValueT}
|
|
r := sym.val.(reflect.Value)
|
|
n.child[0].fsize = r.Type().NumOut()
|
|
n.child[0].val = r
|
|
n.child[0].kind = BasicLit
|
|
} else if sym.typ != nil && sym.typ.cat == ValueT {
|
|
n.gen = callDirectBin
|
|
n.typ = &Type{cat: ValueT}
|
|
} else {
|
|
n.val = sym.node
|
|
n.fsize = len(sym.typ.ret)
|
|
if n.fsize == 1 {
|
|
// If called func returns exactly one value, propagate its type in call node.
|
|
// Multiple return values will be handled differently through AssignX.
|
|
n.typ = sym.typ.ret[0]
|
|
}
|
|
}
|
|
} else if n.child[0].kind == SelectorSrc {
|
|
// Forward type of first returned value
|
|
// Multiple return values will be handled differently through AssignX.
|
|
if len(n.child[0].typ.ret) > 0 {
|
|
n.typ = n.child[0].typ.ret[0]
|
|
n.fsize = len(n.child[0].typ.ret)
|
|
}
|
|
}
|
|
*/
|
|
|
|
case CaseClause:
|
|
n.findex = scope.inc(interp)
|
|
n.tnext = n.child[len(n.child)-1].start
|
|
|
|
case CompositeLitExpr:
|
|
wireChild(n)
|
|
n.findex = scope.inc(interp)
|
|
if n.child[0].typ == nil {
|
|
n.child[0].typ = nodeType(interp, scope, n.child[0])
|
|
}
|
|
// TODO: Check that composite litteral expr matches corresponding type
|
|
n.typ = n.child[0].typ
|
|
switch n.typ.cat {
|
|
case ArrayT:
|
|
n.gen = arrayLit
|
|
case MapT:
|
|
n.gen = mapLit
|
|
case StructT:
|
|
n.action, n.gen = CompositeLit, compositeLit
|
|
// Handle object assign from sparse key / values
|
|
if len(n.child) > 1 && n.child[1].kind == KeyValueExpr {
|
|
n.gen = compositeSparse
|
|
n.typ = nodeType(interp, scope, n.child[0])
|
|
for _, c := range n.child[1:] {
|
|
c.findex = n.typ.fieldIndex(c.child[0].ident)
|
|
}
|
|
}
|
|
}
|
|
|
|
case Continue:
|
|
n.tnext = loopRestart
|
|
|
|
case Field:
|
|
// A single child node (no ident, just type) means that the field refers
|
|
// to a return value, and space on frame should be accordingly allocated.
|
|
// Otherwise, just point to corresponding location in frame, resolved in
|
|
// ident child.
|
|
l := len(n.child) - 1
|
|
n.typ = nodeType(interp, scope, n.child[l])
|
|
if l == 0 {
|
|
n.findex = scope.inc(interp)
|
|
} else {
|
|
for _, f := range n.child[:l] {
|
|
scope.sym[f.ident].typ = n.typ
|
|
f.typ = n.typ
|
|
}
|
|
}
|
|
|
|
case File:
|
|
wireChild(n)
|
|
scope = scope.pop()
|
|
n.fsize = scope.size + 1
|
|
n.types = frameTypes(n, n.fsize)
|
|
|
|
case For0: // for {}
|
|
body := n.child[0]
|
|
n.start = body.start
|
|
body.tnext = n.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case For1: // for cond {}
|
|
cond, body := n.child[0], n.child[1]
|
|
n.start = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case For2: // for init; cond; {}
|
|
init, cond, body := n.child[0], n.child[1], n.child[2]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case For3: // for ; cond; post {}
|
|
cond, post, body := n.child[0], n.child[1], n.child[2]
|
|
n.start = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = post.start
|
|
post.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case For3a: // for int; ; post {}
|
|
init, post, body := n.child[0], n.child[1], n.child[2]
|
|
n.start = init.start
|
|
init.tnext = body.start
|
|
body.tnext = post.start
|
|
post.tnext = body.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case For4: // for init; cond; post {}
|
|
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = post.start
|
|
post.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.pop()
|
|
|
|
case ForRangeStmt:
|
|
loop, loopRestart = nil, nil
|
|
n.start = n.child[0].start
|
|
//n.findex = n.child[0].findex
|
|
n.child[0].fnext = n
|
|
scope = scope.pop()
|
|
|
|
case FuncDecl:
|
|
n.flen = scope.size + 1
|
|
if len(n.child[0].child) > 0 {
|
|
// Method: restore receiver frame location (used at run)
|
|
n.framepos = append(n.framepos, n.child[0].child[0].child[0].findex)
|
|
}
|
|
n.framepos = append(n.framepos, n.child[2].framepos...)
|
|
scope = scope.pop()
|
|
funcName := n.child[1].ident
|
|
if canExport(funcName) {
|
|
(*exports)[funcName] = reflect.MakeFunc(n.child[2].typ.TypeOf(), n.wrapNode).Interface()
|
|
(*expval)[funcName] = reflect.MakeFunc(n.child[2].typ.TypeOf(), n.wrapNode)
|
|
}
|
|
n.typ = n.child[2].typ
|
|
n.val = n
|
|
n.start = n.child[3].start
|
|
interp.scope[pkgName].sym[funcName].index = -1 // to force value to n.val
|
|
interp.scope[pkgName].sym[funcName].typ = n.typ
|
|
interp.scope[pkgName].sym[funcName].kind = Func
|
|
interp.scope[pkgName].sym[funcName].node = n
|
|
n.types = frameTypes(n, n.flen)
|
|
genFun(n)
|
|
|
|
case FuncLit:
|
|
n.typ = n.child[2].typ
|
|
n.val = n
|
|
n.flen = scope.size + 1
|
|
scope = scope.pop()
|
|
n.types = frameTypes(n, n.flen)
|
|
n.framepos = n.child[2].framepos
|
|
|
|
case FuncType:
|
|
n.typ = nodeType(interp, scope, n)
|
|
// Store list of parameter frame indices in framepos
|
|
for _, c := range n.child[0].child {
|
|
for _, f := range c.child[:len(c.child)-1] {
|
|
n.framepos = append(n.framepos, f.findex)
|
|
}
|
|
}
|
|
// TODO: do the same for return values
|
|
|
|
case GoStmt:
|
|
wireChild(n)
|
|
// TODO: should error if call expression refers to a builtin
|
|
|
|
case Ident:
|
|
if n.anc.kind == File || (n.anc.kind == SelectorExpr && n.anc.child[0] != n) || (n.anc.kind == KeyValueExpr && n.anc.child[0] == n) {
|
|
// Skip symbol creation/lookup for idents used as key
|
|
} else if l := len(n.anc.child); n.anc.kind == Field && l > 1 && n.anc.child[l-1] != n {
|
|
// Create a new local symbol for func argument
|
|
n.findex = scope.inc(interp)
|
|
scope.sym[n.ident] = &Symbol{index: scope.size, kind: Var}
|
|
} else if sym, level, ok := scope.lookup(n.ident); ok {
|
|
// Found symbol, populate node info
|
|
n.typ, n.findex, n.level = sym.typ, sym.index, level
|
|
if n.findex < 0 {
|
|
n.val = sym.node
|
|
n.kind = sym.node.kind
|
|
} else {
|
|
n.sym = sym
|
|
if sym.kind == Const && sym.val != nil {
|
|
n.val = sym.val
|
|
n.kind = BasicLit
|
|
} else if n.ident == "iota" {
|
|
n.val = iotaValue
|
|
n.kind = BasicLit
|
|
} else if n.ident == "nil" {
|
|
n.kind = BasicLit
|
|
n.val = nil
|
|
}
|
|
}
|
|
} else {
|
|
if n.ident == "_" || n.anc.kind == Define || n.anc.kind == DefineX || n.anc.kind == RangeStmt || n.anc.kind == ValueSpec {
|
|
// Create a new local symbol for func argument or local var definition
|
|
n.findex = scope.inc(interp)
|
|
scope.sym[n.ident] = &Symbol{index: scope.size, global: scope.global, kind: Var}
|
|
n.sym = scope.sym[n.ident]
|
|
} else {
|
|
log.Println(n.index, "unresolved global symbol", n.ident)
|
|
}
|
|
}
|
|
|
|
case If0: // if cond {}
|
|
cond, tbody := n.child[0], n.child[1]
|
|
n.start = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = n
|
|
tbody.tnext = n
|
|
scope = scope.pop()
|
|
|
|
case If1: // if cond {} else {}
|
|
cond, tbody, fbody := n.child[0], n.child[1], n.child[2]
|
|
n.start = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = fbody.start
|
|
tbody.tnext = n
|
|
fbody.tnext = n
|
|
scope = scope.pop()
|
|
|
|
case If2: // if init; cond {}
|
|
init, cond, tbody := n.child[0], n.child[1], n.child[2]
|
|
n.start = init.start
|
|
tbody.tnext = n
|
|
init.tnext = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = n
|
|
scope = scope.pop()
|
|
|
|
case If3: // if init; cond {} else {}
|
|
init, cond, tbody, fbody := n.child[0], n.child[1], n.child[2], n.child[3]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = fbody.start
|
|
tbody.tnext = n
|
|
fbody.tnext = n
|
|
scope = scope.pop()
|
|
|
|
case KeyValueExpr:
|
|
wireChild(n)
|
|
|
|
case LandExpr:
|
|
n.start = n.child[0].start
|
|
n.child[0].tnext = n.child[1].start
|
|
n.child[0].fnext = n
|
|
n.child[1].tnext = n
|
|
n.findex = scope.inc(interp)
|
|
n.typ = n.child[0].typ
|
|
|
|
case LorExpr:
|
|
n.start = n.child[0].start
|
|
n.child[0].tnext = n
|
|
n.child[0].fnext = n.child[1].start
|
|
n.child[1].tnext = n
|
|
n.findex = scope.inc(interp)
|
|
n.typ = n.child[0].typ
|
|
|
|
case RangeStmt:
|
|
n.start = n.child[2] // Get array or map object
|
|
n.child[2].tnext = n.child[0].start // then go to iterator init
|
|
n.child[0].tnext = n // then go to range function
|
|
n.tnext = n.child[3].start // then go to range body
|
|
n.child[3].tnext = n // then body go to range function (loop)
|
|
n.child[0].gen = empty // init filled later by generator
|
|
if n.child[2].typ.cat == MapT {
|
|
scope.sym[n.child[0].ident].typ = n.child[2].typ.key
|
|
n.child[0].typ = n.child[2].typ.key
|
|
n.gen = rangeMap
|
|
} else {
|
|
scope.sym[n.child[0].ident].typ = scope.getType("int")
|
|
n.child[0].typ = scope.getType("int")
|
|
}
|
|
vtype := n.child[2].typ.val
|
|
scope.sym[n.child[1].ident].typ = vtype
|
|
n.child[1].typ = vtype
|
|
|
|
case ReturnStmt:
|
|
wireChild(n)
|
|
n.tnext = nil
|
|
|
|
case SelectorExpr:
|
|
wireChild(n)
|
|
n.findex = scope.inc(interp)
|
|
n.typ = n.child[0].typ
|
|
n.recv = n.child[0].recv
|
|
if n.typ == nil {
|
|
log.Fatal("typ should not be nil:", n.index, n.child[0])
|
|
}
|
|
log.Println(n.index, "selector", n.child[0].ident+"."+n.child[1].ident, n.typ.cat)
|
|
if n.typ.cat == ValueT {
|
|
// Handle object defined in runtime
|
|
if method, ok := n.typ.rtype.MethodByName(n.child[1].ident); ok {
|
|
if method.Func.IsValid() {
|
|
n.rval = method.Func
|
|
n.typ.rtype = method.Func.Type()
|
|
n.gen = nop
|
|
} else {
|
|
n.val = method.Index
|
|
//n.gen = getIndexBinMethod
|
|
n.gen = nop
|
|
}
|
|
n.fsize = method.Type.NumOut()
|
|
} else {
|
|
// Method can be only resolved from value at execution
|
|
n.gen = getIndexBinMethod
|
|
}
|
|
} else if n.typ.cat == PtrT && n.typ.val.cat == ValueT {
|
|
// Handle pointer on object defined in runtime
|
|
if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
|
n.typ = &Type{cat: ValueT, rtype: field.Type}
|
|
n.val = field.Index
|
|
n.gen = getPtrIndexBin
|
|
} else if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
|
n.val = method.Func
|
|
n.fsize = method.Type.NumOut()
|
|
n.gen = nop
|
|
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
|
n.val = method.Func
|
|
n.fsize = method.Type.NumOut()
|
|
n.gen = nop
|
|
} else {
|
|
log.Println(n.index, "selector unresolved")
|
|
}
|
|
} else if n.typ.cat == BinPkgT {
|
|
// Resolve binary package symbol: a type or a value
|
|
name := n.child[1].ident
|
|
pkg := n.child[0].sym.path
|
|
if s, ok := interp.binValue[pkg][name]; ok {
|
|
n.kind = Rvalue
|
|
n.rval = s
|
|
n.typ = &Type{cat: ValueT, rtype: s.Type()}
|
|
if s.Kind() == reflect.Func {
|
|
n.fsize = n.typ.rtype.NumOut()
|
|
}
|
|
n.gen = nop
|
|
} else if s, ok := interp.binType[pkg][name]; ok {
|
|
n.kind = Rtype
|
|
n.typ = &Type{cat: ValueT, rtype: s}
|
|
n.gen = nop
|
|
if s.Kind() == reflect.Func {
|
|
n.fsize = s.NumOut()
|
|
}
|
|
}
|
|
} else if n.typ.cat == ArrayT {
|
|
n.typ = n.typ.val
|
|
n.gen = nop
|
|
} else if n.typ.cat == SrcPkgT {
|
|
// Resolve source package symbol
|
|
if sym, ok := interp.scope[n.child[0].ident].sym[n.child[1].ident]; ok {
|
|
n.val = sym.node
|
|
n.gen = nop
|
|
n.kind = SelectorSrc
|
|
n.typ = sym.typ
|
|
} else {
|
|
log.Println(n.index, "selector unresolved:", n.child[0].ident+"."+n.child[1].ident)
|
|
}
|
|
} else if fi := n.typ.fieldIndex(n.child[1].ident); fi >= 0 {
|
|
//log.Println(n.index, "selector field", fi)
|
|
// Resolve struct field index
|
|
if n.typ.cat == PtrT {
|
|
n.gen = getPtrIndex
|
|
}
|
|
n.typ = n.typ.fieldType(fi)
|
|
n.child[1].kind = BasicLit
|
|
n.child[1].val = fi
|
|
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
|
|
// Handle method
|
|
n.gen = nop
|
|
//n.kind = BasicLit
|
|
n.val = m
|
|
n.child[1].val = lind
|
|
n.typ = m.typ
|
|
n.recv = n.child[0]
|
|
log.Println(n.index, "method recv", m.index, n.recv.index, lind)
|
|
} else {
|
|
// Handle promoted field in embedded struct
|
|
if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
|
n.child[1].kind = BasicLit
|
|
n.child[1].val = ti
|
|
n.gen = getIndexSeq
|
|
} else {
|
|
log.Println(n.index, "Selector not found:", n.child[1].ident)
|
|
n.gen = nop
|
|
//panic("Field not found in selector")
|
|
}
|
|
}
|
|
|
|
case Switch0:
|
|
n.start = n.child[1].start
|
|
// Chain case clauses
|
|
clauses := n.child[1].child
|
|
l := len(clauses)
|
|
for i, c := range clauses[:l-1] {
|
|
// chain to next clause
|
|
c.tnext = c.child[1].start
|
|
c.child[1].tnext = n
|
|
c.fnext = clauses[i+1]
|
|
}
|
|
// Handle last clause
|
|
if c := clauses[l-1]; len(c.child) > 1 {
|
|
// No default clause
|
|
c.tnext = c.child[1].start
|
|
c.fnext = n
|
|
c.child[1].tnext = n
|
|
} else {
|
|
// Default
|
|
c.tnext = c.child[0].start
|
|
c.child[0].tnext = n
|
|
}
|
|
scope = scope.pop()
|
|
|
|
case TypeAssertExpr:
|
|
if n.child[1].typ == nil {
|
|
n.child[1].typ = scope.getType(n.child[1].ident)
|
|
}
|
|
n.typ = n.child[1].typ
|
|
|
|
case SliceExpr, UnaryExpr:
|
|
wireChild(n)
|
|
n.typ = n.child[0].typ
|
|
|
|
case ValueSpec:
|
|
l := len(n.child) - 1
|
|
if n.typ = n.child[l].typ; n.typ == nil {
|
|
n.typ = scope.getType(n.child[l].ident)
|
|
}
|
|
for _, c := range n.child[:l] {
|
|
c.typ = n.typ
|
|
scope.sym[c.ident].typ = n.typ
|
|
}
|
|
}
|
|
})
|
|
|
|
return initNodes
|
|
}
|
|
|
|
func genRun(n *Node) {
|
|
n.Walk(func(n *Node) bool {
|
|
if n.kind == FuncType && len(n.anc.child) == 4 {
|
|
setExec(n.anc.child[3].start)
|
|
}
|
|
if n.kind == VarDecl || n.kind == ConstDecl {
|
|
setExec(n.start)
|
|
return false
|
|
}
|
|
return true
|
|
}, nil)
|
|
}
|
|
|
|
// Find default case clause index of a switch statement, if any
|
|
func getDefault(n *Node) int {
|
|
for i, c := range n.child[1].child {
|
|
if len(c.child) == 1 {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// isType returns true if node refers to a type definition, false otherwise
|
|
func (n *Node) isType(scope *Scope) bool {
|
|
switch n.kind {
|
|
case ArrayType, ChanType, FuncType, MapType, StructType, Rtype:
|
|
return true
|
|
case Ident:
|
|
return scope.getType(n.ident) != nil
|
|
}
|
|
return false
|
|
}
|
|
|
|
// wireChild wires AST nodes for CFG in subtree
|
|
func wireChild(n *Node) {
|
|
// Set start node, in subtree (propagated to ancestors by post-order processing)
|
|
for _, child := range n.child {
|
|
switch child.kind {
|
|
case ArrayType, ChanType, FuncDecl, ImportDecl, MapType, BasicLit, Ident, TypeDecl:
|
|
continue
|
|
default:
|
|
n.start = child.start
|
|
}
|
|
break
|
|
}
|
|
|
|
// Chain sequential operations inside a block (next is right sibling)
|
|
for i := 1; i < len(n.child); i++ {
|
|
n.child[i-1].tnext = n.child[i].start
|
|
}
|
|
|
|
// Chain subtree next to self
|
|
for i := len(n.child) - 1; i >= 0; i-- {
|
|
switch n.child[i].kind {
|
|
case ArrayType, ChanType, ImportDecl, MapType, FuncDecl, BasicLit, Ident, TypeDecl:
|
|
continue
|
|
case Break, Continue, ReturnStmt:
|
|
// tnext is already computed, no change
|
|
default:
|
|
n.child[i].tnext = n
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
func canExport(name string) bool {
|
|
if r := []rune(name); len(r) > 0 && unicode.IsUpper(r[0]) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func getExec(n *Node) Builtin {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
if n.exec == nil {
|
|
setExec(n)
|
|
}
|
|
return n.exec
|
|
}
|
|
|
|
// setExec recursively sets the node exec builtin function
|
|
// it does nothing if exec is already defined
|
|
func setExec(n *Node) {
|
|
if n.exec != nil {
|
|
return
|
|
}
|
|
seen := map[*Node]bool{}
|
|
var set func(n *Node)
|
|
|
|
set = func(n *Node) {
|
|
if n == nil || n.exec != nil {
|
|
return
|
|
}
|
|
seen[n] = true
|
|
if n.tnext != nil && n.tnext.exec == nil {
|
|
if seen[n.tnext] {
|
|
m := n.tnext
|
|
n.tnext.exec = func(f *Frame) Builtin { return m.exec(f) }
|
|
} else {
|
|
set(n.tnext)
|
|
}
|
|
}
|
|
if n.fnext != nil && n.fnext.exec == nil {
|
|
if seen[n.fnext] {
|
|
m := n.fnext
|
|
n.fnext.exec = func(f *Frame) Builtin { return m.exec(f) }
|
|
} else {
|
|
set(n.fnext)
|
|
}
|
|
}
|
|
n.gen(n)
|
|
}
|
|
|
|
set(n)
|
|
}
|
|
|
|
func valueGenerator(n *Node, i int) func(*Frame) reflect.Value {
|
|
switch n.level {
|
|
case 0:
|
|
return func(f *Frame) reflect.Value { return f.data[i] }
|
|
case 1:
|
|
return func(f *Frame) reflect.Value {
|
|
//log.Println(n.index, i, f.anc.data[i])
|
|
return f.anc.data[i]
|
|
}
|
|
case 2:
|
|
return func(f *Frame) reflect.Value { return f.anc.anc.data[i] }
|
|
default:
|
|
return func(f *Frame) reflect.Value {
|
|
for level := n.level; level > 0; level-- {
|
|
f = f.anc
|
|
}
|
|
return f.data[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
func genValuePtr(n *Node) func(*Frame) reflect.Value {
|
|
v := genValue(n)
|
|
return func(f *Frame) reflect.Value { return v(f).Addr() }
|
|
}
|
|
|
|
func genValue(n *Node) func(*Frame) reflect.Value {
|
|
switch n.kind {
|
|
case BasicLit, FuncDecl, SelectorSrc:
|
|
var v reflect.Value
|
|
if w, ok := n.val.(reflect.Value); ok {
|
|
v = w
|
|
} else {
|
|
v = reflect.ValueOf(n.val)
|
|
}
|
|
return func(f *Frame) reflect.Value { return v }
|
|
case Rvalue:
|
|
v := n.rval
|
|
return func(f *Frame) reflect.Value { return v }
|
|
default:
|
|
if n.sym != nil {
|
|
if n.sym.index < 0 {
|
|
return genValue(n.sym.node)
|
|
}
|
|
i := n.sym.index
|
|
if n.sym.global {
|
|
return func(f *Frame) reflect.Value {
|
|
return n.interp.Frame.data[i]
|
|
}
|
|
}
|
|
return valueGenerator(n, i)
|
|
}
|
|
if n.findex < 0 {
|
|
var v reflect.Value
|
|
if w, ok := n.val.(reflect.Value); ok {
|
|
v = w
|
|
} else {
|
|
v = reflect.ValueOf(n.val)
|
|
}
|
|
return func(f *Frame) reflect.Value { return v }
|
|
}
|
|
return valueGenerator(n, n.findex)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func genFun(n *Node) {
|
|
start := n.child[3].start
|
|
framepos := n.framepos
|
|
flen := n.flen
|
|
types := n.types
|
|
nout := len(n.typ.ret)
|
|
|
|
n.fun = func(f *Frame, in []reflect.Value, goroutine bool) []reflect.Value {
|
|
// Allocate local frame
|
|
nf := Frame{anc: f, data: make([]reflect.Value, flen)}
|
|
for i, t := range types {
|
|
if t != nil {
|
|
nf.data[i] = reflect.New(t).Elem()
|
|
}
|
|
}
|
|
// Copy input parameters from caller frame to local
|
|
for i, pos := range framepos {
|
|
nf.data[pos].Set(in[i])
|
|
}
|
|
if goroutine {
|
|
go runCfg(start, &nf)
|
|
} else {
|
|
runCfg(start, &nf)
|
|
}
|
|
// Propagate return values to caller
|
|
return nf.data[:nout]
|
|
}
|
|
}
|
|
|
|
// Experimental, temporary & incomplete
|
|
func getValue(n *Node) (int, reflect.Value, bool) {
|
|
var index int
|
|
var val reflect.Value
|
|
var isReflect bool
|
|
switch n.kind {
|
|
case BasicLit:
|
|
val = reflect.ValueOf(n.val)
|
|
default:
|
|
if n.sym != nil {
|
|
index = n.sym.index
|
|
isReflect = true
|
|
} else if n.findex < 0 {
|
|
val = reflect.ValueOf(n.val)
|
|
} else {
|
|
index = n.findex
|
|
isReflect = true
|
|
}
|
|
}
|
|
return index, val, isReflect
|
|
}
|
|
|
|
// frameTypes return a slice of frame types for FuncDecl or FuncLit nodes
|
|
func frameTypes(node *Node, size int) []reflect.Type {
|
|
ft := make([]reflect.Type, size)
|
|
|
|
node.Walk(func(n *Node) bool {
|
|
if n.kind == FuncDecl || n.kind == ImportDecl || n.kind == TypeDecl || n.kind == FuncLit {
|
|
return n == node // Do not dive in substree, except if this the entry point
|
|
}
|
|
if n.findex < 0 || n.typ == nil || n.level > 0 || n.kind == BasicLit || n.kind == SelectorSrc {
|
|
return true
|
|
}
|
|
if ft[n.findex] == nil {
|
|
if n.typ.cat == FuncT {
|
|
ft[n.findex] = reflect.TypeOf(n)
|
|
} else {
|
|
ft[n.findex] = n.typ.TypeOf()
|
|
}
|
|
} else {
|
|
// TODO: Check that type is identical
|
|
}
|
|
return true
|
|
}, nil)
|
|
|
|
return ft
|
|
}
|