handle return values in functions
This commit is contained in:
@@ -17,12 +17,17 @@ func (e *Node) Cfg(i *Interpreter) int {
|
||||
switch (*n.anode).(type) {
|
||||
case *ast.FuncDecl:
|
||||
symIndex = make(map[string]int)
|
||||
maxIndex = 0
|
||||
// allocate entries for return values at start of frame
|
||||
if len(n.Child[1].Child) == 2 {
|
||||
maxIndex = len(n.Child[1].Child[1].Child)
|
||||
} else {
|
||||
maxIndex = 0
|
||||
}
|
||||
}
|
||||
}, func(n *Node) {
|
||||
switch x := (*n.anode).(type) {
|
||||
case *ast.FuncDecl:
|
||||
n.findex = maxIndex
|
||||
n.findex = maxIndex + 1
|
||||
n.isConst = true
|
||||
i.def[n.Child[0].ident] = n
|
||||
case *ast.BlockStmt:
|
||||
@@ -31,6 +36,9 @@ func (e *Node) Cfg(i *Interpreter) int {
|
||||
n.isNop = true
|
||||
n.run = nop
|
||||
n.findex = n.Child[len(n.Child)-1].findex
|
||||
case *ast.ReturnStmt:
|
||||
wireChild(n)
|
||||
n.run = _return
|
||||
case *ast.IncDecStmt:
|
||||
wireChild(n)
|
||||
switch x.Tok {
|
||||
@@ -57,6 +65,8 @@ func (e *Node) Cfg(i *Interpreter) int {
|
||||
case *ast.BinaryExpr:
|
||||
wireChild(n)
|
||||
switch x.Op {
|
||||
case token.ADD:
|
||||
n.run = add
|
||||
case token.AND:
|
||||
n.run = and
|
||||
case token.EQL:
|
||||
@@ -67,9 +77,20 @@ func (e *Node) Cfg(i *Interpreter) int {
|
||||
maxIndex++
|
||||
n.findex = maxIndex
|
||||
case *ast.Field:
|
||||
n.findex = n.Child[0].findex
|
||||
// A single child node (no ident, just type) means that the field refers
|
||||
// to a return value, and space on frame should be accordingly allocated.
|
||||
// Otherwise, just point to corresponding location in frame, resolved in
|
||||
// ident child.
|
||||
if len(n.Child) == 1 {
|
||||
maxIndex++
|
||||
n.findex = maxIndex
|
||||
} else {
|
||||
n.findex = n.Child[0].findex
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
wireChild(n)
|
||||
// FIXME: should reserve as many entries as nb of ret values for called function
|
||||
// node frame index should point to the first entry
|
||||
n.run = i.call
|
||||
maxIndex++
|
||||
n.findex = maxIndex
|
||||
|
||||
@@ -26,18 +26,18 @@ type Node struct {
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
type Frame []interface{}
|
||||
|
||||
//type Frame struct {
|
||||
// up *Frame // up level in call stack
|
||||
// val []interface{} // array of values
|
||||
//}
|
||||
|
||||
// Interpreter contains global resources and state
|
||||
type Interpreter struct {
|
||||
opt InterpOpt
|
||||
frame *Frame
|
||||
out interface{}
|
||||
def map[string]*Node // map of defined symbols
|
||||
opt InterpOpt
|
||||
out interface{}
|
||||
def map[string]*Node // map of defined symbols
|
||||
}
|
||||
|
||||
type InterpOpt struct {
|
||||
@@ -47,7 +47,7 @@ type InterpOpt struct {
|
||||
|
||||
// n.isLeaf() returns true if Node n is a leaf in the AST
|
||||
func (n *Node) isLeaf() bool {
|
||||
return len((*n).Child) == 0
|
||||
return len(n.Child) == 0
|
||||
}
|
||||
|
||||
// n.Walk(cbin, cbout) traverses AST n in depth first order, call cbin function
|
||||
@@ -86,6 +86,6 @@ func (i *Interpreter) Eval(src string) interface{} {
|
||||
// Execute CFG
|
||||
entry := root.Child[1] // FIXME: entry point should be resolved from 'main' name
|
||||
//entry.OptimCfg()
|
||||
Run(entry, nil, nil)
|
||||
Run(entry, nil, nil, nil)
|
||||
return i.out
|
||||
}
|
||||
|
||||
@@ -3,31 +3,31 @@ package interp
|
||||
import "fmt"
|
||||
|
||||
// Run a Go function
|
||||
func Run(def *Node, uf *Frame, args []*Node) {
|
||||
func Run(def *Node, cf *Frame, args []*Node, rets []int) {
|
||||
// Allocate a new Frame to store local variables
|
||||
f := Frame(make([]interface{}, def.findex))
|
||||
|
||||
// Pass func parameters by value: copy each parameter from caller frame
|
||||
param := def.Child[1].Child[0].Child
|
||||
for i, arg := range args {
|
||||
f[param[i].findex] = value(arg, cf)
|
||||
}
|
||||
|
||||
// Execute by walking the CFG and running node func at each step
|
||||
body := def.Child[2]
|
||||
param := def.Child[1].Child[0]
|
||||
|
||||
// Init new Frame
|
||||
f := &Frame{val: make([]interface{}, def.findex), up: uf}
|
||||
if uf != nil {
|
||||
uf.down = f
|
||||
}
|
||||
|
||||
// Pass func parameters as value: copy each func parameter from old to new frame
|
||||
// Frame locations were pre-computed during cfg
|
||||
for i := 0; i < len(args); i++ {
|
||||
f.val[param.Child[i].findex] = uf.val[args[i].findex]
|
||||
}
|
||||
|
||||
// Start execution by runnning entry function and go to next
|
||||
for n := body.Start; n != nil; {
|
||||
n.run(n, f)
|
||||
if n.fnext == nil || value(n, f).(bool) {
|
||||
n.run(n, &f)
|
||||
if n.fnext == nil || value(n, &f).(bool) {
|
||||
n = n.tnext
|
||||
} else {
|
||||
n = n.fnext
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate return values to caller frame
|
||||
for i, ret := range rets {
|
||||
(*cf)[ret] = f[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Functions set to run during execution of CFG
|
||||
@@ -36,16 +36,16 @@ func value(n *Node, f *Frame) interface{} {
|
||||
if n.isConst {
|
||||
return n.val
|
||||
}
|
||||
return f.val[n.findex]
|
||||
return (*f)[n.findex]
|
||||
|
||||
}
|
||||
|
||||
func assign(n *Node, f *Frame) {
|
||||
f.val[n.findex] = value(n.Child[1], f)
|
||||
(*f)[n.findex] = value(n.Child[1], f)
|
||||
}
|
||||
|
||||
func and(n *Node, f *Frame) {
|
||||
f.val[n.findex] = value(n.Child[0], f).(int64) & value(n.Child[1], f).(int64)
|
||||
(*f)[n.findex] = value(n.Child[0], f).(int64) & value(n.Child[1], f).(int64)
|
||||
}
|
||||
|
||||
func printa(n []*Node, f *Frame) {
|
||||
@@ -55,29 +55,48 @@ func printa(n []*Node, f *Frame) {
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func (i *Interpreter) call(n *Node, f *Frame) {
|
||||
func (interp *Interpreter) call(n *Node, f *Frame) {
|
||||
if n.Child[0].ident == "println" {
|
||||
printa(n.Child[1:], f)
|
||||
return
|
||||
}
|
||||
// FIXME: resolve fn during compile, not exec ?
|
||||
if fn := i.def[n.Child[0].ident]; fn != nil {
|
||||
Run(fn, f, n.Child[1:])
|
||||
if fn := interp.def[n.Child[0].ident]; fn != nil {
|
||||
var rets []int
|
||||
if len(fn.Child[1].Child) > 1 {
|
||||
if fieldList := fn.Child[1].Child[1]; fieldList != nil {
|
||||
rets = make([]int, len(fieldList.Child))
|
||||
for i, _ := range fieldList.Child {
|
||||
rets[i] = n.findex + i
|
||||
}
|
||||
}
|
||||
}
|
||||
Run(fn, f, n.Child[1:], rets)
|
||||
} else {
|
||||
panic("function not found")
|
||||
}
|
||||
}
|
||||
|
||||
func add(n *Node, f *Frame) {
|
||||
(*f)[n.findex] = value(n.Child[0], f).(int64) + value(n.Child[1], f).(int64)
|
||||
}
|
||||
|
||||
func equal(n *Node, f *Frame) {
|
||||
f.val[n.findex] = value(n.Child[0], f).(int64) == value(n.Child[1], f).(int64)
|
||||
(*f)[n.findex] = value(n.Child[0], f).(int64) == value(n.Child[1], f).(int64)
|
||||
}
|
||||
|
||||
func inc(n *Node, f *Frame) {
|
||||
f.val[n.findex] = value(n.Child[0], f).(int64) + 1
|
||||
(*f)[n.findex] = value(n.Child[0], f).(int64) + 1
|
||||
}
|
||||
|
||||
func lower(n *Node, f *Frame) {
|
||||
f.val[n.findex] = value(n.Child[0], f).(int64) < value(n.Child[1], f).(int64)
|
||||
(*f)[n.findex] = value(n.Child[0], f).(int64) < value(n.Child[1], f).(int64)
|
||||
}
|
||||
|
||||
func nop(n *Node, i *Frame) {}
|
||||
|
||||
func _return(n *Node, f *Frame) {
|
||||
for i, c := range n.Child {
|
||||
(*f)[i] = (*f)[c.findex]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user