feat: add support for named output variables (#113)

* feat: add support for named output variables

Function output parameters are located at the start of the function
frame, uninitialized, as they could be only set through return
statements.

Supporting named output variables requires to:

- identify and allocate symbols corresponding to output names:
  done at ident parsing, with the funcRet() helper,

- compute the location of output name in the frame:
  done with retRank() helper,

- initialize the frame entry with the zero value of symbol type,
  as the symbol can be accessed and written at multiple times,
  and return not setting the variable (opposite to unnamed).
  Done with frameType() helper, which now takes into account
  output parameters.

* refactor: simplify memory management

Perform function frame analysis at pre-order, instead of post-order, to
initialize memory frame, and track value types. Remove all operation
involving unitialized types. Frame memory layout is now build
incrementally, instead of having to perform dedicated tree walks on AST.
This commit is contained in:
Marc Vertes
2019-03-12 19:58:02 +01:00
committed by Ludovic Fernandez
parent 22a6d011f4
commit 6657e9a18b
12 changed files with 620 additions and 509 deletions

20
_test/variadic3.go Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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: "<nil>"},
{
desc: "return func which return nil error",
desc: "return func which return error",
pre: func() {
eval(t, i, `
package bar

View File

@@ -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
}

View File

@@ -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

151
interp/scope.go Normal file
View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)