diff --git a/_test/variadic3.go b/_test/variadic3.go new file mode 100644 index 00000000..937d65d4 --- /dev/null +++ b/_test/variadic3.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func f(a ...int) int { + fmt.Println(a) + res := 0 + for _, v := range a { + res += v + } + return res +} + +func main() { + fmt.Println(f(1, 2, 3, 4)) +} + +// Output: +// [1 2 3 4] +// 10 diff --git a/interp/ast.go b/interp/ast.go index 56163bf0..fa4b5bf0 100644 --- a/interp/ast.go +++ b/interp/ast.go @@ -405,9 +405,20 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) { ancAst := anc.ast.(*ast.CaseClause) if len(ancAst.List)+len(ancAst.Body) == len(anc.node.child) { // All case clause children are collected. - // Split children in condition and body nodest to desambiguify the AST. + // Split children in condition and body nodes to desambiguify the AST. interp.nindex++ body := &Node{anc: anc.node, interp: interp, index: interp.nindex, pos: pos, kind: CaseBody, action: Nop, val: &i, gen: nop} + + if ts := anc.node.anc.anc; ts.kind == TypeSwitch && ts.child[1].action == Assign { + // In type switch clause, if a switch guard is assigned, duplicate the switch guard symbol + // in each clause body, so a different guard type can be set in each clause + name := ts.child[1].child[0].ident + interp.nindex++ + gn := &Node{anc: body, interp: interp, ident: name, index: interp.nindex, pos: pos, kind: Ident, action: Nop, val: &i, gen: nop} + body.child = append(body.child, gn) + } + + // Add regular body children body.child = append(body.child, anc.node.child[len(ancAst.List):]...) for i := range body.child { body.child[i].anc = body diff --git a/interp/cfg.go b/interp/cfg.go index c02f167d..a185527b 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -3,118 +3,12 @@ package interp import ( "fmt" "reflect" - "strconv" "unicode" ) // A CfgError represents an error during CFG build stage type CfgError error -// A SymKind represents the kind of symbol -type SymKind uint - -// Symbol kinds for the go language -const ( - Const SymKind = iota // Constant - Typ // Type - Var // Variable - Func // Function - Bin // Binary from runtime - Bltn // Builtin -) - -var symKinds = [...]string{ - Const: "Const", - Typ: "Typ", - 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 - recv *Receiver // receiver node value, if sym refers to a method - 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 (s *Scope) inc(interp *Interpreter) int { - if s.global { - interp.fsize++ - s.size = interp.fsize - } else { - s.size++ - } - return s.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. @@ -131,10 +25,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { // Set default package namespace for incremental parse pkgName = "_" if _, ok := interp.scope[pkgName]; !ok { - interp.scope[pkgName] = scope.push(0) + interp.scope[pkgName] = scope.pushBloc() } scope = interp.scope[pkgName] - scope.size = interp.fsize } root.Walk(func(n *Node) bool { @@ -150,44 +43,50 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { } case BlockStmt: - // For range block: ensure that array or map type is propagated to iterators - // prior to process block if n.anc != nil && n.anc.kind == RangeStmt { + // For range block: ensure that array or map type is propagated to iterators + // prior to process block. We cannot perform this at RangeStmt pre-order because + // type of array like value is not yet known. This could be fixed in ast structure + // by setting array/map node as 1st child of ForRangeStmt instead of 3rd child of + // RangeStmt. The following workaround is less elegant but ok. + k, v := n.anc.child[0], n.anc.child[1] + var ktyp, vtyp *Type + switch n.anc.child[2].typ.cat { case ValueT: typ := n.anc.child[2].typ.rtype - switch typ.Kind() { case reflect.Map: - scope.sym[n.anc.child[0].ident].typ = &Type{cat: ValueT, rtype: typ.Key()} - scope.sym[n.anc.child[1].ident].typ = &Type{cat: ValueT, rtype: typ.Elem()} n.anc.gen = rangeMap + ktyp = &Type{cat: ValueT, rtype: typ.Key()} + vtyp = &Type{cat: ValueT, rtype: typ.Elem()} case reflect.Array, reflect.Slice: - scope.sym[n.anc.child[0].ident].typ = scope.getType("int") - n.anc.child[0].typ = scope.getType("int") - vtype := &Type{cat: ValueT, rtype: typ.Elem()} - scope.sym[n.anc.child[1].ident].typ = vtype - n.anc.child[1].typ = vtype + ktyp = scope.getType("int") + vtyp = &Type{cat: ValueT, rtype: typ.Elem()} } case MapT: - scope.sym[n.anc.child[0].ident].typ = n.anc.child[2].typ.key - n.anc.child[0].typ = n.anc.child[2].typ.key n.anc.gen = rangeMap - vtype := n.anc.child[2].typ.val - scope.sym[n.anc.child[1].ident].typ = vtype - n.anc.child[1].typ = vtype - default: - scope.sym[n.anc.child[0].ident].typ = scope.getType("int") - n.anc.child[0].typ = scope.getType("int") - vtype := n.anc.child[2].typ.val - scope.sym[n.anc.child[1].ident].typ = vtype - n.anc.child[1].typ = vtype + ktyp = n.anc.child[2].typ.key + vtyp = n.anc.child[2].typ.val + case ArrayT: + ktyp = scope.getType("int") + vtyp = n.anc.child[2].typ.val } + + kindex := scope.add(ktyp) + scope.sym[k.ident] = &Symbol{index: kindex, kind: Var, typ: ktyp} + k.typ = ktyp + k.findex = kindex + + vindex := scope.add(vtyp) + scope.sym[v.ident] = &Symbol{index: vindex, kind: Var, typ: vtyp} + v.typ = vtyp + v.findex = vindex } - scope = scope.push(0) + scope = scope.pushBloc() case CaseClause: - scope = scope.push(0) + scope = scope.pushBloc() if sn := n.anc.anc; sn.kind == TypeSwitch && sn.child[1].action == Assign { // Type switch clause with a var defined in switch guard var typ *Type @@ -200,13 +99,30 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { typ = scope.getType("interface{}") default: err = n.cfgError("%s is not a type", n.child[0].ident) + return false } } else { // define the var with the type in the switch guard expression typ = sn.child[1].child[1].child[0].typ } - node := sn.child[1].child[0] - scope.sym[node.ident] = &Symbol{index: node.findex, kind: Var, typ: typ} + node := n.lastChild().child[0] + index := scope.add(typ) + scope.sym[node.ident] = &Symbol{index: index, kind: Var, typ: typ} + node.findex = index + node.typ = typ + } + + case CommClause: + scope = scope.pushBloc() + if n.child[0].action == Assign { + ch := n.child[0].child[1].child[0] + if sym, _, ok := scope.lookup(ch.ident); ok { + assigned := n.child[0].child[0] + index := scope.add(sym.typ.val) + scope.sym[assigned.ident] = &Symbol{index: index, kind: Var, typ: sym.typ.val} + assigned.findex = index + assigned.typ = sym.typ.val + } } case CompositeLitExpr: @@ -230,32 +146,68 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case File: pkgName = n.child[0].ident if _, ok := interp.scope[pkgName]; !ok { - interp.scope[pkgName] = scope.push(0) + interp.scope[pkgName] = scope.pushBloc() } scope = interp.scope[pkgName] - scope.size = interp.fsize + n.findex = -1 case For0, ForRangeStmt: loop, loopRestart = n, n.child[0] - scope = scope.push(0) + scope = scope.pushBloc() case For1, For2, For3, For3a, For4: loop, loopRestart = n, n.lastChild() - scope = scope.push(0) + scope = scope.pushBloc() - case FuncDecl, FuncLit: + case FuncLit: + n.typ = nil // to force nodeType to recompute the type + n.typ, err = nodeType(interp, scope, n) + n.findex = scope.add(n.typ) + fallthrough + + case FuncDecl: + n.val = n + // Add a frame indirection level as we enter in a func + scope = scope.pushFunc() + scope.def = n + if len(n.child[2].child) == 2 { + // Allocate frame space for return values, define output symbols + for _, c := range n.child[2].child[1].child { + var typ *Type + typ, err = nodeType(interp, scope, c.lastChild()) + if len(c.child) > 1 { + for _, cc := range c.child[:len(c.child)-1] { + scope.sym[cc.ident] = &Symbol{index: scope.add(typ), kind: Var, typ: typ} + } + } else { + scope.add(typ) + } + } + } + if len(n.child[0].child) > 0 { + // define receiver symbol + var typ *Type + recvName := n.child[0].child[0].child[0].ident + typ, err = nodeType(interp, scope, n.child[0].child[0].lastChild()) + scope.sym[recvName] = &Symbol{index: scope.add(typ), kind: Var, typ: typ} + } + for _, c := range n.child[2].child[0].child { + // define input parameter symbols + var typ *Type + typ, err = nodeType(interp, scope, c.lastChild()) + if typ.variadic { + typ = &Type{cat: ArrayT, val: typ} + } + for _, cc := range c.child[:len(c.child)-1] { + scope.sym[cc.ident] = &Symbol{index: scope.add(typ), kind: Var, typ: typ} + } + } 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) + scope = scope.pushBloc() case Switch, SwitchIf, TypeSwitch: // Make sure default clause is in last position @@ -263,14 +215,14 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { if i, l := getDefault(n), len(c)-1; i >= 0 && i != l { c[i], c[l] = c[l], c[i] } - scope = scope.push(0) + scope = scope.pushBloc() loop = n case ImportSpec, TypeSpec: // processing already done in GTA pass return false - case ArrayType, BasicLit, ChanType, MapType, StructType: + case ArrayType, BasicLit, ChanType, FuncType, MapType, StructType: n.typ, err = nodeType(interp, scope, n) return false } @@ -284,41 +236,50 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case Address: wireChild(n) n.typ = &Type{cat: PtrT, val: n.child[0].typ} - n.findex = scope.inc(interp) + n.findex = scope.add(n.typ) case AssignStmt, Define: if n.anc.kind == TypeSwitch && n.anc.child[1] == n { // type switch guard assignment: assign dest to concrete value of src - n.child[0].typ = n.child[1].child[0].typ + n.gen = nop + break + } + if n.anc.kind == CommClause { + n.gen = nop break } dest, src := n.child[0], n.lastChild() - sym, level, _ := scope.lookup(dest.ident) + var sym *Symbol + var level int if n.kind == Define { if src.typ.cat == NilT { err = src.cfgError("use of untyped nil") break } - if len(n.child) == 3 { + switch { + case len(n.child) == 3: // type is provided in var declaration dest.typ, err = nodeType(interp, scope, n.child[1]) - } else { + case isRegularCall(src) || isBinCall(src): + dest.typ = getReturnedType(src.child[0]) + default: dest.typ = src.typ } + if scope.global { + // Do not overload existings symbols (defined in GTA) in global scope + sym, _, _ = scope.lookup(dest.ident) + } else { + sym = &Symbol{index: scope.add(dest.typ), kind: Var, global: scope.global} + scope.sym[dest.ident] = sym + } dest.val = src.val dest.recv = src.recv dest.findex = sym.index - if src.action == GetFunc { - sym.index = -1 - sym.node = src - } if src.kind == BasicLit { sym.val = src.val - } else if isRegularCall(src) || isBinCall(src) { - // propagate call return value type - dest.typ = getReturnedType(src.child[0]) - sym.typ = dest.typ } + } else { + sym, level, _ = scope.lookup(dest.ident) } wireChild(n) // Detect invalid float truncate @@ -378,7 +339,19 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { n.level = level } - case AssignXStmt, DefineX: + case AssignXStmt: + wireChild(n) + l := len(n.child) - 1 + switch n.child[l].kind { + case IndexExpr: + n.child[l].gen = getIndexMap2 + n.gen = nop + case TypeAssertExpr: + n.child[l].gen = typeAssert2 + n.gen = nop + } + + case DefineX: wireChild(n) l := len(n.child) - 1 var types []*Type @@ -392,6 +365,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { } else { types = funtype.ret } + if l > len(types) { + n.gen = assignX2 + } case IndexExpr: types = append(types, n.child[l].child[0].typ.val, scope.getType("bool")) @@ -412,24 +388,19 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { err = n.cfgError("unsupported assign expression") return } - for i, c := range n.child[:l] { - if i == len(types) { - break // skip useless type symbol - } - sym, _, ok := scope.lookup(c.ident) - if !ok { - err = c.cfgError("undefined: %s", c.ident) - } - sym.typ = types[i] - c.typ = sym.typ + for i, t := range types { + index := scope.add(t) + scope.sym[n.child[i].ident] = &Symbol{index: index, kind: Var, typ: t} + n.child[i].typ = t + n.child[i].findex = index } case BinaryExpr: wireChild(n) - n.findex = scope.inc(interp) nilSym := interp.universe.sym["nil"] if t0, t1 := n.child[0].typ, n.child[1].typ; !t0.untyped && !t1.untyped && t0.id() != t1.id() { err = n.cfgError("mismatched types %s and %s", t0.id(), t1.id()) + break } switch n.action { case NotEqual: @@ -447,14 +418,15 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { default: n.typ, err = nodeType(interp, scope, n) } + // TODO: Possible optimisation: if type is bool and not in assignment or call, then skip result store + n.findex = scope.add(n.typ) case IndexExpr: wireChild(n) - n.findex = scope.inc(interp) n.typ = n.child[0].typ.val + n.findex = scope.add(n.typ) n.recv = &Receiver{node: 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 @@ -486,8 +458,8 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case CallExpr: wireChild(n) - n.findex = scope.inc(interp) - if isBuiltinCall(n) { + switch { + case isBuiltinCall(n): n.gen = n.child[0].sym.builtin n.child[0].typ = &Type{cat: BuiltinT} switch n.child[0].ident { @@ -506,32 +478,44 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case "recover": n.typ = scope.getType("interface{}") } - } else if n.child[0].isType(scope) { + if n.typ != nil { + n.findex = scope.add(n.typ) + } + case n.child[0].isType(scope): // Type conversion expression - n.typ = n.child[0].typ n.gen = convert - } else if isBinCall(n) { + n.typ = n.child[0].typ + n.findex = scope.add(n.typ) + case isBinCall(n): n.gen = callBin - n.fsize = n.child[0].fsize if typ := n.child[0].typ.rtype; typ.NumOut() > 0 { n.typ = &Type{cat: ValueT, rtype: typ.Out(0)} + n.findex = scope.add(n.typ) + for i := 1; i < typ.NumOut(); i++ { + scope.add(&Type{cat: ValueT, rtype: typ.Out(i)}) + } + } + default: + if n.child[0].action == GetFunc { + // allocate frame entry for anonymous function + scope.add(n.child[0].typ) + } + if typ := n.child[0].typ; len(typ.ret) > 0 { + n.typ = typ.ret[0] + n.findex = scope.add(n.typ) + for _, t := range typ.ret[1:] { + scope.add(t) + } + } else { + n.findex = -1 } - } else if typ := n.child[0].typ; len(typ.ret) > 0 { - n.typ = typ.ret[0] - n.fsize = len(typ.ret) - } - - // Reserve entries in frame to store results of call - if scope.global { - interp.fsize += n.fsize - scope.size = interp.fsize - } else { - scope.size += n.fsize } case CaseBody: wireChild(n) - if len(n.child) > 0 { + if typeSwichAssign(n) && len(n.child) > 1 { + n.start = n.child[1].start + } else { n.start = n.child[0].start } @@ -546,11 +530,12 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { n.start = n.child[0].start // default clause } n.lastChild().tnext = n.anc.anc // exit node is SelectStmt + scope = scope.pop() case CompositeLitExpr: wireChild(n) if n.anc.action != Assign { - n.findex = scope.inc(interp) + n.findex = scope.add(n.typ) } // TODO: Check that composite literal expr matches corresponding type switch n.typ.cat { @@ -576,38 +561,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { err = n.cfgError("fallthrough statement out of place") } - 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 - if n.typ, err = nodeType(interp, scope.anc, n.child[l]); err != nil { - return - } - if l == 0 { - if n.anc.anc.kind == FuncDecl { - // Receiver with implicit var decl - scope.sym[n.child[0].ident].typ = n.typ - n.child[0].typ = n.typ - } else { - n.findex = scope.inc(interp) - } - } else { - for _, f := range n.child[:l] { - f.typ = n.typ - if n.typ.variadic { - scope.sym[f.ident].typ = &Type{cat: ArrayT, val: n.typ} - } else { - scope.sym[f.ident].typ = n.typ - } - } - } - case File: wireChild(n) scope = scope.pop() - n.fsize = scope.size + 1 case For0: // for {} body := n.child[0] @@ -672,16 +628,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { 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...) + n.types = scope.types scope = scope.pop() funcName := n.child[1].ident - 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 @@ -689,48 +638,20 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { interp.scope[pkgName].sym[funcName].node = n case FuncLit: - n.typ = n.child[2].typ - n.val = n - n.flen = scope.size + 1 + n.types = scope.types scope = scope.pop() - n.framepos = n.child[2].framepos - - case FuncType: - if n.typ, err = nodeType(interp, scope, n); err != nil { - return - } - // 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) case Ident: - if isKey(n) { - // Skip symbol creation/lookup for identifier used as key - } else if isFuncArg(n) { - n.findex = scope.inc(interp) - scope.sym[n.ident] = &Symbol{index: scope.size, kind: Var, global: scope.global} - n.sym = scope.sym[n.ident] - } else if isNewDefine(n) { - // Create a new symbol in current scope, type to be set by parent node - // Note that global symbol should already be defined (gta) - if _, _, ok := scope.lookup(n.ident); !ok || !scope.global { - n.findex = scope.inc(interp) - scope.sym[n.ident] = &Symbol{index: scope.size, kind: Var, global: scope.global} - n.sym = scope.sym[n.ident] - } + if isKey(n) || isNewDefine(n) { + break } 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 switch { @@ -750,9 +671,6 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { n.kind = Rvalue } n.typ = sym.typ - if n.typ.rtype.Kind() == reflect.Func { - n.fsize = n.typ.rtype.NumOut() - } n.rval = sym.val.(reflect.Value) case sym.kind == Bltn: if n.anc.kind != CallExpr { @@ -811,16 +729,16 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { 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 + n.findex = scope.add(n.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 + n.findex = scope.add(n.typ) case ParenExpr: wireChild(n) @@ -841,7 +759,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { for i, c := range n.child { if c.typ.cat == NilT { // nil: Set node value to zero of return type - f := getAncFunc(n) + f := scope.def var typ *Type typ, err = nodeType(interp, scope, f.child[2].child[1].child[i].lastChild()) if err != nil { @@ -855,7 +773,6 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { 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 { @@ -871,7 +788,6 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { n.val = method.Index n.gen = getIndexBinMethod n.typ = &Type{cat: ValueT, rtype: method.Type} - n.fsize = method.Type.NumOut() n.recv = &Receiver{node: n.child[0]} case n.typ.rtype.Kind() == reflect.Ptr: if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok { @@ -901,12 +817,10 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { } else if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok { n.val = method.Index n.typ = &Type{cat: ValueT, rtype: method.Type} - n.fsize = method.Type.NumOut() n.recv = &Receiver{node: n.child[0]} n.gen = getIndexBinMethod } else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok { n.val = method.Index - n.fsize = method.Type.NumOut() n.gen = getIndexBinMethod n.typ = &Type{cat: ValueT, rtype: method.Type} n.recv = &Receiver{node: n.child[0]} @@ -921,27 +835,23 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { 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 { err = n.cfgError("package %s \"%s\" has no symbol %s", n.child[0].ident, pkg, name) } } else if n.typ.cat == SrcPkgT { + pkg, name := n.child[0].ident, n.child[1].ident // Resolve source package symbol - if sym, ok := interp.scope[n.child[0].ident].sym[n.child[1].ident]; ok { + if sym, ok := interp.scope[pkg].sym[name]; ok { n.findex = sym.index n.val = sym.node n.gen = nop n.typ = sym.typ + n.sym = sym } else { err = n.cfgError("undefined selector: %s", n.child[1].ident) } @@ -969,12 +879,14 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { // function in a struct field is always wrapped in reflect.Value rtype := n.typ.TypeOf() n.typ = &Type{cat: ValueT, rtype: rtype} - n.fsize = rtype.NumOut() } } } else { err = n.cfgError("undefined selector: %s", n.child[1].ident) } + if n.findex != -1 { + n.findex = scope.add(n.typ) + } case SelectStmt: wireChild(n) @@ -987,6 +899,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case n.anc.kind == Define && len(n.anc.child) == 3 && n.anc.child[1] == n: // pointer type expression in a var definition n.gen = nop + case n.anc.kind == ValueSpec && n.anc.lastChild() == n: + // pointer type expression in a value spec + n.gen = nop case n.anc.kind == Field: // pointer type expression in a field expression (arg or struct field) n.gen = nop @@ -994,7 +909,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { // dereference expression wireChild(n) n.typ = n.child[0].typ.val - n.findex = scope.inc(interp) + n.findex = scope.add(n.typ) } case TypeSwitch: @@ -1087,7 +1002,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { } if n.anc.action != AssignX { n.typ = n.child[1].typ - n.findex = scope.inc(interp) + n.findex = scope.add(n.typ) } } else { n.gen = nop @@ -1096,8 +1011,8 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { case SliceExpr, UnaryExpr: wireChild(n) n.typ = n.child[0].typ - // TODO: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr) - n.findex = scope.inc(interp) + // TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr) + n.findex = scope.add(n.typ) case ValueSpec: l := len(n.child) - 1 @@ -1108,12 +1023,17 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) { } } for _, c := range n.child[:l] { + index := scope.add(n.typ) + scope.sym[c.ident] = &Symbol{index: index, kind: Var, typ: n.typ} c.typ = n.typ - scope.sym[c.ident].typ = n.typ + c.findex = index } } }) + if scope != interp.universe { + scope.pop() + } return initNodes, err } @@ -1130,16 +1050,9 @@ func genRun(node *Node) error { return false } switch n.kind { - case File: - if n.types, err = frameTypes(n, n.fsize); err != nil { - return false - } - case FuncDecl, FuncLit: - if n.types, err = frameTypes(n, n.flen); err != nil { - return false - } case FuncType: if len(n.anc.child) == 4 { + // function body entry point setExec(n.anc.child[3].start) } case ConstDecl, VarDecl: @@ -1248,20 +1161,6 @@ func isNewDefine(n *Node) bool { return false } -func isFuncArg(n *Node) bool { - if n.anc.kind != Field { - return false - } - l := len(n.anc.child) - if l == 1 && n.anc.anc.anc.kind == FuncDecl { - return true - } - if l > 1 && n.anc.child[l-1] != n { - return true - } - return false -} - func isBuiltinCall(n *Node) bool { return n.kind == CallExpr && n.child[0].sym != nil && n.child[0].sym.kind == Bltn } @@ -1302,8 +1201,8 @@ func getExec(n *Node) Builtin { return n.exec } -// setExec recursively sets the node exec builtin function -// it does nothing if exec is already defined +// setExec recursively sets the node exec builtin function by walking the CFG +// from the entry point (first node to exec). func setExec(n *Node) { if n.exec != nil { return @@ -1338,17 +1237,6 @@ func setExec(n *Node) { set(n) } -// getAncFunc returns the first node in ancestorship which is a FuncDecl or FuncLit -func getAncFunc(n *Node) *Node { - for anc := n.anc; anc != nil; anc = anc.anc { - switch anc.kind { - case FuncDecl, FuncLit: - return anc - } - } - return nil -} - func getReturnedType(n *Node) *Type { switch n.typ.cat { case BuiltinT: @@ -1362,36 +1250,7 @@ func getReturnedType(n *Node) *Type { return n.typ.ret[0] } -// frameTypes returns a slice of frame types for FuncDecl or FuncLit nodes -func frameTypes(node *Node, size int) ([]reflect.Type, error) { - ft := make([]reflect.Type, size) - var err CfgError - - node.Walk(func(n *Node) bool { - if err != nil { - return false - } - if n.kind == FuncDecl || n.kind == ImportDecl || n.kind == TypeDecl || n.kind == FuncLit { - return n == node // Do not dive in subtree, except if this is the entry point - } - if n.findex < 0 || n.typ == nil || n.level > 0 || n.kind == BasicLit || n.typ.cat == BinPkgT { - return true - } - if ft[n.findex] == nil { - if n.typ.incomplete { - if n.typ, err = n.typ.finalize(); err != nil { - return false - } - } - if n.typ.cat == FuncT { - ft[n.findex] = reflect.TypeOf(n) - } else { - ft[n.findex] = n.typ.TypeOf() - } - } - // TODO: Check that type is identical - return true - }, nil) - - return ft, err +func typeSwichAssign(n *Node) bool { + ts := n.anc.anc.anc + return ts.kind == TypeSwitch && ts.child[1].action == Assign } diff --git a/interp/gta.go b/interp/gta.go index d0f77d25..56c3a33a 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -9,29 +9,58 @@ import ( // All function bodies are skipped. GTA is necessary to handle out of // order declarations and multiple source files packages. func (interp *Interpreter) Gta(root *Node, rpath string) error { + scope := interp.universe var err error var pkgName string - scope := interp.universe + var iotaValue int + + if root.kind != File { + // Set default package namespace for incremental parse + pkgName = "_" + if _, ok := interp.scope[pkgName]; !ok { + interp.scope[pkgName] = scope.pushBloc() + } + scope = interp.scope[pkgName] + } root.Walk(func(n *Node) bool { if err != nil { return false } switch n.kind { + case ConstDecl: + iotaValue = 0 + case Define: - varName := n.child[0].ident - scope.sym[varName] = &Symbol{kind: Var, global: true, index: scope.inc(interp)} + var typ *Type if len(n.child) > 1 { - scope.sym[varName].typ, err = nodeType(interp, scope, n.child[1]) + typ, err = nodeType(interp, scope, n.child[1]) } else { - scope.sym[varName].typ, err = nodeType(interp, scope, n.anc.child[0].child[1]) + typ, err = nodeType(interp, scope, n.anc.child[0].child[1]) + } + if err != nil { + return false + } + if typ.cat == NilT { + err = n.cfgError("use of untyped nil") + return false + } + var val interface{} = iotaValue + if len(n.child) > 1 { + val = n.child[1].val + } + scope.sym[n.child[0].ident] = &Symbol{kind: Var, global: true, index: scope.add(typ), typ: typ, val: val} + if n.anc.kind == ConstDecl { + iotaValue++ } return false + // TODO: add DefineX, ValueSpec + case File: pkgName = n.child[0].ident if _, ok := interp.scope[pkgName]; !ok { - interp.scope[pkgName] = scope.push(0) + interp.scope[pkgName] = scope.pushBloc() } scope = interp.scope[pkgName] @@ -98,6 +127,7 @@ func (interp *Interpreter) Gta(root *Node, rpath string) error { } else { // TODO: make sure we do not import a src package more than once err = interp.importSrcFile(rpath, ipath, name) + scope.types = interp.universe.types scope.sym[name] = &Symbol{typ: &Type{cat: SrcPkgT}, path: ipath} } @@ -127,5 +157,8 @@ func (interp *Interpreter) Gta(root *Node, rpath string) error { return true }, nil) + if scope != interp.universe { + scope.pop() + } return err } diff --git a/interp/interp.go b/interp/interp.go index 13cfb144..dd1bc3c6 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -12,32 +12,29 @@ import ( // Node structure for AST and CFG type Node struct { - child []*Node // child subtrees (AST) - anc *Node // ancestor (AST) - start *Node // entry point in subtree (CFG) - tnext *Node // true branch successor (CFG) - fnext *Node // false branch successor (CFG) - interp *Interpreter // interpreter context - frame *Frame // frame pointer used for closures only (TODO: suppress this) - index int // node index (dot display) - findex int // index of value in frame or frame size (func def, type def) - fsize int // number of entries in frame (call expressions) - flen int // frame length (function definition) - level int // number of frame indirections to access value - kind Kind // kind of node - fset *token.FileSet // fileset to locate node in source code - pos token.Pos // position in source code, relative to fset - sym *Symbol // associated symbol - typ *Type // type of value in frame, or nil - recv *Receiver // method receiver node for call, or nil - types []reflect.Type // frame types, used by function literals only - framepos []int // frame positions of function parameters - action Action // action - exec Builtin // generated function to execute - gen BuiltinGenerator // generator function to produce above bltn - val interface{} // static generic value (CFG execution) - rval reflect.Value // reflection value to let runtime access interpreter (CFG) - ident string // set if node is a var or func + child []*Node // child subtrees (AST) + anc *Node // ancestor (AST) + start *Node // entry point in subtree (CFG) + tnext *Node // true branch successor (CFG) + fnext *Node // false branch successor (CFG) + interp *Interpreter // interpreter context + frame *Frame // frame pointer used for closures only (TODO: suppress this) + index int // node index (dot display) + findex int // index of value in frame or frame size (func def, type def) + level int // number of frame indirections to access value + kind Kind // kind of node + fset *token.FileSet // fileset to locate node in source code + pos token.Pos // position in source code, relative to fset + sym *Symbol // associated symbol + typ *Type // type of value in frame, or nil + recv *Receiver // method receiver node for call, or nil + types []reflect.Type // frame types, used by function literals only + action Action // action + exec Builtin // generated function to execute + gen BuiltinGenerator // generator function to produce above bltn + val interface{} // static generic value (CFG execution) + rval reflect.Value // reflection value to let runtime access interpreter (CFG) + ident string // set if node is a var or func } // Receiver stores method receiver object access path @@ -75,7 +72,6 @@ type Interpreter struct { Opt Name string // program name Frame *Frame // program data storage during execution - fsize int // global interpreter frame size nindex int // next node index universe *Scope // interpreter global level scope scope map[string]*Scope // package level scopes, indexed by package name @@ -180,9 +176,17 @@ func initUniverse() *Scope { // resizeFrame resizes the global frame of interpreter func (i *Interpreter) resizeFrame() { - f := &Frame{data: make([]reflect.Value, i.fsize)} - copy(f.data, i.Frame.data) - i.Frame = f + l := len(i.universe.types) + b := len(i.Frame.data) + if l-b <= 0 { + return + } + data := make([]reflect.Value, l) + copy(data, i.Frame.data) + for j, t := range i.universe.types[b:] { + data[b+j] = reflect.New(t).Elem() + } + i.Frame.data = data } // Eval evaluates Go code represented as a string. It returns a map on @@ -216,7 +220,6 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) { initNodes = append(initNodes, sym.node) } } else { - root.types, _ = frameTypes(root, i.fsize+1) setExec(root.start) } if i.universe.sym[pkgName] == nil { @@ -233,7 +236,6 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) { if err = genRun(root); err != nil { return res, err } - i.fsize++ i.resizeFrame() i.run(root, nil) diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go index 618db19d..6b61fa60 100644 --- a/interp/interp_eval_test.go +++ b/interp/interp_eval_test.go @@ -2,6 +2,7 @@ package interp_test import ( "fmt" + "log" "net/http" "reflect" "testing" @@ -11,6 +12,8 @@ import ( "github.com/containous/dyngo/stdlib" ) +func init() { log.SetFlags(log.Lshortfile) } + // testCase represents an interpreter test case. // Care must be taken when defining multiple test cases within the same interpreter // context, as all declarations occur in the global scope and are therefore @@ -46,6 +49,17 @@ func TestEvalDecl(t *testing.T) { }) } +func TestEvalFunc(t *testing.T) { + i := interp.New(interp.Opt{}) + runTests(t, i, []testCase{ + {src: `(func () string {return "ok"})()`, res: "ok"}, + {src: `(func () (res string) {res = "ok"; return})()`, res: "ok"}, + {src: `(func () int {f := func() (a, b int) {a, b = 3, 4; return}; x, y := f(); return x+y})()`, res: "7"}, + {src: `(func () int {f := func() (a int, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"}, + {src: `(func () int {f := func() (a, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"}, + }) +} + func TestEvalImport(t *testing.T) { i := interp.New(interp.Opt{}) i.Use(stdlib.Value, stdlib.Type) @@ -58,10 +72,10 @@ func TestEvalNil(t *testing.T) { i := interp.New(interp.Opt{}) i.Use(stdlib.Value, stdlib.Type) runTests(t, i, []testCase{ - {desc: "assign nil", src: "a := nil", err: "1:27: use of untyped nil"}, + {desc: "assign nil", src: "a := nil", err: "1:22: use of untyped nil"}, {desc: "return nil", pre: func() { eval(t, i, "func getNil() error {return nil}") }, src: "getNil()", res: ""}, { - desc: "return func which return nil error", + desc: "return func which return error", pre: func() { eval(t, i, ` package bar diff --git a/interp/interp_test.go b/interp/interp_test.go index f5be46a4..c7ecbebd 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -7837,3 +7837,34 @@ func main() { // Output: // 3 } + +func Example_variadic3() { + src := ` +package main + +import "fmt" + +func f(a ...int) int { + fmt.Println(a) + res := 0 + for _, v := range a { + res += v + } + return res +} + +func main() { + fmt.Println(f(1, 2, 3, 4)) +} +` + i := interp.New(interp.Opt{Entry: "main"}) + i.Use(stdlib.Value, stdlib.Type) + _, err := i.Eval(src) + if err != nil { + panic(err) + } + + // Output: + // [1 2 3 4] + // 10 +} diff --git a/interp/run.go b/interp/run.go index 7dff09cf..d391367f 100644 --- a/interp/run.go +++ b/interp/run.go @@ -77,16 +77,12 @@ func (interp *Interpreter) run(n *Node, cf *Frame) { if cf == nil { f = interp.Frame } else { - f = &Frame{anc: cf, data: make([]reflect.Value, n.flen)} + f = &Frame{anc: cf, data: make([]reflect.Value, len(n.types))} } for i, t := range n.types { - // FIXME: nil types are forbidden and should be detected at compile time (CFG) - if t != nil && i < len(f.data) && !f.data[i].IsValid() { - f.data[i] = reflect.New(t).Elem() - } + f.data[i] = reflect.New(t).Elem() } - //log.Println(n.index, "run", n.start.index) runCfg(n.start, f) } @@ -117,7 +113,7 @@ func typeAssert(n *Node) { switch { case n.child[0].typ.cat == ValueT: n.exec = func(f *Frame) Builtin { - f.data[i] = value(f).Elem() + f.data[i].Set(value(f).Elem()) return next } case n.child[1].typ.cat == InterfaceT: @@ -129,7 +125,7 @@ func typeAssert(n *Node) { default: n.exec = func(f *Frame) Builtin { v := value(f).Interface().(valueInterface) - f.data[i] = v.value + f.data[i].Set(v.value) return next } } @@ -205,30 +201,46 @@ func assignX(n *Node) { } } +// assignX2 implements multiple value assignment for expression where type is defined +func assignX2(n *Node) { + l := len(n.child) - 2 + b := n.child[l].findex + s := n.child[:l] + next := getExec(n.tnext) + values := make([]func(*Frame) reflect.Value, l) + for i, c := range s { + values[i] = genValue(c) + } + + n.exec = func(f *Frame) Builtin { + for i, value := range values { + if f.data[b+i].IsValid() { + value(f).Set(f.data[b+i]) + } + } + return next + } +} + // assign implements single value assignment func assign(n *Node) { next := getExec(n.tnext) + value := genValue(n) + dest, src := n.child[0], n.lastChild() + var value1 func(*Frame) reflect.Value - if n.child[0].typ.cat == InterfaceT { - valueAddr := genValueAddr(n) - value1 := genValueInterface(n.lastChild()) - n.exec = func(f *Frame) Builtin { - *(valueAddr(f)) = value1(f) - return next - } - } else { - value := genValue(n) - dest, src := n.child[0], n.lastChild() - var value1 func(*Frame) reflect.Value - if dest.typ.cat == ValueT && src.typ.cat == FuncT { - value1 = genNodeWrapper(src) - } else { - value1 = genValue(src) - } - n.exec = func(f *Frame) Builtin { - value(f).Set(value1(f)) - return next - } + switch { + case n.child[0].typ.cat == InterfaceT: + value1 = genValueInterface(src) + case dest.typ.cat == ValueT && src.typ.cat == FuncT: + value1 = genNodeWrapper(src) + default: + value1 = genValue(src) + } + + n.exec = func(f *Frame) Builtin { + value(f).Set(value1(f)) + return next } } @@ -322,11 +334,6 @@ func _println(n *Node) { fmt.Printf(" ") } fmt.Printf("%v", value(f)) - - // Handle multiple results of a function call argument - for j := 1; j < child[i].fsize; j++ { - fmt.Printf(" %v", f.data[child[i].findex+j]) - } } fmt.Println("") return next @@ -362,6 +369,7 @@ func genNodeWrapper(n *Node) func(*Frame) reflect.Value { def := n.val.(*Node) setExec(def.child[3].start) start := def.child[3].start + numRet := len(def.typ.ret) var receiver func(*Frame) reflect.Value if n.recv != nil { @@ -374,43 +382,32 @@ func genNodeWrapper(n *Node) func(*Frame) reflect.Value { } return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value { // Allocate and init local frame. All values to be settable and addressable. - frame := Frame{anc: f, data: make([]reflect.Value, def.flen)} + frame := Frame{anc: f, data: make([]reflect.Value, len(def.types))} + d := frame.data for i, t := range def.types { - if t != nil { - frame.data[i] = reflect.New(t).Elem() - } + d[i] = reflect.New(t).Elem() } // Copy method receiver as first argument, if defined - i := 0 if receiver != nil { - frame.data[def.framepos[i]].Set(receiver(f)) - i++ + d[numRet].Set(receiver(f)) + d = d[numRet+1:] + } else { + d = d[numRet:] } // Copy function input arguments in local frame - for _, arg := range in { - frame.data[def.framepos[i]].Set(arg) - i++ + for i, arg := range in { + d[i].Set(arg) } // Interpreter code execution runCfg(start, &frame) - // Return output values in a result vector - var result []reflect.Value - - if len(def.child[2].child) > 1 { - if fieldList := def.child[2].child[1]; fieldList != nil { - result = make([]reflect.Value, len(fieldList.child)) - for i, c := range fieldList.child { - if c.typ.cat == FuncT { - gv := genNodeWrapper(frame.data[i].Interface().(*Node)) - result[i] = gv(f) - } else { - result[i] = frame.data[i] - } - } + result := frame.data[:numRet] + for i, r := range result { + if v, ok := r.Interface().(*Node); ok { + result[i] = genNodeWrapper(v)(f) } } return result @@ -473,6 +470,7 @@ func call(n *Node) { values = append(values, nil) method = true } + numRet := len(n.child[0].typ.ret) variadic := variadicPos(n) child := n.child[1:] tnext := getExec(n.tnext) @@ -517,24 +515,21 @@ func call(n *Node) { if def.frame != nil { anc = def.frame } - nf := Frame{anc: anc, data: make([]reflect.Value, def.flen)} + nf := Frame{anc: anc, data: make([]reflect.Value, len(def.types))} var vararg reflect.Value // Init local frame values for i, t := range def.types { - if t != nil { - nf.data[i] = reflect.New(t).Elem() - } + nf.data[i] = reflect.New(t).Elem() } // Init variadic argument vector if variadic >= 0 { - fi := def.framepos[variadic] - nf.data[fi] = reflect.New(reflect.SliceOf(def.types[fi])).Elem() - vararg = nf.data[fi] + vararg = nf.data[numRet+variadic] } // Copy input parameters from caller + dest := nf.data[numRet:] for i, v := range values { switch { case method && i == 0: @@ -552,24 +547,21 @@ func call(n *Node) { } else { src = v(f) } - dest := nf.data[def.framepos[i]] // Accommodate to receiver type - ks, kd := src.Kind(), dest.Kind() - if ks != kd { + d := dest[0] + if ks, kd := src.Kind(), d.Kind(); ks != kd { if kd == reflect.Ptr { - dest.Set(src.Addr()) + d.Set(src.Addr()) } else { - dest.Set(src.Elem()) + d.Set(src.Elem()) } } else { - dest.Set(src) + d.Set(src) } case variadic >= 0 && i >= variadic: vararg.Set(reflect.Append(vararg, v(f))) - case len(def.typ.arg) > i && def.typ.arg[i].cat == InterfaceT: - nf.data[def.framepos[i]] = v(f) default: - nf.data[def.framepos[i]].Set(v(f)) + dest[i].Set(v(f)) } } @@ -588,7 +580,6 @@ func call(n *Node) { return fnext } // Propagate return values to caller frame - //log.Println(n.index, "call rets:", ret, nf.data[:len(ret)]) for i, r := range ret { f.data[r] = nf.data[i] } @@ -650,7 +641,6 @@ func callBin(n *Node) { } } l := len(values) - fsize := n.child[0].fsize switch { case n.anc.kind == GoStmt: @@ -670,8 +660,8 @@ func callBin(n *Node) { for i, v := range values { in[i] = v(f) } - r := value(f).Call(in) - if r[0].Bool() { + res := value(f).Call(in) + if res[0].Bool() { return tnext } return fnext @@ -682,12 +672,7 @@ func callBin(n *Node) { for i, v := range values { in[i] = v(f) } - //log.Println(n.index, "callbin", value(f).Type(), in) - r := value(f).Call(in) - //log.Println(n.index, "callBin, res:", r, fsize, n.findex, len(r), len(f.data)) - for i := 0; i < fsize; i++ { - f.data[n.findex+i] = r[i] - } + copy(f.data[n.findex:], value(f).Call(in)) return tnext } } @@ -769,16 +754,13 @@ func getIndexMap2(n *Node) { func getFunc(n *Node) { i := n.findex next := getExec(n.tnext) - if len(n.types) == 0 { - n.types, _ = frameTypes(n, n.flen) - } n.exec = func(f *Frame) Builtin { frame := *f node := *n node.val = &node node.frame = &frame - f.data[i] = reflect.ValueOf(&node) + f.data[i].Set(reflect.ValueOf(&node)) return next } } @@ -1178,29 +1160,38 @@ func _case(n *Node) { for i := range types { types[i] = n.child[i].typ } - value := genValue(sn.child[1].lastChild().child[0]) + srcValue := genValue(sn.child[1].lastChild().child[0]) if len(sn.child[1].child) == 2 { // assign in switch guard - vaddr := genValueAddr(sn.child[1].child[0]) + destValue := genValue(n.lastChild().child[0]) switch len(types) { case 0: // default clause: assign var to interface value n.exec = func(f *Frame) Builtin { - *(vaddr(f)) = value(f) + destValue(f).Set(srcValue(f)) return tnext } case 1: // match against 1 type: assign var to concrete value typ := types[0] n.exec = func(f *Frame) Builtin { - if v := value(f); !v.IsValid() { + v := srcValue(f) + if !v.IsValid() { // match zero value against nil if typ.cat == NilT { return tnext } return fnext - } else if vi := v.Interface().(valueInterface); vi.node.typ.id() == typ.id() { - *(vaddr(f)) = vi.value + } + vi := v.Interface().(valueInterface) + if vi.node == nil { + if typ.cat == NilT { + return tnext + } + return fnext + } + if vi.node.typ.id() == typ.id() { + destValue(f).Set(vi.value) return tnext } return fnext @@ -1208,10 +1199,11 @@ func _case(n *Node) { default: // match against multiple types: assign var to interface value n.exec = func(f *Frame) Builtin { - vtyp := value(f).Interface().(valueInterface).node.typ + val := srcValue(f) + vid := val.Interface().(valueInterface).node.typ.id() for _, typ := range types { - if vtyp.id() == typ.id() { - *(vaddr(f)) = value(f) + if vid == typ.id() { + destValue(f).Set(val) return tnext } } @@ -1224,7 +1216,7 @@ func _case(n *Node) { n.exec = func(f *Frame) Builtin { return tnext } } else { n.exec = func(f *Frame) Builtin { - vtyp := value(f).Interface().(valueInterface).node.typ + vtyp := srcValue(f).Interface().(valueInterface).node.typ for _, typ := range types { if vtyp.id() == typ.id() { return tnext diff --git a/interp/scope.go b/interp/scope.go new file mode 100644 index 00000000..2944ed1d --- /dev/null +++ b/interp/scope.go @@ -0,0 +1,151 @@ +package interp + +import ( + "log" + "reflect" + "strconv" +) + +// A SymKind represents the kind of symbol +type SymKind uint + +// Symbol kinds for the go language +const ( + Const SymKind = iota // Constant + Typ // Type + Var // Variable + Func // Function + Bin // Binary from runtime + Bltn // Builtin +) + +var symKinds = [...]string{ + Const: "Const", + Typ: "Typ", + 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 + recv *Receiver // receiver node value, if sym refers to a method + 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 symbols in maps, and frame layout as array of types +// The purposes of scopes are to manage the visibility of each symbol +// and to store the memory frame layout informations (type and index in frame) +// at each level (global, package, functions) +// +// scopes are organized in a stack fashion: a first scope (universe) is created +// once at global level, and for each block (package, func, for, etc...), a new +// scope is pushed at entry, and poped at exit. +// +// Nested scopes with the same level value use the same frame: it allows to have +// eaxctly one frame per function, with a fixed position for each variable (named +// or not), no matter the inner complexity (number of nested blocks in the function) +// +// In symbols, the index value corresponds to the index in scope.types, and at +// execution to the index in frame, created exactly from the types layout. +// +type Scope struct { + anc *Scope // Ancestor upper scope + def *Node // function definition node this scope belongs to, or nil + types []reflect.Type // Frame layout, may be shared by same level scopes + level int // Frame level: number of frame indirections to access var during execution + 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) +} + +// push creates a new scope and chain it to the current one +func (s *Scope) push(indirect bool) *Scope { + scope := Scope{anc: s, level: s.level, sym: map[string]*Symbol{}} + if indirect { + scope.types = []reflect.Type{} + scope.level = s.level + 1 + } else { + // propagate size, types, def and global as scopes at same level share the same frame + scope.types = s.types + scope.def = s.def + scope.global = s.global + scope.level = s.level + } + return &scope +} + +func (s *Scope) pushBloc() *Scope { return s.push(false) } +func (s *Scope) pushFunc() *Scope { return s.push(true) } + +func (s *Scope) pop() *Scope { + if s.level == s.anc.level { + // propagate size and types, as scopes at same level share the same frame + s.anc.types = s.types + } + return s.anc +} + +// lookup searches for a symbol in the current scope, and upper ones if not found +// it returns the symbol, the number of indirections level from the current scope +// and status (false if no result) +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 +} + +// add adds a type to the scope types array, and returns its index +func (s *Scope) add(typ *Type) (index int) { + if typ == nil { + log.Panic("nil type") + } + index = len(s.types) + var t reflect.Type + switch typ.cat { + case FuncT: + t = reflect.TypeOf((*Node)(nil)) + case InterfaceT: + t = reflect.TypeOf((*valueInterface)(nil)).Elem() + default: + t = typ.TypeOf() + if t == nil { + log.Panic("nil reflect type") + } + } + s.types = append(s.types, t) + return +} diff --git a/interp/src.go b/interp/src.go index 927ccd1d..440c0165 100644 --- a/interp/src.go +++ b/interp/src.go @@ -81,13 +81,13 @@ func (i *Interpreter) importSrcFile(rPath, path, alias string) error { return nil } + i.resizeFrame() + // Once all package sources have been parsed, execute entry points then init functions for _, n := range rootNodes { if genRun(n) != nil { return err } - i.fsize++ - i.resizeFrame() i.run(n, nil) } diff --git a/interp/type.go b/interp/type.go index 184776b1..7e4c5963 100644 --- a/interp/type.go +++ b/interp/type.go @@ -286,29 +286,36 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) { t.incomplete = t.key.incomplete || t.val.incomplete case SelectorExpr: - pkgName, typeName := n.child[0].ident, n.child[1].ident - if sym, _, found := scope.lookup(pkgName); found { + 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.binType[sym.path] - if typ, ok := pkg[typeName]; ok { + types := interp.binType[sym.path] + if typ, ok := types[name]; ok { t.cat = ValueT t.rtype = typ - } else { - t.incomplete = true + break } + values := interp.binValue[sym.path] + if v, ok := values[name]; ok { + t.cat = ValueT + t.rtype = v.Type() + break + } + t.incomplete = true + case SrcPkgT: - pkg := interp.scope[pkgName] - if st, ok := pkg.sym[typeName]; ok && st.kind == Typ { + spkg := interp.scope[pkg] + if st, ok := spkg.sym[name]; ok && st.kind == Typ { t = st.typ } } } else { - err = n.cfgError("undefined package: %s", pkgName) + err = n.cfgError("undefined package: %s", pkg) } // TODO: handle pkgsrc types diff --git a/interp/value.go b/interp/value.go index 2a8df0f8..47f2cc97 100644 --- a/interp/value.go +++ b/interp/value.go @@ -84,15 +84,6 @@ func genValue(n *Node) func(*Frame) reflect.Value { } } -func genValueAddr(n *Node) func(*Frame) *reflect.Value { - return func(f *Frame) *reflect.Value { - for level := n.level; level > 0; level-- { - f = f.anc - } - return &f.data[n.findex] - } -} - func genValueInterface(n *Node) func(*Frame) reflect.Value { value := genValue(n)