improve handling of global symbols

This commit is contained in:
Marc Vertes
2018-09-21 11:33:40 +02:00
parent bddcb841ea
commit 2ce194d6d8
9 changed files with 149 additions and 117 deletions

10
_test/const2.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func main() {
println(a)
}
const a = "hello"
// Output:
// hello

8
_test/const3.go Normal file
View File

@@ -0,0 +1,8 @@
package main
const a, b, c int = 1, 2, 3
func main() { println(a, b, c) }
// Output:
// 1 2 3

View File

@@ -4,7 +4,8 @@ import "fmt"
const (
SomeString = "constant string"
SomeInt = 1
// SomeInt = 1
)
type T1 struct {

View File

@@ -119,7 +119,6 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
var initNodes []*Node
var exports *BinMap
var expval *ValueMap
var unresolved *UnresolvedMap
var iotaValue int
var pkgName string
@@ -145,19 +144,15 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
interp.scope[pkgName] = scope.push(0)
}
scope = interp.scope[pkgName]
log.Println("scope:", scope)
scope.size = interp.fsize
if pkg, ok := interp.Exports[pkgName]; ok {
exports = pkg
expval = interp.Expval[pkgName]
unresolved = interp.unresolved[pkgName]
} else {
exports = &BinMap{}
interp.Exports[pkgName] = exports
expval = &ValueMap{}
interp.Expval[pkgName] = expval
unresolved = &UnresolvedMap{}
interp.unresolved[pkgName] = unresolved
}
case For0, ForRangeStmt:
@@ -214,19 +209,6 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
scope = scope.push(0)
case TypeSpec:
/*
// Type analysis is performed recursively and no post-order processing
// needs to be done for types, so do not dive in subtree
if n.child[1].kind == Ident {
// Create a type alias of an existing one
n.typ = &Type{cat: AliasT, val: nodeType(interp, scope, n.child[1])}
} else {
// Define a new type
n.typ = nodeType(interp, scope, n.child[1])
}
scope.sym[n.child[0].ident] = &Symbol{kind: Typ, typ: n.typ}
// TODO: export type for use by runtime
*/
return false
case ArrayType, BasicLit, ChanType, MapType, StructType:
@@ -478,21 +460,14 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
}
} else {
// Resolve method and receiver path, store them in node static value for run
if methodDecl, ok := n.child[0].val.(*Node); ok {
if len(methodDecl.child[2].child) > 1 {
// Allocate frame for method return values (if any)
n.fsize = len(methodDecl.child[2].child[1].child)
n.typ = methodDecl.typ.ret[0]
// TODO: handle multiple return values
} else {
n.fsize = 0
}
methodDecl := n.child[0].val.(*Node)
if len(methodDecl.child[2].child) > 1 {
// Allocate frame for method return values (if any)
n.fsize = len(methodDecl.child[2].child[1].child)
n.typ = methodDecl.typ.ret[0]
// TODO: handle multiple return values
} else {
method := n.child[0].child[1].ident
_, _, ok := scope.lookup(n.child[0].child[1].ident)
(*unresolved)[method] = append((*unresolved)[method], n)
log.Println(n.index, "could not resolve forward method declaration", method, ok, unresolved, n.kind)
n.fsize = 0
}
n.child[0].findex = -1 // To force reading value from node instead of frame (methods)
}
@@ -521,7 +496,7 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
}
n.fsize = l
} else {
log.Println(n.index, "call to unknown def", n.child[0].ident)
log.Println(n.index, "call to unknown def", n.child[0].ident, sym.typ)
}
}
} else if n.child[0].kind == SelectorSrc {
@@ -680,11 +655,6 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
interp.scope[pkgName].sym[funcName].kind = Func
interp.scope[pkgName].sym[funcName].node = n
// Handle forward function declaration
for _, nod := range (*unresolved)[funcName] {
log.Println("fixing unresolved", funcName, nod.index)
}
case FuncLit:
n.typ = n.child[2].typ
n.val = n
@@ -712,6 +682,10 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
case Ident:
if n.anc.kind == File || (n.anc.kind == SelectorExpr && n.anc.child[0] != n) {
// skip symbol creation/lookup for idents used as key
} else if l := len(n.anc.child); n.anc.kind == Field && l > 1 && n.anc.child[l-1] != n {
// Create a new local symbol for func argument
n.findex = scope.inc(interp)
scope.sym[n.ident] = &Symbol{index: scope.size, kind: Var}
} else if sym, level, ok := scope.lookup(n.ident); ok {
n.typ, n.findex, n.level = sym.typ, sym.index, level
if n.findex < 0 {
@@ -732,22 +706,12 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
}
n.recv = n
} else {
if n.ident == "_" || n.anc.kind == Define || n.anc.kind == DefineX || n.anc.kind == Field || n.anc.kind == RangeStmt || n.anc.kind == ValueSpec {
if n.ident == "_" || n.anc.kind == Define || n.anc.kind == DefineX || n.anc.kind == RangeStmt || n.anc.kind == ValueSpec {
// Create a new local symbol for func argument or local var definition
n.findex = scope.inc(interp)
scope.sym[n.ident] = &Symbol{index: scope.size, global: scope.global, kind: Var}
} else {
// symbol may be defined globally elsewhere later, add an entry at pkg level
interp.fsize++
interp.scope[pkgName].size = interp.fsize
interp.scope[pkgName].sym[n.ident] = &Symbol{index: interp.fsize, global: true, kind: Var}
n.sym = interp.scope[pkgName].sym[n.ident]
n.level = scope.level
n.findex = interp.fsize
// Record undefined symbols for delayed resolution processing
if n.anc.kind != FuncDecl && !(n.anc.kind == KeyValueExpr && n.anc.child[0] == n) {
(*unresolved)[n.ident] = append((*unresolved)[n.ident], n)
}
log.Println(n.index, "unresolved global symbol", n.ident)
}
}

View File

@@ -1,7 +1,6 @@
package interp
import (
"log"
"path"
)
@@ -15,8 +14,25 @@ func (interp *Interpreter) Gta(root *Node) {
scope := interp.universe
root.Walk(func(n *Node) bool {
log.Println(n.index, n.kind)
switch n.kind {
case Define:
if len(n.child) > 1 {
scope.sym[n.child[0].ident] = &Symbol{
kind: Var,
global: scope.global,
index: scope.inc(interp),
typ: nodeType(interp, scope, n.child[1]),
}
} else {
scope.sym[n.child[0].ident] = &Symbol{
kind: Var,
global: scope.global,
index: scope.inc(interp),
typ: nodeType(interp, scope, n.anc.child[0].child[1]),
}
}
return false
case File:
pkgName = n.child[0].ident
if _, ok := interp.scope[pkgName]; !ok {
@@ -67,5 +83,4 @@ func (interp *Interpreter) Gta(root *Node) {
}
return true
}, nil)
log.Println(interp.scope[pkgName])
}

View File

@@ -53,8 +53,6 @@ type LibValueMap map[string]map[string]reflect.Value
type LibTypeMap map[string]map[string]reflect.Type
type UnresolvedMap map[string][]*Node
// Opt stores interpreter options
type Opt struct {
AstDot bool // display AST graph (debug)
@@ -66,16 +64,15 @@ type Opt struct {
// Interpreter contains global resources and state
type Interpreter struct {
Opt
Frame *Frame // programe 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
binValue LibValueMap
binType LibTypeMap
unresolved map[string]*UnresolvedMap
Exports PkgMap // exported symbols for use by runtime
Expval PkgValueMap // same as abobe (TODO: keep only one)
Frame *Frame // programe 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
binValue LibValueMap
binType LibTypeMap
Exports PkgMap // exported symbols for use by runtime
Expval PkgValueMap // same as abobe (TODO: keep only one)
}
// Walk traverses AST n in depth first order, call cbin function
@@ -95,15 +92,14 @@ func (n *Node) Walk(in func(n *Node) bool, out func(n *Node)) {
// NewInterpreter creates and returns a new interpreter object
func NewInterpreter(opt Opt) *Interpreter {
return &Interpreter{
Opt: opt,
universe: initUniverse(),
scope: map[string]*Scope{},
Exports: make(PkgMap),
Expval: make(PkgValueMap),
binValue: LibValueMap(stdlib.Value),
binType: LibTypeMap(stdlib.Type),
unresolved: make(map[string]*UnresolvedMap),
Frame: &Frame{data: []interface{}{}},
Opt: opt,
universe: initUniverse(),
scope: map[string]*Scope{},
Exports: make(PkgMap),
Expval: make(PkgValueMap),
binValue: LibValueMap(stdlib.Value),
binType: LibTypeMap(stdlib.Type),
Frame: &Frame{data: []interface{}{}},
}
}
@@ -166,6 +162,7 @@ func (i *Interpreter) Eval(src string) string {
root.AstDot(DotX())
}
// Global type analysis
i.Gta(root)
// Annotate AST with CFG infos
@@ -178,14 +175,6 @@ func (i *Interpreter) Eval(src string) string {
root.CfgDot(DotX())
}
// Fix nodes with unresolved symbols due to out of order parsing
for _, nodes := range *(i.unresolved[pkgName]) {
for _, n := range nodes {
n.typ = n.sym.typ
n.val = n.sym.val
}
}
// Execute CFG
if !i.NoRun {
i.fsize++

View File

@@ -578,6 +578,38 @@ func main() {
// 1 hello
}
func Example_const2() {
src := `
package main
func main() {
println(a)
}
const a = "hello"
`
i := NewInterpreter(Opt{Entry: "main"})
i.Eval(src)
// Output:
// hello
}
func Example_const3() {
src := `
package main
const a, b, c int = 1, 2, 3
func main() { println(a, b, c) }
`
i := NewInterpreter(Opt{Entry: "main"})
i.Eval(src)
// Output:
// 1 2 3
}
func Example_cont() {
src := `
package main

View File

@@ -20,7 +20,7 @@ func (interp *Interpreter) importSrcFile(path string) {
rootNodes := []*Node{}
var root *Node
var pkgName string
//var pkgName string
// Parse source files
for _, file := range files {
@@ -38,7 +38,7 @@ func (interp *Interpreter) importSrcFile(path string) {
log.Fatal(err)
}
pkgName, root = interp.Ast(string(buf))
_, root = interp.Ast(string(buf))
rootNodes = append(rootNodes, root)
if interp.AstDot {
root.AstDot(DotX())
@@ -51,15 +51,6 @@ func (interp *Interpreter) importSrcFile(path string) {
initNodes = append(initNodes, interp.Cfg(root)...)
}
// Fix nodes with unresolved symbols due to out of order parsing
log.Println("unresolved:", *(interp.unresolved[pkgName]))
for _, nodes := range *(interp.unresolved[pkgName]) {
for _, n := range nodes {
n.typ = n.sym.typ
n.val = n.sym.val
}
}
if interp.NoRun {
return
}

View File

@@ -119,8 +119,15 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
if n.typ != nil && !n.typ.incomplete {
return n.typ
}
var t = &Type{node: n, scope: scope}
switch n.kind {
case Address, StarExpr:
t.cat = PtrT
t.val = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
case ArrayType:
t.cat = ArrayT
if len(n.child) > 1 {
@@ -139,15 +146,12 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
}
}
t.val = nodeType(interp, scope, n.child[1])
if t.val.incomplete {
t.incomplete = true
}
t.incomplete = t.incomplete || t.val.incomplete
} else {
t.val = nodeType(interp, scope, n.child[0])
if t.val.incomplete {
t.incomplete = true
}
t.incomplete = t.val.incomplete
}
case BasicLit:
switch n.val.(type) {
case bool:
@@ -165,43 +169,53 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
default:
log.Panicf("Missing support for basic type %T, node %v\n", n.val, n.index)
}
case ChanType:
t.cat = ChanT
t.val = nodeType(interp, scope, n.child[0])
if t.val.incomplete {
t.incomplete = true
}
t.incomplete = t.val.incomplete
case CompositeLitExpr:
t = nodeType(interp, scope, n.child[0])
case Ellipsis:
t = nodeType(interp, scope, n.child[0])
t.variadic = true
case FuncType:
t.cat = FuncT
for _, arg := range n.child[0].child {
t.arg = append(t.arg, nodeType(interp, scope, arg.child[len(arg.child)-1]))
typ := nodeType(interp, scope, arg.child[len(arg.child)-1])
t.arg = append(t.arg, typ)
t.incomplete = t.incomplete || typ.incomplete
}
if len(n.child) == 2 {
for _, ret := range n.child[1].child {
t.ret = append(t.ret, nodeType(interp, scope, ret.child[len(ret.child)-1]))
typ := nodeType(interp, scope, ret.child[len(ret.child)-1])
t.ret = append(t.ret, typ)
t.incomplete = t.incomplete || typ.incomplete
}
}
case Ident:
if sym, _, found := scope.lookup(n.ident); found {
t = sym.typ
} else {
t.incomplete = true
}
case InterfaceType:
t.cat = InterfaceT
//for _, method := range n.child[0].child {
// t.method = append(t.method, nodeType(interp, scope, method))
//}
case MapType:
t.cat = MapT
t.key = nodeType(interp, scope, n.child[0])
t.val = nodeType(interp, scope, n.child[1])
if t.key.incomplete || t.val.incomplete {
t.incomplete = true
}
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 {
@@ -218,32 +232,28 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
log.Panicln("unknown package", pkgName)
}
// TODO: handle pkgsrc types
case StarExpr:
t.cat = PtrT
t.val = nodeType(interp, scope, n.child[0])
case StructType:
t.cat = StructT
for _, c := range n.child[0].child {
if len(c.child) == 1 {
typ := nodeType(interp, scope, c.child[0])
if typ.incomplete {
t.incomplete = true
}
t.field = append(t.field, StructField{typ: typ})
t.incomplete = t.incomplete || typ.incomplete
} else {
l := len(c.child)
typ := nodeType(interp, scope, c.child[l-1])
if typ.incomplete {
t.incomplete = true
}
t.incomplete = t.incomplete || typ.incomplete
for _, d := range c.child[:l-1] {
t.field = append(t.field, StructField{name: d.ident, typ: typ})
}
}
}
default:
log.Panicln("type definition not implemented for node", n.index, n.kind)
}
return t
}
@@ -284,20 +294,24 @@ func (t *Type) zero() interface{} {
switch t.cat {
case AliasT:
return t.val.zero()
case ArrayT:
if t.size > 0 {
return reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(t.val.zero())), t.size, t.size).Interface()
} else {
return reflect.Zero(reflect.SliceOf(reflect.TypeOf(t.val.zero()))).Interface()
}
case StructT:
z := make([]interface{}, len(t.field))
for i, f := range t.field {
z[i] = f.typ.zero()
}
return z
case ValueT:
return reflect.New(t.rtype).Elem().Interface()
default:
return zeroValues[t.cat]
}
@@ -385,11 +399,14 @@ func (t *Type) TypeOf() reflect.Type {
switch t.cat {
case ArrayT:
return reflect.SliceOf(t.val.TypeOf())
case ChanT:
return reflect.ChanOf(reflect.BothDir, t.val.TypeOf())
case ErrorT:
var e = new(error)
return reflect.TypeOf(e).Elem()
case FuncT:
in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret))
@@ -400,10 +417,13 @@ func (t *Type) TypeOf() reflect.Type {
out[i] = v.TypeOf()
}
return reflect.FuncOf(in, out, false)
case MapT:
return reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
case PtrT:
return reflect.PtrTo(t.val.TypeOf())
case StructT:
var fields = []reflect.StructField{}
for _, f := range t.field {
@@ -413,8 +433,10 @@ func (t *Type) TypeOf() reflect.Type {
fields = append(fields, reflect.StructField{Name: f.name, Type: f.typ.TypeOf()})
}
return reflect.StructOf(fields)
case ValueT:
return t.rtype
default:
return reflect.TypeOf(t.zero())
}