Improve handling of out of order declarations

This commit is contained in:
Marc Vertes
2018-07-24 16:32:08 +02:00
parent 915d976d12
commit 567985f0cf
8 changed files with 84 additions and 62 deletions

View File

@@ -2,11 +2,11 @@ package main
import "fmt" import "fmt"
func r2() (int, int) { return 1, 2 }
func main() { func main() {
fmt.Println(r2()) fmt.Println(r2())
} }
func r2() (int, int) { return 1, 2 }
// Output: // Output:
// 1 2 // 1 2

View File

@@ -5,6 +5,3 @@ import "github.com/containous/gi/_test/provider"
func main() { func main() {
provider.F1() provider.F1()
} }
// Output:
// Hello from Foo

View File

@@ -261,9 +261,8 @@ func (a Action) String() string {
// of CFG, in order to accomodate forward type declarations // of CFG, in order to accomodate forward type declarations
// Ast parses src string containing Go code and generates the corresponding AST. // Ast parses src string containing Go code and generates the corresponding AST.
// The AST root node is returned. // The package name and the AST root node are returned.
func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) { func (interp *Interpreter) Ast(src string) (string, *Node) {
def := NodeMap{}
fset := token.NewFileSet() // positions are relative to fset fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "sample.go", src, 0) f, err := parser.ParseFile(fset, "sample.go", src, 0)
if err != nil { if err != nil {
@@ -494,8 +493,6 @@ func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
// function is not a method, create an empty receiver list // function is not a method, create an empty receiver list
addChild(&root, n, &index, FieldList, Nop) addChild(&root, n, &index, FieldList, Nop)
} }
// Add func name to definitions
def[a.Name.Name] = n
st.push(n) st.push(n)
case *ast.FuncLit: case *ast.FuncLit:
@@ -609,10 +606,7 @@ func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
st.push(addChild(&root, anc, &index, TypeAssertExpr, TypeAssert)) st.push(addChild(&root, anc, &index, TypeAssertExpr, TypeAssert))
case *ast.TypeSpec: case *ast.TypeSpec:
n := addChild(&root, anc, &index, TypeSpec, Nop) st.push(addChild(&root, anc, &index, TypeSpec, Nop))
// Add type name to definitions
def[a.Name.Name] = n
st.push(n)
case *ast.UnaryExpr: case *ast.UnaryExpr:
var kind = UnaryExpr var kind = UnaryExpr
@@ -657,7 +651,7 @@ func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
} }
return true return true
}) })
return root, &def return pkgName, root
} }
type nodestack []*Node type nodestack []*Node

View File

