Fix: handle recursive type definition (#239)
This commit is contained in:
committed by
Ludovic Fernandez
parent
dea1f56f38
commit
122506cc79
15
_test/struct22.go
Normal file
15
_test/struct22.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
Child *S
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{Name: "root"}
|
||||
s.Child = &S{Name: "child"}
|
||||
println(s.Child.Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// child
|
||||
15
_test/struct23.go
Normal file
15
_test/struct23.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
Child []*S
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{Name: "root"}
|
||||
s.Child = append(s.Child, &S{Name: "child"})
|
||||
println(s.Child[0].Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// child
|
||||
38
interp/example_eval_test.go
Normal file
38
interp/example_eval_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
)
|
||||
|
||||
// Generic example
|
||||
func Example_eval() {
|
||||
// Create a new interpreter context
|
||||
i := interp.New(interp.Options{})
|
||||
|
||||
// Run some code: define a new function
|
||||
_, err := i.Eval("func f(i int) int { return 2 * i }")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Access the interpreted f function with Eval
|
||||
v, err := i.Eval("f")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Returned v is a reflect.Value, so we can use its interface
|
||||
f, ok := v.Interface().(func(int) int)
|
||||
if !ok {
|
||||
log.Fatal("type assertion failed")
|
||||
}
|
||||
|
||||
// Use interpreted f as it was pre-compiled
|
||||
fmt.Println(f(2))
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
}
|
||||
@@ -198,6 +198,16 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isRecursiveStruct(t *itype) bool {
|
||||
if t.cat == structT && t.rtype.Kind() == reflect.Interface {
|
||||
return true
|
||||
}
|
||||
if t.cat == ptrT {
|
||||
return isRecursiveStruct(t.val)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func assign(n *node) {
|
||||
next := getExec(n.tnext)
|
||||
dvalue := make([]func(*frame) reflect.Value, n.nleft)
|
||||
@@ -218,6 +228,8 @@ func assign(n *node) {
|
||||
case src.kind == basicLit && src.val == nil:
|
||||
t := dest.typ.TypeOf()
|
||||
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
|
||||
case isRecursiveStruct(dest.typ):
|
||||
svalue[i] = genValueInterfacePtr(src)
|
||||
default:
|
||||
svalue[i] = genValue(src)
|
||||
}
|
||||
@@ -1071,8 +1083,14 @@ func getIndexSeq(n *node) {
|
||||
|
||||
func getPtrIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
var value func(*frame) reflect.Value
|
||||
if isRecursiveStruct(n.child[0].typ) {
|
||||
v := genValue(n.child[0])
|
||||
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
@@ -1341,6 +1359,7 @@ func mapLit(n *node) {
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func compositeBinMap(n *node) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
@@ -1697,9 +1716,12 @@ func _append(n *node) {
|
||||
l := len(args)
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
if arg.typ.untyped {
|
||||
switch {
|
||||
case isRecursiveStruct(n.typ.val):
|
||||
values[i] = genValueInterfacePtr(arg)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
} else {
|
||||
default:
|
||||
values[i] = genValue(arg)
|
||||
}
|
||||
}
|
||||
@@ -1713,9 +1735,14 @@ func _append(n *node) {
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
value0 := genValue(n.child[2])
|
||||
if n.child[2].typ.untyped {
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch {
|
||||
case isRecursiveStruct(n.typ.val):
|
||||
value0 = genValueInterfacePtr(n.child[2])
|
||||
case n.child[2].typ.untyped:
|
||||
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
value0 = genValue(n.child[2])
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
|
||||
@@ -54,6 +54,7 @@ type symbol struct {
|
||||
path string // package path if typ.cat is SrcPkgT or BinPkgT
|
||||
builtin bltnGenerator // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
recursive bool // true if symbol is a recursive type definition
|
||||
// TODO: implement constant checking
|
||||
//constant bool // true if symbol value is constant
|
||||
}
|
||||
|
||||
@@ -308,6 +308,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case identExpr:
|
||||
if sym, _, found := sc.lookup(n.ident); found {
|
||||
t = sym.typ
|
||||
if sym.recursive && t.incomplete {
|
||||
t.incomplete = false
|
||||
t.rtype = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
sym.typ = t
|
||||
}
|
||||
if t.incomplete && t.node != n {
|
||||
m := t.method
|
||||
if t, err = nodeType(interp, sc, t.node); err != nil {
|
||||
@@ -388,7 +393,13 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
|
||||
case structType:
|
||||
t.cat = structT
|
||||
var incomplete bool
|
||||
var incomplete, found bool
|
||||
var sym *symbol
|
||||
if sname := structName(n); sname != "" {
|
||||
if sym, _, found = sc.lookup(sname); found && sym.kind == typeSym {
|
||||
sym.recursive = true
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[0].child {
|
||||
switch {
|
||||
case len(c.child) == 1:
|
||||
@@ -432,6 +443,14 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
return t, err
|
||||
}
|
||||
|
||||
// struct name returns the name of a struct type
|
||||
func structName(n *node) string {
|
||||
if n.anc.kind == typeSpec {
|
||||
return n.anc.child[0].ident
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// fieldName returns an implicit struct field name according to node kind
|
||||
func fieldName(n *node) string {
|
||||
switch n.kind {
|
||||
|
||||
@@ -107,6 +107,17 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
it := reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := reflect.New(it).Elem()
|
||||
v.Set(value(f))
|
||||
return v.Addr()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user