fix: correct handling of assign from multiple return func in REPL (#233)
This commit is contained in:
committed by
Ludovic Fernandez
parent
274c0fc47a
commit
c991e09ca6
24
_test/eval0.go
Normal file
24
_test/eval0.go
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user