improve handling of global symbols
This commit is contained in:
10
_test/const2.go
Normal file
10
_test/const2.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
const a = "hello"
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
8
_test/const3.go
Normal file
8
_test/const3.go
Normal 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
|
||||
@@ -4,7 +4,8 @@ import "fmt"
|
||||
|
||||
const (
|
||||
SomeString = "constant string"
|
||||
SomeInt = 1
|
||||
|
||||
// SomeInt = 1
|
||||
)
|
||||
|
||||
type T1 struct {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user