Improve handling of out of order declarations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -5,6 +5,3 @@ import "github.com/containous/gi/_test/provider"
|
||||
func main() {
|
||||
provider.F1()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from Foo
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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{}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user