fix: correct handling of multi-assign operations, including swap (#173)

This commit is contained in:
Marc Vertes
2019-05-01 16:25:31 +02:00
committed by Ludovic Fernandez
parent 0bbdd37e55
commit 16690838e3
14 changed files with 397 additions and 250 deletions

12
_test/assign3.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
a, b := 1, 2
println(a, b)
a, b = b, a
println(a, b)
}
// Output:
// 1 2
// 2 1

12
_test/assign4.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
a, b, c := 1, 2, 3
println(a, b, c)
a, b, c = c, a, b
println(a, b, c)
}
// Output:
// 1 2 3
// 3 1 2

14
_test/assign5.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
t := []int{1, 2}
fmt.Println(t)
t[0], t[1] = t[1], t[0]
fmt.Println(t)
}
// Output:
// [1 2]
// [2 1]

14
_test/assign6.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
t := map[string]int{"a": 1, "b": 2}
fmt.Println(t["a"], t["b"])
t["a"], t["b"] = t["b"], t["a"]
fmt.Println(t["a"], t["b"])
}
// Output:
// 1 2
// 2 1

16
_test/assign7.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "fmt"
func main() {
a := 3
t := map[string]int{"a": 1, "b": 2}
s := []int{4, 5}
fmt.Println(a, t["b"], s)
a, t["b"], s[1] = t["b"], s[1], a
fmt.Println(a, t["b"], s)
}
// Output:
// 3 2 [4 5]
// 2 5 [4 3]

14
_test/const4.go Normal file
View File

@@ -0,0 +1,14 @@
package main
const (
a = 2
b = 3
c
)
func main() {
println(a, b, c)
}
// Output:
// 2 3 3

20
_test/const5.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
)
const (
a uint8 = 2 * iota
b
c
)
func main() {
fmt.Printf("%T\n", c)
fmt.Println(a, b, c)
}
// Output:
// uint8
// 0 2 4

View File

