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:
committed by
Ludovic Fernandez
parent
22a6d011f4
commit
6657e9a18b
20
_test/variadic3.go
Normal file
20
_test/variadic3.go
Normal 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
|
||||
@@ -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
|
||||
|
||||
561
interp/cfg.go
561
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
186
interp/run.go
186
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
|
||||
|
||||
151
interp/scope.go
Normal file
151
interp/scope.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user