handle return values in functions

This commit is contained in:
Marc Vertes
2018-02-10 21:10:34 +01:00
parent b5ab905e2d
commit bef44db73c
4 changed files with 84 additions and 41 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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]
}
}

3
l4.gi Normal file
View File

@@ -0,0 +1,3 @@
package main
func main() { println(f(5)) }
func f(i int) int { return i + 1 }