Apply better names for function and files. Add images for doc

This commit is contained in:
Marc Vertes
2018-02-01 15:20:37 +01:00
parent d1704750bf
commit 75f96aeac7
11 changed files with 86 additions and 65 deletions

View File

@@ -49,3 +49,25 @@ Our AST is represented with a tree of objects of all the same type Node, each no
This structure allows a simpler implementation of recursive tree walk, and makes possible an faster and more efficient non-recursive tree walk.
The tree walk allows callback both for pre-order and post-order processing.
## Example
Consider the following source:
```go
package main
func main() {
for a := 0; a < 10000; a++ {
if (a & 0x8ff) == 0x800 {
println(a)
}
}
}
```
AST: ![ast](images/ast.jpg)
CFG: ![cfg](images/cfg.jpg)
In CFG, the node labels correspond to the number in left of AST nodes

1
gi.go
View File

@@ -9,7 +9,6 @@ func main() {
package main
func main() {
println(1)
for a := 0; a < 10000; a++ {
if (a & 0x8ff) == 0x800 {
println(a)

BIN
images/ast.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
images/cfg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -6,8 +6,9 @@ import (
"go/token"
)
// Parse src string containing go code and generate AST. Returns the root node.
func SrcToAst(src string) *Node {
// Ast(src) parses src string containing Go code and generates the corresponding AST.
// The AST root node is returned.
func Ast(src string) *Node {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "sample.go", src, 0)
if err != nil {

View File

@@ -5,7 +5,7 @@ import (
"testing"
)
func TestSrcToAst(t *testing.T) {
func TestAst(t *testing.T) {
src := `
package main
@@ -13,6 +13,6 @@ func main() {
println(1)
}
`
root := SrcToAst(src)
root := Ast(src)
fmt.Println(root.index)
}

View File

@@ -7,16 +7,17 @@ import (
"strconv"
)
// TODO: remove coupling with go/ast and go/token. This should be handled only in SrcToAst
// TODO: remove coupling with go/ast and go/token. This should be handled only in Ast()
// Generate a CFG from AST (wiring successors in AST)
func (e *Node) AstToCfg() {
// n.Cfg() generates a control flow graph (CFG) from AST (wiring successors in AST)
func (e *Node) Cfg() {
//var findex int
e.Walk(nil, func(n *Node) {
switch x := (*n.anode).(type) {
case *ast.BlockStmt:
wire_child(n)
// FIXME: could bypass this node at CFG and wire directly last child
n.isnop = true
n.isNop = true
n.run = nop
n.val = n.Child[len(n.Child)-1].val
case *ast.IncDecStmt:
@@ -31,13 +32,13 @@ func (e *Node) AstToCfg() {
case *ast.ExprStmt:
wire_child(n)
// FIXME: could bypass this node at CFG and wire directly last child
n.isnop = true
n.isNop = true
n.run = nop
n.val = n.Child[len(n.Child)-1].val
case *ast.ParenExpr:
wire_child(n)
// FIXME: could bypass this node at CFG and wire directly last child
n.isnop = true
n.isNop = true
n.run = nop
n.val = n.Child[len(n.Child)-1].val
case *ast.BinaryExpr:
@@ -54,7 +55,7 @@ func (e *Node) AstToCfg() {
wire_child(n)
n.run = call
case *ast.IfStmt:
n.isnop = true
n.isNop = true
n.run = nop
n.Start = n.Child[0].Start
n.Child[1].snext = n
@@ -68,7 +69,7 @@ func (e *Node) AstToCfg() {
n.Child[0].next[0] = n
}
case *ast.ForStmt:
n.isnop = true
n.isNop = true
n.run = nop
// FIXME: works only if for node has 4 children
n.Start = n.Child[0].Start
@@ -78,6 +79,7 @@ func (e *Node) AstToCfg() {
n.Child[3].snext = n.Child[2].Start
n.Child[2].snext = n.Child[1].Start
case *ast.BasicLit:
n.isConst = true
// FIXME: values must be converted to int or float if possible
if v, err := strconv.ParseInt(x.Value, 0, 0); err == nil {
*n.val = v

View File

@@ -10,57 +10,68 @@ type RunFun func(n *Node, i *Interpreter)
// Structure for AST and CFG
type Node struct {
Child []*Node // child subtrees
anc *Node // ancestor
Start *Node // entry point in subtree (CFG)
snext *Node // successor (CFG)
next [2]*Node // conditional successors, for false and for true (CFG)
index int // node index (dot display)
run RunFun // function to run at CFG execution
val *interface{} // pointer on generic value (CFG execution)
ident string // set if node is a var or func
isnop bool // node is a no op
anode *ast.Node // original ast node (temporary, will be removed)
Child []*Node // child subtrees
anc *Node // ancestor
Start *Node // entry point in subtree (CFG)
snext *Node // successor (CFG)
next [2]*Node // conditional successors, for false and for true (CFG)
index int // node index (dot display)
findex int // index of value in frame
run RunFun // function to run at CFG execution
val *interface{} // pointer on generic value (CFG execution)
ident string // set if node is a var or func
isNop bool // node run function us a no-op
isConst bool // node value is a constant
anode *ast.Node // original ast node (temporary, will be removed)
}
// Interpreter execution context
// A Frame contains values for the current execution level
type Frame struct {
up *Frame // up level in call stack
down *Frame // down in call stack
val []interface{} // array of values
}
// Interpreter contains global resources and state
type Interpreter struct {
sym map[string]*interface{}
res interface{}
sym map[string]*interface{}
constant map[string]*interface{}
frame *Frame
out interface{}
}
// Returns true if node is a leaf in the AST
// n.isLeaf() returns true if Node n is a leaf in the AST
func (n *Node) isLeaf() bool {
return len((*n).Child) == 0
}
// Walk AST in depth first order, call 'in' function at node entry and
// 'out' function at node exit.
func (n *Node) Walk(in func(n *Node), out func(n *Node)) {
if in != nil {
in(n)
// n.Walk(cbin, cbout) traverses AST n in depth first order, call cbin function
// at node entry and cbout function at node exit.
func (n *Node) Walk(cbin func(n *Node), cbout func(n *Node)) {
if cbin != nil {
cbin(n)
}
for _, Child := range n.Child {
Child.Walk(in, out)
Child.Walk(cbin, cbout)
}
if out != nil {
out(n)
if cbout != nil {
cbout(n)
}
}
// Create and return a new interpreter object
// NewInterpreter()creates and returns a new interpreter object
func NewInterpreter() *Interpreter {
return &Interpreter{sym: make(map[string]*interface{})}
}
//
// i.Eval(s) evaluates Go code represented as a string
func (i *Interpreter) Eval(src string) interface{} {
root := SrcToAst(src)
root.AstDot()
cfg_entry := root.Child[1].Child[2] // FIXME: entry point should be resolved from 'main' name
cfg_entry.AstToCfg()
cfg_entry.OptimCfg()
//cfg_entry.CfgDot()
i.RunCfg(cfg_entry.Start)
return i.res
root := Ast(src)
//root.AstDot()
entry := root.Child[1].Child[2] // FIXME: entry point should be resolved from 'main' name
entry.Cfg()
entry.OptimCfg()
//entry.CfgDot()
i.Run(entry.Start)
return i.out
}

View File

@@ -1,6 +1,6 @@
package interp
func ExampleRunCfg_1() {
func ExampleEval_1() {
src := `
package main
@@ -8,15 +8,12 @@ func main() {
println(1)
}
`
root := SrcToAst(src)
cfg_entry := root.Child[1].Child[2] // FIXME: entry point should be resolved from 'main' name
AstToCfg(cfg_entry)
RunCfg(cfg_entry.Start)
NewInterpreter().Eval(src)
// Output:
// 1
}
func ExampleRunCfg_2() {
func ExampleEval_2() {
src := `
package main
@@ -30,10 +27,7 @@ func main() {
}
`
root := SrcToAst(src)
cfg_entry := root.Child[1].Child[2] // FIXME: entry point should be resolved from 'main' name
AstToCfg(cfg_entry)
RunCfg(cfg_entry.Start)
NewInterpreter().Eval(src)
// Output:
// 1
// 2048

View File

@@ -2,7 +2,7 @@ package interp
import "fmt"
func (i *Interpreter) RunCfg(entry *Node) {
func (i *Interpreter) Run(entry *Node) {
for n := entry; n != nil; {
n.run(n, i)
if n.snext != nil {
@@ -28,14 +28,6 @@ func assign(n *Node, i *Interpreter) {
n.val = i.sym[name]
}
func cond_branch(n *Node, i *Interpreter) {
if (*n.val).(bool) {
n.snext = n.next[1]
} else {
n.snext = n.next[0]
}
}
func and(n *Node, i *Interpreter) {
for _, child := range n.Child {
if child.ident != "" {