@@ -13,7 +13,6 @@ type Symbol struct {
node *Node // Node value if index is negative node *Node // Node value if index is negative
index int // index of value in frame or -1 index int // index of value in frame or -1
pkgbin *SymMap // Map of package symbols if typ.cat is BinPkgT, or nil pkgbin *SymMap // Map of package symbols if typ.cat is BinPkgT, or nil
pkgsrc *NodeMap // Map of package symbols if typ.cat is SrcPkgT, or nil
bin interface{} // Symbol from imported bin package if typ.cat is BinT, or nil bin interface{} // Symbol from imported bin package if typ.cat is BinT, or nil
} }
@@ -50,14 +49,14 @@ type FrameIndex struct {
// PkgContext type stores current state of a package during compiling // PkgContext type stores current state of a package during compiling
type PkgContext struct { type PkgContext struct {
frameIndex *FrameIndex frameIndex *FrameIndex
nodeMap *NodeMap NodeMap
} }
// Cfg generates a control flow graph (CFG) from AST (wiring successors in AST) // 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 // and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned. // variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run // Following this pass, the CFG is ready to run
func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node { func (interp *Interpreter) Cfg(root *Node) []*Node {
scope := &Scope{sym: map[string]*Symbol{}} scope := &Scope{sym: map[string]*Symbol{}}
frameIndex := &FrameIndex{} frameIndex := &FrameIndex{}
var loop, loopRestart *Node var loop, loopRestart *Node
@@ -66,13 +65,7 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
var exports *SymMap var exports *SymMap
var expval *ValueMap var expval *ValueMap
var iotaValue int var iotaValue int
var srcPkg *NodeMap var pkgName string
// Fill root scope with initial symbol definitions
for name, node := range *sdef {
scope.sym[name] = &Symbol{node: node, index: -1}
}
log.Println(interp.srcPkg["provider"])
root.Walk(func(n *Node) bool { root.Walk(func(n *Node) bool {
// Pre-order processing // Pre-order processing
@@ -91,10 +84,9 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
scope = scope.push(0) scope = scope.push(0)
case File: case File:
pkgName := n.child[0].ident pkgName = n.child[0].ident
srcPkg = interp.srcPkg[pkgName] if _, ok := interp.context[pkgName]; !ok {
if srcPkg == nil { interp.context[pkgName] = PkgContext{NodeMap: NodeMap{}}
srcPkg = &NodeMap{}
} }
if pkg, ok := interp.Exports[pkgName]; ok { if pkg, ok := interp.Exports[pkgName]; ok {
exports = pkg exports = pkg
@@ -174,6 +166,7 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
} }
// TODO: deprecate use of interp.types in favor of scope.sym // TODO: deprecate use of interp.types in favor of scope.sym
interp.types[n.child[0].ident] = n.typ interp.types[n.child[0].ident] = n.typ
interp.context[pkgName].NodeMap[n.child[0].ident] = n
scope.sym[n.child[0].ident] = &Symbol{typ: n.typ} scope.sym[n.child[0].ident] = &Symbol{typ: n.typ}
// TODO: export type for use by runtime // TODO: export type for use by runtime
return false return false
@@ -427,6 +420,8 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
n.typ = interp.types[def.child[2].child[j].child[0].child[0].ident] n.typ = interp.types[def.child[2].child[j].child[0].child[0].ident]
} }
n.fsize = l n.fsize = l
} else {
log.Println(n.index, "call to unknown def")
} }
} }
} else if n.child[0].kind == SelectorSrc { } else if n.child[0].kind == SelectorSrc {
@@ -578,6 +573,9 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
n.typ = n.child[2].typ n.typ = n.child[2].typ
n.val = n n.val = n
scope.sym[funcName].typ = n.typ scope.sym[funcName].typ = n.typ
scope.sym[funcName].node = n
scope.sym[funcName].index = -1
interp.context[pkgName].NodeMap[funcName] = n
case FuncLit: case FuncLit:
n.typ = n.child[2].typ n.typ = n.child[2].typ
@@ -628,12 +626,12 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
} else if n.typ != nil { } else if n.typ != nil {
if n.typ.cat == BinPkgT { if n.typ.cat == BinPkgT {
n.val = sym.pkgbin n.val = sym.pkgbin
} else if n.typ.cat == SrcPkgT {
n.val = sym.pkgsrc
} }
} else {
n.sym = sym
} }
n.recv = n n.recv = n
} else if node, ok := (*srcPkg)[n.ident]; ok { } else if node, ok := interp.context[pkgName].NodeMap[n.ident]; ok {
n.val = node n.val = node
n.typ = node.typ n.typ = node.typ
n.kind = node.kind n.kind = node.kind
@@ -649,17 +647,20 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
symFrameIndex := frameIndex symFrameIndex := frameIndex
symScope := scope symScope := scope
level := 0 level := 0
// Back to package level scope
for symFrameIndex.anc != nil { for symFrameIndex.anc != nil {
level++ level++
symScope = symScope.anc
symFrameIndex = symFrameIndex.anc symFrameIndex = symFrameIndex.anc
} }
for symScope.anc != nil {
symScope = symScope.anc
}
symFrameIndex.max++ symFrameIndex.max++
symScope.sym[n.ident] = &Symbol{index: symFrameIndex.max} symScope.sym[n.ident] = &Symbol{index: symFrameIndex.max}
(*sdef)[n.ident] = n interp.context[pkgName].NodeMap[n.ident] = n
n.findex = symFrameIndex.max n.findex = symFrameIndex.max
n.level = level n.level = level
log.Println(n.index, n.ident, n.anc.kind, "unresolved, create new at pkg level", n.findex, level) n.sym = symScope.sym[n.ident]
} }
} }
@@ -719,7 +720,7 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
} else { } else {
// TODO: make sure we do not import a src package more than once // TODO: make sure we do not import a src package more than once
interp.importSrcFile(ipath) interp.importSrcFile(ipath)
scope.sym[name] = &Symbol{typ: &Type{cat: SrcPkgT}, pkgsrc: interp.srcPkg[name]} scope.sym[name] = &Symbol{typ: &Type{cat: SrcPkgT}}
} }
case KeyValueExpr: case KeyValueExpr:
@@ -818,15 +819,13 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
n.run = nop n.run = nop
} else if n.typ.cat == SrcPkgT { } else if n.typ.cat == SrcPkgT {
// Resolve source package symbol // Resolve source package symbol
pkgSrc := n.child[0].val.(*NodeMap) if node, ok := interp.context[n.child[0].ident].NodeMap[n.child[1].ident]; ok {
name := n.child[1].ident
if node, ok := (*pkgSrc)[name]; ok {
n.val = node n.val = node
n.run = nop n.run = nop
n.kind = SelectorSrc n.kind = SelectorSrc
n.typ = node.typ n.typ = node.typ
} else { } else {
log.Println(n.index, "selector unresolved:", n.child[0].ident+"."+name) log.Println(n.index, "selector unresolved:", n.child[0].ident+"."+n.child[1].ident)
} }
} else if fi := n.typ.fieldIndex(n.child[1].ident); fi >= 0 { } else if fi := n.typ.fieldIndex(n.child[1].ident); fi >= 0 {
// Resolve struct field index // Resolve struct field index
@@ -896,7 +895,6 @@ func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
} }
} }
}) })
log.Println(sdef)
return initNodes return initNodes
} }

