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"
func r2() (int, int) { return 1, 2 }
func main() {
fmt.Println(r2())
}
func r2() (int, int) { return 1, 2 }
// Output:
// 1 2

View File

@@ -5,6 +5,3 @@ import "github.com/containous/gi/_test/provider"
func main() {
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
// Ast parses src string containing Go code and generates the corresponding AST.
// The AST root node is returned.
func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
def := NodeMap{}
// The package name and the AST root node are returned.
func (interp *Interpreter) Ast(src string) (string, *Node) {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "sample.go", src, 0)
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
addChild(&root, n, &index, FieldList, Nop)
}
// Add func name to definitions
def[a.Name.Name] = n
st.push(n)
case *ast.FuncLit:
@@ -609,10 +606,7 @@ func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
st.push(addChild(&root, anc, &index, TypeAssertExpr, TypeAssert))
case *ast.TypeSpec:
n := addChild(&root, anc, &index, TypeSpec, Nop)
// Add type name to definitions
def[a.Name.Name] = n
st.push(n)
st.push(addChild(&root, anc, &index, TypeSpec, Nop))
case *ast.UnaryExpr:
var kind = UnaryExpr
@@ -657,7 +651,7 @@ func (interp *Interpreter) Ast(src string) (*Node, *NodeMap) {
}
return true
})
return root, &def
return pkgName, root
}
type nodestack []*Node

View File

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

View File

@@ -2,6 +2,7 @@
package interp
import (
"log"
"reflect"
)
@@ -19,6 +20,7 @@ type Node struct {
flen int // frame length (function definition)
level int // number of frame indirections to access value
kind Kind // kind of node
sym *Symbol // associated symbol
typ *Type // type of value in frame, or nil
recv *Node // method receiver node for call, or nil
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
type PkgSrcMap map[string]*NodeMap
type PkgCtxMap map[string]PkgContext
// SymMap stores executable symbols indexed by name
type SymMap map[string]interface{}
@@ -66,7 +70,7 @@ type Interpreter struct {
Opt
Frame *Frame
types TypeMap
srcPkg PkgSrcMap
context PkgCtxMap
binPkg PkgMap
Exports PkgMap
Expval PkgValueMap
@@ -91,7 +95,7 @@ func NewInterpreter(opt Opt) *Interpreter {
return &Interpreter{
Opt: opt,
types: defaultTypes,
srcPkg: make(PkgSrcMap),
context: make(PkgCtxMap),
binPkg: make(PkgMap),
Exports: make(PkgMap),
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
// current interpreted package exported symbols
func (i *Interpreter) Eval(src string) (string, *NodeMap) {
func (i *Interpreter) Eval(src string) string {
// Parse source to AST
root, sdef := i.Ast(src)
pkgName, root := i.Ast(src)
log.Println(pkgName)
if i.AstDot {
root.AstDot(DotX())
}
// Annotate AST with CFG infos
initNodes := i.Cfg(root, sdef)
if entry, ok := (*sdef)[i.Entry]; ok {
initNodes := i.Cfg(root)
if entry, ok := i.context[pkgName].NodeMap[i.Entry]; ok {
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)
}
}
return root.child[0].ident, sdef
return root.child[0].ident
}

View File

@@ -1839,11 +1839,11 @@ package main
import "fmt"
func r2() (int, int) { return 1, 2 }
func main() {
fmt.Println(r2())
}
func r2() (int, int) { return 1, 2 }
`
i := NewInterpreter(Opt{Entry: "main"})
i.ImportBin(export.Pkg)
@@ -2594,6 +2594,21 @@ func main() {
// 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() {
src := `
package main

View File

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

View File

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