Introduce gobal type analysis step. Add several tests for out of order.
This commit is contained in:
15
_test/const1.go
Normal file
15
_test/const1.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
var t = T{1, "hello"}
|
||||
|
||||
func main() {
|
||||
println(t.a, t.b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1 hello
|
||||
12
_test/method9.go
Normal file
12
_test/method9.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type Coord struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
o := Coord{3, 4}
|
||||
println(o.dist())
|
||||
}
|
||||
|
||||
func (c Coord) dist() int { return c.x*c.x + c.y*c.y }
|
||||
@@ -4,6 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
SomeString = "constant string"
|
||||
SomeInt = 1
|
||||
)
|
||||
|
||||
type T1 struct {
|
||||
|
||||
10
_test/run11.go
Normal file
10
_test/run11.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println(f())
|
||||
}
|
||||
|
||||
func f() (int, int) { return 2, 3 }
|
||||
|
||||
// Output:
|
||||
// 2 3
|
||||
13
_test/run12.go
Normal file
13
_test/run12.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f(a int) (int, int) {
|
||||
return a + 1, a + 2
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, b := f(3)
|
||||
println(a, b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4 5
|
||||
13
_test/run13.go
Normal file
13
_test/run13.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a, b := f(3)
|
||||
println(a, b)
|
||||
}
|
||||
|
||||
func f(a int) (int, int) {
|
||||
return a + 1, a + 2
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4 5
|
||||
16
_test/type5.go
Normal file
16
_test/type5.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type T int
|
||||
|
||||
func main() {
|
||||
a := T(12)
|
||||
fmt.Println(reflect.TypeOf(a))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// int
|
||||
16
_test/type6.go
Normal file
16
_test/type6.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := T(12)
|
||||
fmt.Println(reflect.TypeOf(a))
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
// Output:
|
||||
// int
|
||||
@@ -2,7 +2,6 @@ package interp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unicode"
|
||||
@@ -36,14 +35,15 @@ func (k SymKind) String() string {
|
||||
|
||||
// A Symbol represents an interpreter object such as type, constant, var, func, builtin or binary object
|
||||
type Symbol struct {
|
||||
kind SymKind
|
||||
typ *Type // Type of value
|
||||
node *Node // Node value if index is negative
|
||||
index int // index of value in frame or -1
|
||||
val interface{} // default value (used for constants)
|
||||
path string // package path if typ.cat is SrcPkgT or BinPkgT
|
||||
builtin Builtin // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
kind SymKind
|
||||
typ *Type // Type of value
|
||||
node *Node // Node value if index is negative
|
||||
index int // index of value in frame or -1
|
||||
val interface{} // default value (used for constants)
|
||||
path string // package path if typ.cat is SrcPkgT or BinPkgT
|
||||
builtin Builtin // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
constant bool // true if symbol value is constant
|
||||
}
|
||||
|
||||
// A SymMap stores symbols indexed by name
|
||||
@@ -145,20 +145,18 @@ 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 {
|
||||
x := make(BinMap)
|
||||
exports = &x
|
||||
exports = &BinMap{}
|
||||
interp.Exports[pkgName] = exports
|
||||
y := make(ValueMap)
|
||||
expval = &y
|
||||
expval = &ValueMap{}
|
||||
interp.Expval[pkgName] = expval
|
||||
z := make(UnresolvedMap)
|
||||
unresolved = &z
|
||||
unresolved = &UnresolvedMap{}
|
||||
interp.unresolved[pkgName] = unresolved
|
||||
}
|
||||
|
||||
@@ -216,17 +214,19 @@ 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
|
||||
/*
|
||||
// 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:
|
||||
@@ -787,29 +787,30 @@ func (interp *Interpreter) Cfg(root *Node) []*Node {
|
||||
fbody.tnext = n
|
||||
scope = scope.pop()
|
||||
|
||||
case ImportSpec:
|
||||
var name, ipath string
|
||||
if len(n.child) == 2 {
|
||||
ipath = n.child[1].val.(string)
|
||||
name = n.child[0].ident
|
||||
} else {
|
||||
ipath = n.child[0].val.(string)
|
||||
name = path.Base(ipath)
|
||||
}
|
||||
if pkg, ok := interp.binValue[ipath]; ok {
|
||||
if name == "." {
|
||||
for n, s := range pkg {
|
||||
scope.sym[n] = &Symbol{typ: &Type{cat: BinT}, val: s}
|
||||
/*
|
||||
case ImportSpec:
|
||||
var name, ipath string
|
||||
if len(n.child) == 2 {
|
||||
ipath = n.child[1].val.(string)
|
||||
name = n.child[0].ident
|
||||
} else {
|
||||
ipath = n.child[0].val.(string)
|
||||
name = path.Base(ipath)
|
||||
}
|
||||
} else {
|
||||
scope.sym[name] = &Symbol{typ: &Type{cat: BinPkgT}, path: ipath}
|
||||
}
|
||||
} 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}, path: ipath}
|
||||
}
|
||||
|
||||
if pkg, ok := interp.binValue[ipath]; ok {
|
||||
if name == "." {
|
||||
for n, s := range pkg {
|
||||
scope.sym[n] = &Symbol{typ: &Type{cat: BinT}, val: s}
|
||||
}
|
||||
} else {
|
||||
scope.sym[name] = &Symbol{typ: &Type{cat: BinPkgT}, path: ipath}
|
||||
}
|
||||
} 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}, path: ipath}
|
||||
}
|
||||
*/
|
||||
case KeyValueExpr:
|
||||
wireChild(n)
|
||||
|
||||
|
||||
71
interp/gta.go
Normal file
71
interp/gta.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Gta performs a global types analysis on the AST, registering types,
|
||||
// variables and functions at package level, prior to CFG. All function
|
||||
// bodies are skipped.
|
||||
// GTA is necessary to handle out of order declarations and multiple
|
||||
// source files packages.
|
||||
func (interp *Interpreter) Gta(root *Node) {
|
||||
var pkgName string
|
||||
scope := interp.universe
|
||||
|
||||
root.Walk(func(n *Node) bool {
|
||||
log.Println(n.index, n.kind)
|
||||
switch n.kind {
|
||||
case File:
|
||||
pkgName = n.child[0].ident
|
||||
if _, ok := interp.scope[pkgName]; !ok {
|
||||
interp.scope[pkgName] = scope.push(0)
|
||||
}
|
||||
scope = interp.scope[pkgName]
|
||||
|
||||
case FuncDecl:
|
||||
scope.sym[n.child[1].ident] = &Symbol{
|
||||
kind: Func,
|
||||
typ: nodeType(interp, scope, n.child[2]),
|
||||
node: n,
|
||||
}
|
||||
return false
|
||||
|
||||
case ImportSpec:
|
||||
var name, ipath string
|
||||
if len(n.child) == 2 {
|
||||
ipath = n.child[1].val.(string)
|
||||
name = n.child[0].ident
|
||||
} else {
|
||||
ipath = n.child[0].val.(string)
|
||||
name = path.Base(ipath)
|
||||
}
|
||||
if pkg, ok := interp.binValue[ipath]; ok {
|
||||
if name == "." {
|
||||
for n, s := range pkg {
|
||||
scope.sym[n] = &Symbol{typ: &Type{cat: BinT}, val: s}
|
||||
}
|
||||
} else {
|
||||
scope.sym[name] = &Symbol{typ: &Type{cat: BinPkgT}, path: ipath}
|
||||
}
|
||||
} 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}, path: ipath}
|
||||
}
|
||||
|
||||
case TypeSpec:
|
||||
if n.child[1].kind == Ident {
|
||||
n.typ = &Type{cat: AliasT, val: nodeType(interp, scope, n.child[1])}
|
||||
} else {
|
||||
n.typ = nodeType(interp, scope, n.child[1])
|
||||
}
|
||||
scope.sym[n.child[0].ident] = &Symbol{kind: Typ, typ: n.typ}
|
||||
return false
|
||||
|
||||
}
|
||||
return true
|
||||
}, nil)
|
||||
log.Println(interp.scope[pkgName])
|
||||
}
|
||||
@@ -166,6 +166,8 @@ func (i *Interpreter) Eval(src string) string {
|
||||
root.AstDot(DotX())
|
||||
}
|
||||
|
||||
i.Gta(root)
|
||||
|
||||
// Annotate AST with CFG infos
|
||||
initNodes := i.Cfg(root)
|
||||
if sym := i.scope[pkgName].sym[i.Entry]; sym != nil {
|
||||
|
||||
@@ -554,6 +554,28 @@ func main() {
|
||||
|
||||
}
|
||||
|
||||
func Example_const1() {
|
||||
src := `
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
var t = T{1, "hello"}
|
||||
|
||||
func main() {
|
||||
println(t.a, t.b)
|
||||
}
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// 1 hello
|
||||
}
|
||||
|
||||
func Example_cont() {
|
||||
src := `
|
||||
package main
|
||||
@@ -2047,10 +2069,13 @@ func main() {
|
||||
println(f())
|
||||
}
|
||||
|
||||
func f() (int, int) { return 2, 3 }`
|
||||
func f() (int, int) { return 2, 3 }
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// 2 3
|
||||
}
|
||||
|
||||
func Example_run12() {
|
||||
@@ -2064,10 +2089,33 @@ func f(a int) (int, int) {
|
||||
func main() {
|
||||
a, b := f(3)
|
||||
println(a, b)
|
||||
}`
|
||||
}
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// 4 5
|
||||
}
|
||||
|
||||
func Example_run13() {
|
||||
src := `
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a, b := f(3)
|
||||
println(a, b)
|
||||
}
|
||||
|
||||
func f(a int) (int, int) {
|
||||
return a + 1, a + 2
|
||||
}
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// 4 5
|
||||
}
|
||||
|
||||
func Example_run4() {
|
||||
@@ -3239,6 +3287,52 @@ func main() {
|
||||
// int32
|
||||
}
|
||||
|
||||
func Example_type5() {
|
||||
src := `
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type T int
|
||||
|
||||
func main() {
|
||||
a := T(12)
|
||||
fmt.Println(reflect.TypeOf(a))
|
||||
}
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// int
|
||||
}
|
||||
|
||||
func Example_type6() {
|
||||
src := `
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := T(12)
|
||||
fmt.Println(reflect.TypeOf(a))
|
||||
}
|
||||
|
||||
type T int
|
||||
`
|
||||
i := NewInterpreter(Opt{Entry: "main"})
|
||||
i.Eval(src)
|
||||
|
||||
// Output:
|
||||
// int
|
||||
}
|
||||
|
||||
func Example_var() {
|
||||
src := `
|
||||
package main
|
||||
|
||||
@@ -693,7 +693,8 @@ func compositeSparse(n *Node, f *Frame) {
|
||||
}
|
||||
|
||||
func _range(n *Node, f *Frame) {
|
||||
i, index := 0, n.child[0].findex
|
||||
i := 0
|
||||
index := n.child[0].findex
|
||||
if f.data[index] != nil {
|
||||
i = f.data[index].(int) + 1
|
||||
}
|
||||
|
||||
@@ -39,14 +39,20 @@ func (interp *Interpreter) importSrcFile(path string) {
|
||||
}
|
||||
|
||||
pkgName, root = interp.Ast(string(buf))
|
||||
initNodes = append(initNodes, interp.Cfg(root)...)
|
||||
rootNodes = append(rootNodes, root)
|
||||
if interp.AstDot {
|
||||
root.AstDot(DotX())
|
||||
}
|
||||
interp.Gta(root)
|
||||
}
|
||||
|
||||
// Generate control flow graphs
|
||||
for _, root := range rootNodes {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user