130 lines
2.9 KiB
Go
130 lines
2.9 KiB
Go
package interp
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
"reflect"
|
|
"strconv"
|
|
)
|
|
|
|
// TODO: remove coupling with go/ast and go/token. This should be handled only in Ast()
|
|
|
|
// 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.run = nop
|
|
n.val = n.Child[len(n.Child)-1].val
|
|
case *ast.IncDecStmt:
|
|
wire_child(n)
|
|
switch x.Tok {
|
|
case token.INC:
|
|
n.run = inc
|
|
}
|
|
case *ast.AssignStmt:
|
|
n.run = assign
|
|
wire_child(n)
|
|
case *ast.ExprStmt:
|
|
wire_child(n)
|
|
// FIXME: could bypass this node at CFG and wire directly last child
|
|
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.run = nop
|
|
n.val = n.Child[len(n.Child)-1].val
|
|
case *ast.BinaryExpr:
|
|
wire_child(n)
|
|
switch x.Op {
|
|
case token.AND:
|
|
n.run = and
|
|
case token.EQL:
|
|
n.run = equal
|
|
case token.LSS:
|
|
n.run = lower
|
|
}
|
|
case *ast.CallExpr:
|
|
wire_child(n)
|
|
n.run = call
|
|
case *ast.IfStmt:
|
|
n.isNop = true
|
|
n.run = nop
|
|
n.Start = n.Child[0].Start
|
|
n.Child[1].snext = n
|
|
if len(n.Child) == 3 {
|
|
n.Child[2].snext = n
|
|
}
|
|
n.Child[0].next[1] = n.Child[1].Start
|
|
if len(n.Child) == 3 {
|
|
n.Child[0].next[0] = n.Child[2].Start
|
|
} else {
|
|
n.Child[0].next[0] = n
|
|
}
|
|
case *ast.ForStmt:
|
|
n.isNop = true
|
|
n.run = nop
|
|
// FIXME: works only if for node has 4 children
|
|
n.Start = n.Child[0].Start
|
|
n.Child[0].snext = n.Child[1].Start
|
|
n.Child[1].next[0] = n
|
|
n.Child[1].next[1] = n.Child[3].Start
|
|
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
|
|
} else {
|
|
*n.val = x.Value
|
|
}
|
|
case *ast.Ident:
|
|
n.ident = x.Name
|
|
default:
|
|
println("unknown type:", reflect.TypeOf(*n.anode).String())
|
|
}
|
|
})
|
|
}
|
|
|
|
// Wire AST nodes of sequential blocks
|
|
func wire_child(n *Node) {
|
|
for _, child := range n.Child {
|
|
if !child.isLeaf() {
|
|
n.Start = child.Start
|
|
break
|
|
}
|
|
}
|
|
if n.Start == nil {
|
|
n.Start = n
|
|
}
|
|
for i := 1; i < len(n.Child); i++ {
|
|
n.Child[i-1].snext = n.Child[i].Start
|
|
}
|
|
for i := len(n.Child) - 1; i >= 0; i-- {
|
|
if !n.Child[i].isLeaf() {
|
|
n.Child[i].snext = n
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// optimisation: rewire CFG to skip nop nodes
|
|
func (e *Node) OptimCfg() {
|
|
e.Walk(nil, func(n *Node) {
|
|
for s := n.snext; s != nil && s.snext != nil; s = s.snext {
|
|
n.snext = s
|
|
}
|
|
for s := n.next[0]; s != nil && s.snext != nil; s = s.snext {
|
|
n.next[0] = s
|
|
}
|
|
})
|
|
}
|