View File

@@ -2,6 +2,7 @@
package interp package interp
import ( import (
"log"
"reflect" "reflect"
) )
@@ -19,6 +20,7 @@ type Node struct {
flen int // frame length (function definition) flen int // frame length (function definition)
level int // number of frame indirections to access value level int // number of frame indirections to access value
kind Kind // kind of node kind Kind // kind of node
sym *Symbol // associated symbol
typ *Type // type of value in frame, or nil typ *Type // type of value in frame, or nil
recv *Node // method receiver node for call, or nil recv *Node // method receiver node for call, or nil
frame *Frame // frame pointer, only used for script callbacks from runtime (wrapNode) frame *Frame // frame pointer, only used for script callbacks from runtime (wrapNode)
@@ -41,6 +43,8 @@ type NodeMap map[string]*Node
// PkgSrcMap stores package source nodes // PkgSrcMap stores package source nodes
type PkgSrcMap map[string]*NodeMap type PkgSrcMap map[string]*NodeMap
type PkgCtxMap map[string]PkgContext
// SymMap stores executable symbols indexed by name // SymMap stores executable symbols indexed by name
type SymMap map[string]interface{} type SymMap map[string]interface{}
@@ -66,7 +70,7 @@ type Interpreter struct {
Opt Opt
Frame *Frame Frame *Frame
types TypeMap types TypeMap
srcPkg PkgSrcMap context PkgCtxMap
binPkg PkgMap binPkg PkgMap
Exports PkgMap Exports PkgMap
Expval PkgValueMap Expval PkgValueMap
@@ -91,7 +95,7 @@ func NewInterpreter(opt Opt) *Interpreter {
return &Interpreter{ return &Interpreter{
Opt: opt, Opt: opt,
types: defaultTypes, types: defaultTypes,
srcPkg: make(PkgSrcMap), context: make(PkgCtxMap),
binPkg: make(PkgMap), binPkg: make(PkgMap),
Exports: make(PkgMap), Exports: make(PkgMap),
Expval: make(PkgValueMap), Expval: make(PkgValueMap),
@@ -116,16 +120,17 @@ func (i *Interpreter) ImportBin(pkg *map[string]*map[string]interface{}) {
// Eval evaluates Go code represented as a string. It returns a map on // Eval evaluates Go code represented as a string. It returns a map on
// current interpreted package exported symbols // current interpreted package exported symbols
func (i *Interpreter) Eval(src string) (string, *NodeMap) { func (i *Interpreter) Eval(src string) string {
// Parse source to AST // Parse source to AST
root, sdef := i.Ast(src) pkgName, root := i.Ast(src)
log.Println(pkgName)
if i.AstDot { if i.AstDot {
root.AstDot(DotX()) root.AstDot(DotX())
} }
// Annotate AST with CFG infos // Annotate AST with CFG infos
initNodes := i.Cfg(root, sdef) initNodes := i.Cfg(root)
if entry, ok := (*sdef)[i.Entry]; ok { if entry, ok := i.context[pkgName].NodeMap[i.Entry]; ok {
initNodes = append(initNodes, entry) initNodes = append(initNodes, entry)
} }
@@ -141,5 +146,5 @@ func (i *Interpreter) Eval(src string) (string, *NodeMap) {
Run(n, i.Frame, nil, nil, nil, nil, true, false) Run(n, i.Frame, nil, nil, nil, nil, true, false)
} }
} }
return root.child[0].ident, sdef return root.child[0].ident
} }

View File

@@ -1839,11 +1839,11 @@ package main
import "fmt" import "fmt"
func r2() (int, int) { return 1, 2 }
func main() { func main() {
fmt.Println(r2()) fmt.Println(r2())
} }
func r2() (int, int) { return 1, 2 }
` `
i := NewInterpreter(Opt{Entry: "main"}) i := NewInterpreter(Opt{Entry: "main"})
i.ImportBin(export.Pkg) i.ImportBin(export.Pkg)
@@ -2594,6 +2594,21 @@ func main() {
// Hello from Foo // Hello from Foo
} }
func Example_src4() {
src := `
package main
import "github.com/containous/gi/_test/provider"
func main() {
provider.F1()
}`
i := NewInterpreter(Opt{Entry: "main"})
i.ImportBin(export.Pkg)
i.Eval(src)
}
func Example_str() { func Example_str() {
src := ` src := `
package main package main

View File

@@ -2,7 +2,6 @@ package interp
import ( import (
"fmt" "fmt"
"log"
"reflect" "reflect"
"time" "time"
) )
@@ -128,7 +127,15 @@ func value(n *Node, f *Frame) interface{} {
case Rvalue: case Rvalue:
return n.rval return n.rval
default: default:
//log.Println(n.index, n.ident, n.findex, n.level, f) if n.sym != nil {
if n.sym.index < 0 {
return value(n.sym.node, f)
}
for level := n.level; level > 0; level-- {
f = f.anc
}
return f.data[n.sym.index]
}
for level := n.level; level > 0; level-- { for level := n.level; level > 0; level-- {
f = f.anc f = f.anc
} }
@@ -140,11 +147,19 @@ func value(n *Node, f *Frame) interface{} {
} }
func addrValue(n *Node, f *Frame) *interface{} { func addrValue(n *Node, f *Frame) *interface{} {
log.Println(n.index, n.ident, n.kind, n.level, n.findex)
switch n.kind { switch n.kind {
case BasicLit, FuncDecl, Rvalue: case BasicLit, FuncDecl, Rvalue:
return &n.val return &n.val
default: default:
if n.sym != nil {
if n.sym.index < 0 {
return addrValue(n.sym.node, f)
}
for level := n.level; level > 0; level-- {
f = f.anc
}
return &f.data[n.sym.index]
}
for level := n.level; level > 0; level-- { for level := n.level; level > 0; level-- {
f = f.anc f = f.anc
} }
@@ -307,6 +322,7 @@ func call(n *Node, f *Frame) {
recv = n.child[0].recv recv = n.child[0].recv
rseq = n.child[0].child[1].val.([]int) rseq = n.child[0].child[1].val.([]int)
} }
//log.Println(n.index, "call", n.child[0].ident, value(n.child[0], f))
fn := value(n.child[0], f).(*Node) fn := value(n.child[0], f).(*Node)
var ret []int var ret []int
if len(fn.child[2].child) > 1 { if len(fn.child[2].child) > 1 {
@@ -390,11 +406,12 @@ func callBin(n *Node, f *Frame) {
in[i] = value(c, f).(reflect.Value) in[i] = value(c, f).(reflect.Value)
c.frame = f c.frame = f
} else { } else {
//log.Println(value(c, f), c.sym, c.level)
in[i] = reflect.ValueOf(value(c, f)) in[i] = reflect.ValueOf(value(c, f))
} }
} }
fun := value(n.child[0], f).(reflect.Value) fun := value(n.child[0], f).(reflect.Value)
log.Println("in:", in) //log.Println(n.index, "in:", in)
v := fun.Call(in) v := fun.Call(in)
for i := 0; i < n.fsize; i++ { for i := 0; i < n.fsize; i++ {
f.data[n.findex+i] = v[i].Interface() f.data[n.findex+i] = v[i].Interface()
@@ -720,7 +737,7 @@ func slice(n *Node, f *Frame) {
// slice expression, no low value // slice expression, no low value
func slice0(n *Node, f *Frame) { func slice0(n *Node, f *Frame) {
log.Println(n.index, n.child[0].ident, value(n.child[0], f)) //log.Println(n.index, n.child[0].ident, value(n.child[0], f))
a := value(n.child[0], f).([]interface{}) a := value(n.child[0], f).([]interface{})
switch len(n.child) { switch len(n.child) {
case 1: case 1:

View File

@@ -26,13 +26,9 @@ func (interp *Interpreter) importSrcFile(path string) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
pkgName, sdef := interp.Eval(string(buf)) pkgName := interp.Eval(string(buf))
if interp.srcPkg[pkgName] == nil { if _, ok := interp.context[pkgName]; !ok {
s := make(NodeMap) interp.context[pkgName] = PkgContext{NodeMap: NodeMap{}}
interp.srcPkg[pkgName] = &s
}
for name, node := range *sdef {
(*interp.srcPkg[pkgName])[name] = node
} }
} }
} }