@@ -343,8 +343,6 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
var root *Node
var anc astNode
var st nodestack
var nbAssign int // number of assign operations in the current assign statement
var typeSpec bool
var pkgName string
addChild := func(root **Node, anc astNode, pos token.Pos, kind Kind, action Action) *Node {
@@ -356,56 +354,7 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
*root = n
} else {
anc.node.child = append(anc.node.child, n)
if anc.node.action == Assign {
if nbAssign > 1 {
if !typeSpec && len(anc.node.child) == 2*nbAssign {
// All LHS and RSH assign child are now defined, so split multiple assign
// statement into single assign statements.
newAnc := anc.node.anc
var newChild []*Node
for i := 0; i < nbAssign; i++ {
// set new single assign
interp.nindex++
na := &Node{anc: anc.node.anc, interp: interp, index: interp.nindex, pos: pos, kind: anc.node.kind, action: anc.node.action, val: new(interface{}), gen: anc.node.gen}
na.start = na
newChild = append(newChild, na)
// Set single assign left hand side
anc.node.child[i].anc = na
na.child = append(na.child, anc.node.child[i])
// Set single assign right hand side
anc.node.child[i+nbAssign].anc = na
na.child = append(na.child, anc.node.child[i+nbAssign])
}
newAnc.child = newChild
} else if typeSpec && len(anc.node.child) == 2*nbAssign+1 {
// All LHS and RHS assign child are now defined, so split multiple assign
// statement into single assign statements. Set type for each assignment.
typeSpec = false
newAnc := anc.node.anc
var newChild []*Node
typeNode := anc.node.child[nbAssign]
for i := 0; i < nbAssign; i++ {
// set new single assign
interp.nindex++
na := &Node{anc: anc.node.anc, interp: interp, index: interp.nindex, pos: pos, kind: anc.node.kind, action: anc.node.action, val: new(interface{}), gen: anc.node.gen}
na.start = na
newChild = append(newChild, na)
// set new type for this assignment
interp.nindex++
nt := &Node{anc: na, interp: interp, ident: typeNode.ident, index: interp.nindex, pos: pos, kind: typeNode.kind, action: typeNode.action, val: new(interface{}), gen: typeNode.gen}
// Set single assign left hand side
anc.node.child[i].anc = na
na.child = append(na.child, anc.node.child[i])
// Set assignment type
na.child = append(na.child, nt)
// Set single assign right hand side
anc.node.child[i+nbAssign+1].anc = na
na.child = append(na.child, anc.node.child[i+nbAssign+1])
}
newAnc.child = newChild
}
}
} else if anc.node.action == Case {
if anc.node.action == Case {
ancAst := anc.ast.(*ast.CaseClause)
if len(ancAst.List)+len(ancAst.Body) == len(anc.node.child) {
// All case clause children are collected.
@@ -460,7 +409,6 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
}
action = AssignX
} else {
nbAssign = len(a.Lhs)
kind = AssignStmt
switch a.Tok {
case token.ASSIGN:
@@ -492,14 +440,17 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
action = XorAssign
}
}
st.push(addChild(&root, anc, pos, kind, action), node)
n := addChild(&root, anc, pos, kind, action)
n.nleft = len(a.Lhs)
n.nright = len(a.Rhs)
st.push(n, node)
case *ast.BasicLit:
n := addChild(&root, anc, pos, BasicLit, Nop)
n.ident = a.Value
switch a.Kind {
case token.CHAR:
n.val = rune(a.Value[1])
n.val, _, _, _ = strconv.UnquoteChar(a.Value[1:len(a.Value)-1], '\'')
case token.FLOAT:
n.val, _ = strconv.ParseFloat(a.Value, 64)
case token.IMAG:
@@ -509,7 +460,7 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
v, _ := strconv.ParseInt(a.Value, 0, 0)
n.val = int(v)
case token.STRING:
n.val = a.Value[1 : len(a.Value)-1]
n.val, _ = strconv.Unquote(a.Value)
}
st.push(n, node)
@@ -675,6 +626,21 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
n := addChild(&root, anc, pos, Ident, Nop)
n.ident = a.Name
st.push(n, node)
if n.anc.kind == Define && n.anc.nright == 0 {
// Implicit assign expression (in a ConstDecl block).
// Clone assign source and type from previous
a := n.anc
pa := a.anc.child[childPos(a)-1]
if len(pa.child) > pa.nleft+pa.nright {
// duplicate previous type spec
a.child = append(a.child, interp.dup(pa.child[a.nleft], a))
}
// duplicate previous assign right hand side
a.child = append(a.child, interp.dup(pa.lastChild(), a))
a.nright++
}
case *ast.IfStmt:
// Disambiguate variants of IF statements with a node kind per variant
@@ -807,15 +773,14 @@ func (interp *Interpreter) ast(src, name string) (string, *Node, error) {
kind = AssignStmt
}
action = Assign
nbAssign = len(a.Names)
}
if a.Type != nil {
typeSpec = true
}
} else if anc.node.kind == ConstDecl {
kind, action = Define, Assign
}
st.push(addChild(&root, anc, pos, kind, action), node)
n := addChild(&root, anc, pos, kind, action)
n.nleft = len(a.Names)
n.nright = len(a.Values)
st.push(n, node)
default:
err = AstError(fmt.Errorf("ast: %T not implemented, line %s", a, interp.fset.Position(pos)))
@@ -857,3 +822,18 @@ func (s *nodestack) top() astNode {
}
return astNode{}
}
// dup returns a duplicated node subtree
func (interp *Interpreter) dup(node, anc *Node) *Node {
interp.nindex++
n := *node
n.index = interp.nindex
n.anc = anc
n.start = &n
n.pos = anc.pos
n.child = nil
for _, c := range node.child {
n.child = append(n.child, interp.dup(c, &n))
}
return &n
}

View File

