fix: correct handling of multi-assign operations, including swap (#173)
This commit is contained in:
committed by
Ludovic Fernandez
parent
0bbdd37e55
commit
16690838e3
12
_test/assign3.go
Normal file
12
_test/assign3.go
Normal 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
12
_test/assign4.go
Normal 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
14
_test/assign5.go
Normal 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
14
_test/assign6.go
Normal 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
16
_test/assign7.go
Normal 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
14
_test/const4.go
Normal 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
20
_test/const5.go
Normal 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
|
||||
102
interp/ast.go
102
interp/ast.go
@@ -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
|
||||
}
|
||||
|
||||
218
interp/cfg.go
218
interp/cfg.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
116
interp/run.go
116
interp/run.go
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user