fix: correct handling of assign from multiple return func in REPL (#233)

This commit is contained in:
Marc Vertes
2019-07-01 03:44:13 +02:00
committed by Ludovic Fernandez
parent 274c0fc47a
commit c991e09ca6
4 changed files with 105 additions and 67 deletions

24
_test/eval0.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"log"
"github.com/containous/yaegi/interp"
)
func main() {
log.SetFlags(log.Lshortfile)
i := interp.New()
if _, err := i.Eval(`func f() (int, int) { return 1, 2 }`); err != nil {
log.Fatal(err)
}
if _, err := i.Eval(`a, b := f()`); err != nil {
log.Fatal(err)
}
if _, err := i.Eval(`println(a, b)`); err != nil {
log.Fatal(err)
}
}
// Output:
// 1 2

View File

@@ -470,47 +470,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case defineXStmt:
wireChild(n)
l := len(n.child) - 1
var types []*itype
switch n.child[l].kind {
case callExpr:
if funtype := n.child[l].child[0].typ; funtype.cat == valueT {
// Handle functions imported from runtime
for i := 0; i < funtype.rtype.NumOut(); i++ {
types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)})
}
} else {
types = funtype.ret
}
n.gen = nop
case indexExpr:
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
n.child[l].gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.child[l].gen = typeAssert2
n.gen = nop
case unaryExpr:
if n.child[l].action == aRecv {
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
n.child[l].gen = recv2
n.gen = nop
}
default:
err = n.cfgErrorf("unsupported assign expression")
return
}
for i, t := range types {
index := sc.add(t)
sc.sym[n.child[i].ident] = &symbol{index: index, kind: varSym, typ: t}
n.child[i].typ = t
n.child[i].findex = index
if sc.def == nil {
// in global scope, type definition already handled by GTA
break
}
err = compDefineX(sc, n)
case binaryExpr:
wireChild(n)
@@ -1367,6 +1331,57 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
return initNodes, err
}
func compDefineX(sc *scope, n *node) error {
l := len(n.child) - 1
types := []*itype{}
switch n.child[l].kind {
case callExpr:
funtype, err := nodeType(n.interp, sc, n.child[l].child[0])
if err != nil {
return err
}
if funtype.cat == valueT {
// Handle functions imported from runtime
for i := 0; i < funtype.rtype.NumOut(); i++ {
types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)})
}
} else {
types = funtype.ret
}
n.gen = nop
case indexExpr:
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
n.child[l].gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.child[l].gen = typeAssert2
n.gen = nop
case unaryExpr:
if n.child[l].action == aRecv {
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
n.child[l].gen = recv2
n.gen = nop
}
default:
return n.cfgErrorf("unsupported assign expression")
}
for i, t := range types {
index := sc.add(t)
sc.sym[n.child[i].ident] = &symbol{index: index, kind: varSym, typ: t}
n.child[i].typ = t
n.child[i].findex = index
}
return nil
}
// TODO used for allocation optimization, temporarily disabled
//func isAncBranch(n *node) bool {
// switch n.anc.kind {

View File

@@ -10,7 +10,7 @@ import (
// 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, rpath string) error {
scope, _ := interp.initScopePkg(root)
sc, _ := interp.initScopePkg(root)
var err error
var iotaValue int
@@ -30,7 +30,7 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
case defineStmt:
var atyp *itype
if n.nleft+n.nright < len(n.child) {
if atyp, err = nodeType(interp, scope, n.child[n.nleft]); err != nil {
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
return false
}
}
@@ -45,7 +45,7 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
typ := atyp
val := reflect.ValueOf(iotaValue)
if typ == nil {
if typ, err = nodeType(interp, scope, src); err != nil {
if typ, err = nodeType(interp, sc, src); err != nil {
return false
}
val = src.rval
@@ -56,9 +56,9 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
err = n.cfgErrorf("use of untyped nil")
return false
}
index = scope.add(typ)
index = sc.add(typ)
}
scope.sym[dest.ident] = &symbol{kind: varSym, global: true, index: index, typ: typ, rval: val}
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: index, typ: typ, rval: val}
if n.anc.kind == constDecl {
iotaValue++
}
@@ -66,19 +66,18 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
return false
case defineXStmt:
// TODO: handle global DefineX
//err = n.cfgError("global DefineX not implemented")
err = compDefineX(sc, n)
case valueSpec:
// TODO: handle global ValueSpec
//err = n.cfgError("global ValueSpec not implemented")
case funcDecl:
if n.typ, err = nodeType(interp, scope, n.child[2]); err != nil {
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
return false
}
if !isMethod(n) {
scope.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if len(n.child[0].child) > 0 {
// function is a method, add it to the related type
@@ -95,20 +94,20 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
if typeName == "" {
// The receiver is a pointer, retrieve typeName from indirection
typeName = rcvr.lastChild().child[0].ident
elementType := scope.getType(typeName)
elementType := sc.getType(typeName)
if elementType == nil {
// Add type if necessary, so method can be registered
scope.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
elementType = scope.sym[typeName].typ
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
elementType = sc.sym[typeName].typ
}
rcvrtype = &itype{cat: ptrT, val: elementType}
elementType.method = append(elementType.method, n)
} else {
rcvrtype = scope.getType(typeName)
rcvrtype = sc.getType(typeName)
if rcvrtype == nil {
// Add type if necessary, so method can be registered
scope.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
rcvrtype = scope.sym[typeName].typ
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
rcvrtype = sc.sym[typeName].typ
}
}
rcvrtype.method = append(rcvrtype.method, n)
@@ -131,22 +130,22 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
if isBinType(v) {
typ = typ.Elem()
}
scope.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
}
} else {
scope.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT}, path: ipath}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT}, path: ipath}
}
} else {
// TODO: make sure we do not import a src package more than once
err = interp.importSrcFile(rpath, ipath, name)
scope.types = interp.universe.types
scope.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
sc.types = interp.universe.types
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT}, path: ipath}
}
case typeSpec:
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, scope, n.child[1]); err != nil {
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
return false
}
if n.child[1].kind == identExpr {
@@ -157,19 +156,19 @@ func (interp *Interpreter) gta(root *node, rpath string) error {
n.typ.pkgPath = rpath
}
// Type may already be declared for a receiver in a method function
if scope.sym[typeName] == nil {
scope.sym[typeName] = &symbol{kind: typeSym}
if sc.sym[typeName] == nil {
sc.sym[typeName] = &symbol{kind: typeSym}
} else {
n.typ.method = append(n.typ.method, scope.sym[typeName].typ.method...)
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
}
scope.sym[typeName].typ = n.typ
sc.sym[typeName].typ = n.typ
return false
}
return true
}, nil)
if scope != interp.universe {
scope.pop()
if sc != interp.universe {
sc.pop()
}
return err
}

View File

@@ -66,8 +66,8 @@ type opt struct {
// Interpreter contains global resources and state
type Interpreter struct {
Name string // program name
opt
Name string // program name
frame *frame // program data storage during execution
nindex int // next node index
fset *token.FileSet // fileset to locate node in source code