@@ -27,12 +27,6 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
return false
}
switch n.kind {
case AssignStmt, Define:
if l := len(n.child); n.anc.kind == ConstDecl && l == 1 {
// Implicit iota assignment. TODO: replicate previous explicit assignment
n.child = append(n.child, &Node{anc: n, interp: interp, kind: Ident, ident: "iota"})
}
case BlockStmt:
if n.anc != nil && n.anc.kind == RangeStmt {
// For range block: ensure that array or map type is propagated to iterators
@@ -291,103 +285,110 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.gen = nop
break
}
dest, src := n.child[0], n.lastChild()
var sym *Symbol
var level int
if n.kind == Define {
if src.typ.cat == NilT {
err = src.cfgError("use of untyped nil")
break
}
if len(n.child) == 3 {
// type is provided in var declaration
dest.typ, err = nodeType(interp, scope, n.child[1])
} else {
dest.typ = src.typ
}
if scope.global {
// Do not overload existings symbols (defined in GTA) in global scope
sym, _, _ = scope.lookup(dest.ident)
} else {
sym = &Symbol{index: scope.add(dest.typ), kind: Var}
scope.sym[dest.ident] = sym
}
dest.val = src.val
dest.recv = src.recv
dest.findex = sym.index
if src.kind == BasicLit {
sym.val = src.val
}
} else {
sym, level, _ = scope.lookup(dest.ident)
var atyp *Type
if n.nleft+n.nright < len(n.child) {
atyp, err = nodeType(interp, scope, n.child[n.nleft])
}
var sbase int
if n.nright > 0 {
sbase = len(n.child) - n.nright
}
wireChild(n)
switch t0, t1 := dest.typ, src.typ; n.action {
case AddAssign:
if !(isNumber(t0) && isNumber(t1) || isString(t0) && isString(t1)) || isInt(t0) && isFloat(t1) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
var sym *Symbol
var level int
if n.kind == Define {
if src.typ != nil && src.typ.cat == NilT {
err = src.cfgError("use of untyped nil")
break
}
if atyp != nil {
dest.typ = atyp
} else {
dest.typ = src.typ
}
if scope.global {
// Do not overload existings symbols (defined in GTA) in global scope
sym, _, _ = scope.lookup(dest.ident)
} else {
sym = &Symbol{index: scope.add(dest.typ), kind: Var}
scope.sym[dest.ident] = sym
}
dest.val = src.val
dest.recv = src.recv
dest.findex = sym.index
if src.kind == BasicLit {
sym.val = src.val
}
} else {
sym, level, _ = scope.lookup(dest.ident)
}
case SubAssign, MulAssign, QuoAssign:
if !(isNumber(t0) && isNumber(t1)) || isInt(t0) && isFloat(t1) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
case RemAssign, AndAssign, OrAssign, XorAssign, AndNotAssign:
if !(isInt(t0) && isInt(t1)) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
case ShlAssign, ShrAssign:
if !(isInt(t0) && isUint(t1)) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
default:
// Detect invalid float truncate
if isInt(t0) && isFloat(t1) {
err = src.cfgError("invalid float truncate")
return
}
}
n.findex = dest.findex
n.val = dest.val
n.rval = dest.rval
// Propagate type
// TODO: Check that existing destination type matches source type
switch {
case src.action == Call:
n.gen = nop
src.level = level
src.findex = dest.findex
case src.action == Recv:
// Assign by reading from a receiving channel
n.gen = nop
src.findex = dest.findex // Set recv address to LHS
dest.typ = src.typ.val
case src.action == CompositeLit:
n.gen = nop
src.findex = dest.findex
src.level = level
case src.kind == BasicLit:
// TODO: perform constant folding and propagation here
switch {
case dest.typ.cat == InterfaceT:
src.val = reflect.ValueOf(src.val)
case src.val == nil:
// Assign to nil
switch t0, t1 := dest.typ, src.typ; n.action {
case AddAssign:
if !(isNumber(t0) && isNumber(t1) || isString(t0) && isString(t1)) || isInt(t0) && isFloat(t1) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
case SubAssign, MulAssign, QuoAssign:
if !(isNumber(t0) && isNumber(t1)) || isInt(t0) && isFloat(t1) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
case RemAssign, AndAssign, OrAssign, XorAssign, AndNotAssign:
if !(isInt(t0) && isInt(t1)) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
case ShlAssign, ShrAssign:
if !(isInt(t0) && isUint(t1)) {
err = n.cfgError("illegal operand types for '%v' operator", n.action)
}
default:
// Convert literal value to destination type
src.val = reflect.ValueOf(src.val).Convert(dest.typ.TypeOf())
src.typ = dest.typ
// Detect invalid float truncate
if isInt(t0) && isFloat(t1) {
err = src.cfgError("invalid float truncate")
return
}
}
}
n.typ = dest.typ
if sym != nil {
sym.typ = n.typ
sym.recv = src.recv
}
n.level = level
// If LHS is an indirection, get reference instead of value, to allow setting
if dest.action == GetIndex {
if dest.child[0].typ.cat == MapT {
n.gen = assignMap
n.findex = dest.findex
n.val = dest.val
n.rval = dest.rval
// Propagate type
// TODO: Check that existing destination type matches source type
switch {
case src.action == Call:
n.gen = nop
src.level = level
src.findex = dest.findex
case src.action == Recv:
// Assign by reading from a receiving channel
n.gen = nop
src.findex = dest.findex // Set recv address to LHS
dest.typ = src.typ.val
case src.action == CompositeLit:
n.gen = nop
src.findex = dest.findex
src.level = level
case src.kind == BasicLit:
// TODO: perform constant folding and propagation here
switch {
case dest.typ.cat == InterfaceT:
src.val = reflect.ValueOf(src.val)
case src.val == nil:
// Assign to nil
default:
// Convert literal value to destination type
src.val = reflect.ValueOf(src.val).Convert(dest.typ.TypeOf())
src.typ = dest.typ
}
}
n.typ = dest.typ
if sym != nil {
sym.typ = n.typ
sym.recv = src.recv
}
n.level = level
if isMapEntry(dest) {
dest.gen = nop // skip getIndexMap
}
}
@@ -1224,6 +1225,15 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
return initNodes, err
}
func childPos(n *Node) int {
for i, c := range n.anc.child {
if n == c {
return i
}
}
return -1
}
func (n *Node) cfgError(format string, a ...interface{}) CfgError {
a = append([]interface{}{n.interp.fset.Position(n.pos)}, a...)
return CfgError(fmt.Errorf("%s: "+format, a...))
@@ -1346,10 +1356,7 @@ func isNewDefine(n *Node, scope *Scope) bool {
if n.ident == "_" {
return true
}
if n.anc.kind == Define && n.anc.child[0] == n {
return true
}
if n.anc.kind == DefineX && n.anc.lastChild() != n {
if (n.anc.kind == DefineX || n.anc.kind == Define || n.anc.kind == ValueSpec) && childPos(n) < n.anc.nleft {
return true
}
if n.anc.kind == RangeStmt {
@@ -1361,12 +1368,13 @@ func isNewDefine(n *Node, scope *Scope) bool {
}
return false // array, map or channel are always pre-defined in range expression
}
if n.anc.kind == ValueSpec && n.anc.lastChild() != n {
return true
}
return false
}
func isMapEntry(n *Node) bool {
return n.action == GetIndex && n.child[0].typ.cat == MapT
}
func isBuiltinCall(n *Node) bool {
return n.kind == CallExpr && n.child[0].sym != nil && n.child[0].sym.kind == Bltn
}

View File

@@ -27,26 +27,38 @@ func (interp *Interpreter) Gta(root *Node, rpath string) error {
}
case Define:
var typ *Type
if len(n.child) > 1 {
typ, err = nodeType(interp, scope, n.child[1])
} else {
typ, err = nodeType(interp, scope, n.anc.child[0].child[1])
var atyp *Type
if n.nleft+n.nright < len(n.child) {
atyp, err = nodeType(interp, scope, n.child[n.nleft])
if err != nil {
return false
}
}
if err != nil {
return false
var sbase int
if n.nright > 0 {
sbase = len(n.child) - n.nright
}
if typ.cat == NilT {
err = n.cfgError("use of untyped nil")
return false
}
var val interface{} = iotaValue
if len(n.child) > 1 {
val = n.child[1].val
}
scope.sym[n.child[0].ident] = &Symbol{kind: Var, global: true, index: scope.add(typ), typ: typ, val: val}
if n.anc.kind == ConstDecl {
iotaValue++
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
typ := atyp
var val interface{} = iotaValue
if typ == nil {
typ, err = nodeType(interp, scope, src)
if err != nil {
return false
}
val = src.val
}
if typ.cat == NilT {
err = n.cfgError("use of untyped nil")
return false
}
scope.sym[dest.ident] = &Symbol{kind: Var, global: true, index: scope.add(typ), typ: typ, val: val}
if n.anc.kind == ConstDecl {
iotaValue++
}
}
return false

View File

@@ -22,6 +22,8 @@ type Node struct {
index int // node index (dot display)
findex int // index of value in frame or frame size (func def, type def)
level int // number of frame indirections to access value
nleft int // number of children in left part (assign)
nright int // number of children in right part (assign)
kind Kind // kind of node
pos token.Pos // position in source code, relative to fset
sym *Symbol // associated symbol
@@ -226,6 +228,9 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
if i.astDot {
root.AstDot(DotX(), i.Name)
if i.noRun {
return res, err
}
}
// Global type analysis
@@ -257,21 +262,23 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
root.CfgDot(DotX())
}
// Execute CFG
if !i.noRun {
if err = genRun(root); err != nil {
return res, err
}
i.resizeFrame()
i.run(root, nil)
for _, n := range initNodes {
i.run(n, i.Frame)
}
v := genValue(root)
res = v(i.Frame)
if i.noRun {
return res, err
}
// Execute CFG
if err = genRun(root); err != nil {
return res, err
}
i.resizeFrame()
i.run(root, nil)
for _, n := range initNodes {
i.run(n, i.Frame)
}
v := genValue(root)
res = v(i.Frame)
// If result is an interpreter node, wrap it in a runtime callable function
if res.IsValid() {
if n, ok := res.Interface().(*Node); ok {

View File

@@ -196,51 +196,87 @@ func convert(n *Node) {
}
}
// assign implements single value assignment
func assign(n *Node) {
next := getExec(n.tnext)
value := genValue(n)
dest, src := n.child[0], n.lastChild()
var value1 func(*Frame) reflect.Value
switch {
case dest.typ.cat == InterfaceT:
value1 = genValueInterface(src)
case dest.typ.cat == ValueT && src.typ.cat == FuncT:
value1 = genNodeWrapper(src)
case src.kind == BasicLit && src.val == nil:
t := dest.typ.TypeOf()
value1 = func(*Frame) reflect.Value { return reflect.New(t).Elem() }
default:
value1 = genValue(src)
dvalue := make([]func(*Frame) reflect.Value, n.nleft)
ivalue := make([]func(*Frame) reflect.Value, n.nleft)
svalue := make([]func(*Frame) reflect.Value, n.nleft)
var sbase int
if n.nright > 0 {
sbase = len(n.child) - n.nright
}
n.exec = func(f *Frame) Builtin {
value(f).Set(value1(f))
return next
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
switch {
case dest.typ.cat == InterfaceT:
svalue[i] = genValueInterface(src)
case dest.typ.cat == ValueT && src.typ.cat == FuncT:
svalue[i] = genNodeWrapper(src)
case src.kind == BasicLit && src.val == nil:
t := dest.typ.TypeOf()
svalue[i] = func(*Frame) reflect.Value { return reflect.New(t).Elem() }
default:
svalue[i] = genValue(src)
}
if isMapEntry(dest) {
if dest.child[1].typ.cat == InterfaceT { // key
ivalue[i] = genValueInterface(dest.child[1])
} else {
ivalue[i] = genValue(dest.child[1])
}
dvalue[i] = genValue(dest.child[0])
} else {
dvalue[i] = genValue(dest)
}
}
}
func assignMap(n *Node) {
value := genValue(n.child[0].child[0]) // map
var value0, value1 func(*Frame) reflect.Value
if n.child[0].child[1].typ.cat == InterfaceT { // key
value0 = genValueInterface(n.child[0].child[1])
if n.nleft == 1 {
if s, d, i := svalue[0], dvalue[0], ivalue[0]; i != nil {
n.exec = func(f *Frame) Builtin {
d(f).SetMapIndex(i(f), s(f))
return next
}
} else {
n.exec = func(f *Frame) Builtin {
d(f).Set(s(f))
return next
}
}
} else {
value0 = genValue(n.child[0].child[1])
}
types := make([]reflect.Type, n.nright)
for i := range types {
var t reflect.Type
switch typ := n.child[sbase+i].typ; typ.cat {
case FuncT:
t = reflect.TypeOf((*Node)(nil))
case InterfaceT:
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
default:
t = typ.TypeOf()
}
//types[i] = n.child[sbase+i].typ.TypeOf()
types[i] = t
}
if n.child[1].typ.cat == InterfaceT { // value
value1 = genValueInterface(n.child[1])
} else {
value1 = genValue(n.child[1])
}
next := getExec(n.tnext)
n.exec = func(f *Frame) Builtin {
value(f).SetMapIndex(value0(f), value1(f))
return next
// To handle swap in multi-assign:
// evaluate and copy all values in assign right hand side into temporary
// then evaluate assign left hand side and copy temporary into it
n.exec = func(f *Frame) Builtin {
t := make([]reflect.Value, len(svalue))
for i, s := range svalue {
t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f))
}
for i, d := range dvalue {
if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else {
d(f).Set(t[i]) // Assign a var or array/slice entry
}
}
return next
}
}
}
@@ -513,7 +549,7 @@ func call(n *Node) {
}
rvalues := make([]func(*Frame) reflect.Value, len(n.child[0].typ.ret))
switch n.anc.kind {
case Define, AssignStmt, DefineX, AssignXStmt:
case DefineX, AssignXStmt:
for i := range rvalues {
c := n.anc.child[i]
if c.ident != "_" {
@@ -801,7 +837,7 @@ func getIndexMap2(n *Node) {
}
func getFunc(n *Node) {
i := n.findex
dest := genValue(n)
next := getExec(n.tnext)
n.exec = func(f *Frame) Builtin {
@@ -809,7 +845,7 @@ func getFunc(n *Node) {
node := *n
node.val = &node
node.frame = &frame
f.data[i].Set(reflect.ValueOf(&node))
dest(f).Set(reflect.ValueOf(&node))
return next
}
}

View File

@@ -196,22 +196,26 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) {
t.name = "string"
t.untyped = true
default:
err = n.cfgError("missign support for type %T", n.val)
err = n.cfgError("missing support for type %T: %v", v, n.val)
}
case UnaryExpr:
t, err = nodeType(interp, scope, n.child[0])
case BinaryExpr:
t, err = nodeType(interp, scope, n.child[0])
if err != nil {
return nil, err
}
if t.untyped {
var t1 *Type
t1, err = nodeType(interp, scope, n.child[1])
if !(t1.untyped && isInt(t1) && isFloat(t)) {
t = t1
if a := n.anc; a.kind == Define && len(a.child) > a.nleft+a.nright {
t, err = nodeType(interp, scope, a.child[a.nleft])
} else {
t, err = nodeType(interp, scope, n.child[0])
if err != nil {
return nil, err
}
if t.untyped {
var t1 *Type
t1, err = nodeType(interp, scope, n.child[1])
if !(t1.untyped && isInt(t1) && isFloat(t)) {
t = t1
}
}
}

View File

@@ -9,9 +9,7 @@ func valueGenerator(n *Node, i int) func(*Frame) reflect.Value {
case 0:
return func(f *Frame) reflect.Value { return f.data[i] }
case 1:
return func(f *Frame) reflect.Value {
return f.anc.data[i]
}
return func(f *Frame) reflect.Value { return f.anc.data[i] }
case 2:
return func(f *Frame) reflect.Value { return f.anc.anc.data[i] }
default: