Compare commits

..

19 Commits

Author SHA1 Message Date
Marc Vertes
23dfef0ac8 fix: define a correct zero value for an not initialized interface{} 2020-02-04 18:04:05 +01:00
Marc Vertes
4fd6a2dc56 fix: handle recursive type definition involving a map object 2020-02-04 16:36:05 +01:00
Marc Vertes
92a63dbe09 fix: handle out of order type declaration for global var declaration 2020-02-03 17:36:04 +01:00
Marc Vertes
e434892b9a fix: import different source packages with same base name 2020-02-03 17:22:04 +01:00
Marc Vertes
712891dd77 fix: allow reuse of _ symbol in the same scope 2020-02-03 17:08:05 +01:00
Marc Vertes
137b16580c fix: handle binary methods where receiver is implicit 2020-02-03 16:54:04 +01:00
Sven Dowideit
b057ada531 Add an example of yaegi in a shebang line 2020-01-15 16:54:06 +01:00
Camal Cakar
6d90c5a641 Case sensitivity fix 2020-01-15 16:40:06 +01:00
Marc Vertes
5381ee65d1 fix: continue statement was not applied correctly 2020-01-10 17:50:05 +01:00
Marc Vertes
f1cde2be0f fix: apply automatic type conversion to constant expressions (#484) 2020-01-09 18:01:44 +01:00
Marc Vertes
bb04af2d4d doc: document import of source and binary packages (#477) 2020-01-08 18:51:30 +01:00
Marc Vertes
9a8a88dcb9 fix: use branch operation in || and && operators, fix storage for ! (#476) 2020-01-07 17:27:22 +01:00
Marc Vertes
f3f54a5302 doc: add explanation about CLA in CONTRIBUTING.md (#483) 2020-01-07 16:30:05 +01:00
Marc Vertes
878fcc835c fix: add support for ^ and + as unary operators 2020-01-07 15:34:05 +01:00
Marc Vertes
a1f2d3bf1d fix: isNil was not forwarding result when used in a branch expression 2019-12-19 18:32:04 +01:00
Marc Vertes
3cd37645eb fix: correct isValueUntyped() to handle typed constants 2019-12-19 15:38:05 +01:00
Marc Vertes
e1ac83f7d8 fix: correct type extraction for returned value 2019-12-17 10:18:06 +01:00
Marc Vertes
4f93be7f19 fix: emulate struct by interface{} only for recursive struct types 2019-12-16 19:00:07 +01:00
Marc Vertes
7a0c09f5eb fix: detect untyped values when importing from binary packages 2019-12-13 11:18:04 +01:00
41 changed files with 728 additions and 140 deletions

View File

@@ -13,7 +13,12 @@ discussions.
Once the proposal is approved, a Pull Request can be opened. If you want Once the proposal is approved, a Pull Request can be opened. If you want
to provide early visibility to reviewers, create a [Draft Pull Request]. to provide early visibility to reviewers, create a [Draft Pull Request].
We will also require you to sign the [Containous Contributor License Agreement]
after you submit your first pull request to this project. The link to sign the
agreement will be presented to you in the web interface of the pull request.
[Issues]: https://github.com/containous/yaegi/issues [Issues]: https://github.com/containous/yaegi/issues
[Pull Requests]: https://github.com/containous/yaegi/issues [Pull Requests]: https://github.com/containous/yaegi/issues
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md [Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/ [Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi

View File

@@ -129,6 +129,24 @@ $ yaegi cmd/yaegi/yaegi.go
> >
``` ```
Or for Go scripting in the shebang line:
```console
$ cat /tmp/test
#!/usr/bin/env yaegi
package main
import "fmt"
func main() {
fmt.Println("test")
}
$ ls -la /tmp/test
-rwxr-xr-x 1 dow184 dow184 93 Jan 6 13:38 /tmp/test
$ /tmp/test
test
```
## Documentation ## Documentation
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs]. Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs].

7
_test/b1/foo/foo.go Normal file
View File

@@ -0,0 +1,7 @@
package foo
import bar "github.com/containous/yaegi/_test/b2/foo"
var Desc = "in b1/foo"
var Desc2 = Desc + bar.Desc

3
_test/b2/foo/foo.go Normal file
View File

@@ -0,0 +1,3 @@
package foo
var Desc = "in b2/foo"

13
_test/bin2.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Abs(-5))
}
// Output:
// 5

18
_test/for8.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
for i := 0; i < 4; i++ {
for {
break
}
if i == 1 {
continue
}
println(i)
}
}
// Output:
// 0
// 2
// 3

12
_test/fun11.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var f F
type F func(int)
func main() {
println("ok")
}
// Output:
// ok

10
_test/import8.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/b1/foo"
func main() {
println(foo.Desc)
}
// Output:
// in b1/foo

12
_test/interface19.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
var I interface{}
func main() {
fmt.Printf("%T %v\n", I, I)
}
// Output:
// <nil> <nil>

16
_test/map19.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "fmt"
type cmap struct {
servers map[int64]*server
}
type server struct {
cm *cmap
}
func main() {
m := cmap{}
fmt.Println(m)
}

16
_test/method29.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"context"
"net"
)
var lookupHost = net.DefaultResolver.LookupHost
func main() {
res, err := lookupHost(context.Background(), "localhost")
println(len(res) > 0, err == nil)
}
// Output:
// true true

20
_test/method30.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type T struct {
Name string
}
func (t *T) foo(a string) string {
return t.Name + a
}
var g = &T{"global"}
var f = g.foo
func main() {
println(f("-x"))
}
// Output:
// global-x

15
_test/nil0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func f() (host, port string, err error) {
return "", "", nil
}
func main() {
h, p, err := f()
fmt.Println(h, p, err)
}
// Output:
// <nil>

12
_test/nil1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var a error = nil
if a == nil || a.Error() == "nil" {
println("a is nil")
}
}
// Output:
// a is nil

9
_test/or0.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(c || !c)
}
// Output:
// true

9
_test/or1.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(!c || c)
}
// Output:
// true

26
_test/primes.go Normal file
View File

@@ -0,0 +1,26 @@
package main
func Primes(n int) int {
var xs []int
for i := 2; len(xs) < n; i++ {
ok := true
for _, x := range xs {
if i%x == 0 {
ok = false
break
}
}
if !ok {
continue
}
xs = append(xs, i)
}
return xs[n-1]
}
func main() {
println(Primes(3))
}
// Output:
// 5

11
_test/str4.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "unicode/utf8"
func main() {
r, _ := utf8.DecodeRuneInString("Hello")
println(r < utf8.RuneSelf)
}
// Output:
// true

19
_test/struct29.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
B []T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

19
_test/struct30.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
M map[uint64]T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

15
_test/time11.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"fmt"
"time"
)
const df = time.Minute * 30
func main() {
fmt.Printf("df: %v %T\n", df, df)
}
// Output:
// df: 30m0s time.Duration

11
_test/var10.go Normal file
View File

@@ -0,0 +1,11 @@
package main
var _ = true
var _ = "hello"
func main() {
println("hello")
}
// Output:
// hello

10
_test/var11.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a, _, _, b = 1, true, "foo", 2
func main() {
println(a, b)
}
// Output:
// 1 2

View File

@@ -8,7 +8,7 @@ If invoked with no arguments, it processes the standard input
in a Read-Eval-Print-Loop. A prompt is displayed if standard input in a Read-Eval-Print-Loop. A prompt is displayed if standard input
is a terminal. is a terminal.
Given a file, it operates on that file. if the first line starts with Given a file, it operates on that file. If the first line starts with
"#!/usr/bin/env yaegi", and the file has exec permission, then the file "#!/usr/bin/env yaegi", and the file has exec permission, then the file
can be invoked directly from the shell. can be invoked directly from the shell.

View File

@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
} }
} }
{{end}} {{end}}
{{range $name, $op := .Unary}}
func {{$name}}Const(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
{{- if $op.Bool}}
n.rval.SetBool({{$op.Name}} v.Bool())
{{- else}}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt({{$op.Name}} v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint({{$op.Name}} v.Uint())
{{- if $op.Float}}
case reflect.Float32, reflect.Float64:
n.rval.SetFloat({{$op.Name}} v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex({{$op.Name}} v.Complex())
{{- end}}
}
{{- end}}
}
{{end}}
{{range $name, $op := .Comparison}} {{range $name, $op := .Comparison}}
func {{$name}}(n *node) { func {{$name}}(n *node) {
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
@@ -793,6 +817,7 @@ type Op struct {
Float bool // true if operator applies to float Float bool // true if operator applies to float
Complex bool // true if operator applies to complex Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
} }
func main() { func main() {
@@ -805,17 +830,17 @@ func main() {
b := &bytes.Buffer{} b := &bytes.Buffer{}
data := map[string]interface{}{ data := map[string]interface{}{
"Arithmetic": map[string]Op{ "Arithmetic": map[string]Op{
"add": {"+", true, true, true, false}, "add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false}, "sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false}, "mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false}, "quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false}, "rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true}, "shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true}, "shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false}, "and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false}, "or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false}, "xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false}, "andNot": {"&^", false, false, false, false, false},
}, },
"IncDec": map[string]Op{ "IncDec": map[string]Op{
"inc": {Name: "+"}, "inc": {Name: "+"},
@@ -829,6 +854,12 @@ func main() {
"lowerEqual": {Name: "<=", Complex: false}, "lowerEqual": {Name: "<=", Complex: false},
"notEqual": {Name: "!=", Complex: true}, "notEqual": {Name: "!=", Complex: true},
}, },
"Unary": map[string]Op{
"not": {Name: "!", Float: false, Bool: true},
"neg": {Name: "-", Float: true, Bool: false},
"pos": {Name: "+", Float: true, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false},
},
} }
if err = parse.Execute(b, data); err != nil { if err = parse.Execute(b, data); err != nil {
log.Fatal(err) log.Fatal(err)

View File

@@ -199,6 +199,7 @@ const (
aAndAssign aAndAssign
aAndNot aAndNot
aAndNotAssign aAndNotAssign
aBitNot
aCall aCall
aCase aCase
aCompositeLit aCompositeLit
@@ -217,11 +218,12 @@ const (
aMethod aMethod
aMul aMul
aMulAssign aMulAssign
aNegate aNeg
aNot aNot
aNotEqual aNotEqual
aOr aOr
aOrAssign aOrAssign
aPos
aQuo aQuo
aQuoAssign aQuoAssign
aRange aRange
@@ -255,6 +257,7 @@ var actions = [...]string{
aAndAssign: "&=", aAndAssign: "&=",
aAndNot: "&^", aAndNot: "&^",
aAndNotAssign: "&^=", aAndNotAssign: "&^=",
aBitNot: "^",
aCall: "call", aCall: "call",
aCase: "case", aCase: "case",
aCompositeLit: "compositeLit", aCompositeLit: "compositeLit",
@@ -271,11 +274,12 @@ var actions = [...]string{
aMethod: "Method", aMethod: "Method",
aMul: "*", aMul: "*",
aMulAssign: "*=", aMulAssign: "*=",
aNegate: "-", aNeg: "-",
aNot: "!", aNot: "!",
aNotEqual: "!=", aNotEqual: "!=",
aOr: "|", aOr: "|",
aOrAssign: "|=", aOrAssign: "|=",
aPos: "+",
aQuo: "/", aQuo: "/",
aQuoAssign: "/=", aQuoAssign: "/=",
aRange: "range", aRange: "range",
@@ -771,6 +775,8 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
var kind = unaryExpr var kind = unaryExpr
var act action var act action
switch a.Op { switch a.Op {
case token.ADD:
act = aPos
case token.AND: case token.AND:
kind = addressExpr kind = addressExpr
act = aAddr act = aAddr
@@ -779,7 +785,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
case token.NOT: case token.NOT:
act = aNot act = aNot
case token.SUB: case token.SUB:
act = aNegate act = aNeg
case token.XOR:
act = aBitNot
} }
st.push(addChild(&root, anc, pos, kind, act), nod) st.push(addChild(&root, anc, pos, kind, act), nod)

View File

@@ -24,6 +24,10 @@ var constOp = map[action]func(*node){
aShr: shrConst, aShr: shrConst,
aAndNot: andNotConst, aAndNot: andNotConst,
aXor: xorConst, aXor: xorConst,
aNot: notConst,
aBitNot: bitNotConst,
aNeg: negConst,
aPos: posConst,
} }
var constBltn = map[string]func(*node){ var constBltn = map[string]func(*node){
@@ -38,9 +42,8 @@ var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
// and pre-compute frame sizes and indexes for all un-named (temporary) and named // and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned. // variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run // Following this pass, the CFG is ready to run
func (interp *Interpreter) cfg(root *node) ([]*node, error) { func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc, pkgName := interp.initScopePkg(root) sc := interp.initScopePkg(pkgID)
var loop, loopRestart *node
var initNodes []*node var initNodes []*node
var iotaValue int var iotaValue int
var err error var err error
@@ -232,12 +235,12 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
} }
case forStmt0, forRangeStmt: case forStmt0, forRangeStmt:
loop, loopRestart = n, n.child[0]
sc = sc.pushBloc() sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.child[0]
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4: case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
loop, loopRestart = n, n.lastChild()
sc = sc.pushBloc() sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
case funcLit: case funcLit:
n.typ = nil // to force nodeType to recompute the type n.typ = nil // to force nodeType to recompute the type
@@ -309,7 +312,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
c[i], c[l] = c[l], c[i] c[i], c[l] = c[l], c[i]
} }
sc = sc.pushBloc() sc = sc.pushBloc()
loop = n sc.loop = n
case importSpec: case importSpec:
var name, ipath string var name, ipath string
@@ -407,7 +410,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if src.typ, err = nodeType(interp, sc, src); err != nil { if src.typ, err = nodeType(interp, sc, src); err != nil {
return return
} }
dest.typ = src.typ if src.typ.isBinMethod {
dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()}
} else {
dest.typ = src.typ
}
} }
if dest.typ.sizedef { if dest.typ.sizedef {
dest.typ.size = arrayTypeLen(src) dest.typ.size = arrayTypeLen(src)
@@ -602,8 +609,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
constOp[n.action](n) constOp[n.action](n)
} }
switch { switch {
//case n.typ != nil && n.typ.cat == BoolT && isAncBranch(n):
// n.findex = -1
case n.rval.IsValid(): case n.rval.IsValid():
n.gen = nop n.gen = nop
n.findex = -1 n.findex = -1
@@ -696,14 +701,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if len(n.child) > 0 { if len(n.child) > 0 {
gotoLabel(n.sym) gotoLabel(n.sym)
} else { } else {
n.tnext = loop n.tnext = sc.loop
} }
case continueStmt: case continueStmt:
if len(n.child) > 0 { if len(n.child) > 0 {
gotoLabel(n.sym) gotoLabel(n.sym)
} else { } else {
n.tnext = loopRestart n.tnext = sc.loopRestart
} }
case gotoStmt: case gotoStmt:
@@ -827,7 +832,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
body := n.child[0] body := n.child[0]
n.start = body.start n.start = body.start
body.tnext = n.start body.tnext = n.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt1: // for cond {} case forStmt1: // for cond {}
@@ -839,7 +843,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = cond.start body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt2: // for init; cond; {} case forStmt2: // for init; cond; {}
@@ -852,7 +855,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = cond.start body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt3: // for ; cond; post {} case forStmt3: // for ; cond; post {}
@@ -865,7 +867,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.fnext = n cond.fnext = n
body.tnext = post.start body.tnext = post.start
post.tnext = cond.start post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt3a: // for int; ; post {} case forStmt3a: // for int; ; post {}
@@ -874,7 +875,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
init.tnext = body.start init.tnext = body.start
body.tnext = post.start body.tnext = post.start
post.tnext = body.start post.tnext = body.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt4: // for init; cond; post {} case forStmt4: // for init; cond; post {}
@@ -888,11 +888,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.fnext = n cond.fnext = n
body.tnext = post.start body.tnext = post.start
post.tnext = cond.start post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forRangeStmt: case forRangeStmt:
loop, loopRestart = nil, nil
n.start = n.child[0].start n.start = n.child[0].start
n.child[0].fnext = n n.child[0].fnext = n
sc = sc.pop() sc = sc.pop()
@@ -903,10 +901,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc = sc.pop() sc = sc.pop()
funcName := n.child[1].ident funcName := n.child[1].ident
if !isMethod(n) { if !isMethod(n) {
interp.scopes[pkgName].sym[funcName].index = -1 // to force value to n.val interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgName].sym[funcName].typ = n.typ interp.scopes[pkgID].sym[funcName].typ = n.typ
interp.scopes[pkgName].sym[funcName].kind = funcSym interp.scopes[pkgID].sym[funcName].kind = funcSym
interp.scopes[pkgName].sym[funcName].node = n interp.scopes[pkgID].sym[funcName].node = n
} }
case funcLit: case funcLit:
@@ -1021,6 +1019,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n n.child[1].tnext = n
n.typ = n.child[0].typ n.typ = n.child[0].typ
n.findex = sc.add(n.typ) n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr: case lorExpr:
n.start = n.child[0].start n.start = n.child[0].start
@@ -1029,6 +1030,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n n.child[1].tnext = n
n.typ = n.child[0].typ n.typ = n.child[0].typ
n.findex = sc.add(n.typ) n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr: case parenExpr:
wireChild(n) wireChild(n)
@@ -1068,7 +1072,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// nil: Set node value to zero of return type // nil: Set node value to zero of return type
f := sc.def f := sc.def
var typ *itype var typ *itype
if typ, err = nodeType(interp, sc, f.child[2].child[1].child[i].lastChild()); err != nil { if typ, err = nodeType(interp, sc, f.child[2].child[1].fieldType(i)); err != nil {
return return
} }
if typ.cat == funcT { if typ.cat == funcT {
@@ -1097,7 +1101,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.val = method.Index n.val = method.Index
n.gen = getIndexBinMethod n.gen = getIndexBinMethod
n.recv = &receiver{node: n.child[0]} n.recv = &receiver{node: n.child[0]}
n.typ = &itype{cat: valueT, rtype: method.Type} n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
case n.typ.rtype.Kind() == reflect.Ptr: case n.typ.rtype.Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok { if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type} n.typ = &itype{cat: valueT, rtype: field.Type}
@@ -1155,7 +1159,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()} n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else { } else {
n.kind = rvalueExpr n.kind = rvalueExpr
n.typ = &itype{cat: valueT, rtype: s.Type()} n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
n.rval = s n.rval = s
} }
n.gen = nop n.gen = nop
@@ -1312,7 +1316,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start n.start = sbn.start
} }
sc = sc.pop() sc = sc.pop()
loop = nil
case switchIfStmt: // like an if-else chain case switchIfStmt: // like an if-else chain
sbn := n.lastChild() // switch block node sbn := n.lastChild() // switch block node
@@ -1349,7 +1352,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start n.start = sbn.start
} }
sc = sc.pop() sc = sc.pop()
loop = nil
case typeAssertExpr: case typeAssertExpr:
if len(n.child) > 1 { if len(n.child) > 1 {
@@ -1377,16 +1379,60 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case unaryExpr: case unaryExpr:
wireChild(n) wireChild(n)
n.typ = n.child[0].typ n.typ = n.child[0].typ
if n.action == aRecv { switch n.action {
case aRecv:
// Channel receive operation: set type to the channel data type // Channel receive operation: set type to the channel data type
if n.typ.cat == valueT { if n.typ.cat == valueT {
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()} n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
} else { } else {
n.typ = n.typ.val n.typ = n.typ.val
} }
case aBitNot:
if !isInt(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '^' operator")
return
}
case aNot:
if !isBool(n.typ) {
err = n.cfgErrorf("illegal operand type for '!' operator")
return
}
case aNeg, aPos:
if !isNumber(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '%v' operator", n.action)
return
}
} }
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr) // TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
n.findex = sc.add(n.typ) if n.child[0].rval.IsValid() && constOp[n.action] != nil {
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.typ.TypeOf() // init reflect type
constOp[n.action](n)
}
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = -1
case n.anc.kind == assignStmt && n.anc.action == aAssign:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
default:
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.findex = sc.add(n.typ)
}
case valueSpec: case valueSpec:
n.gen = reset n.gen = reset
@@ -1397,8 +1443,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
} }
} }
for _, c := range n.child[:l] { for _, c := range n.child[:l] {
index := sc.add(n.typ) var index int
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ} if sc.global {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
} else {
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
}
c.typ = n.typ c.typ = n.typ
c.findex = index c.findex = index
} }
@@ -1643,6 +1695,29 @@ func (n *node) isNatural() bool {
return false return false
} }
// fieldType returns the nth parameter field node (type) of a fieldList node
func (n *node) fieldType(m int) *node {
k := 0
l := len(n.child)
for i := 0; i < l; i++ {
cl := len(n.child[i].child)
if cl < 2 {
if k == m {
return n.child[i].lastChild()
}
k++
continue
}
for j := 0; j < cl-1; j++ {
if k == m {
return n.child[i].lastChild()
}
k++
}
}
return nil
}
// lastChild returns the last child of a node // lastChild returns the last child of a node
func (n *node) lastChild() *node { return n.child[len(n.child)-1] } func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
@@ -1718,13 +1793,6 @@ func getExec(n *node) bltn {
return n.exec return n.exec
} }
func fileNode(n *node) *node {
if n == nil || n.kind == fileStmt {
return n
}
return fileNode(n.anc)
}
// setExec recursively sets the node exec builtin function by walking the CFG // setExec recursively sets the node exec builtin function by walking the CFG
// from the entry point (first node to exec). // from the entry point (first node to exec).
func setExec(n *node) { func setExec(n *node) {
@@ -1825,3 +1893,13 @@ func arrayTypeLen(n *node) int {
} }
return max + 1 return max + 1
} }
// isValueUntyped returns true if value is untyped
func isValueUntyped(v reflect.Value) bool {
// Consider only constant values.
if v.CanSet() {
return false
}
t := v.Type()
return t.String() == t.Kind().String()
}

View File

@@ -4,6 +4,18 @@ Package interp provides a complete Go interpreter.
For the Go language itself, refer to the official Go specification For the Go language itself, refer to the official Go specification
https://golang.org/ref/spec. https://golang.org/ref/spec.
Importing packages
Packages can be imported in source or binary form, using the standard
Go import statement. In source form, packages are searched first in the
vendor directory, the preferred way to store source dependencies. If not
found in vendor, sources modules will be searched in GOPATH. Go modules
are not supported yet by yaegi.
Binary form packages are compiled and linked with the interpreter
executable, and exposed to scripts with the Use method. The goexports
command can be used to generate package wrappers.
Custom build tags Custom build tags
Custom build tags allow to control which files in imported source Custom build tags allow to control which files in imported source

View File

@@ -38,7 +38,7 @@ func (n *node) astDot(out io.Writer, name string) {
func (n *node) cfgDot(out io.Writer) { func (n *node) cfgDot(out io.Writer) {
fmt.Fprintf(out, "digraph cfg {\n") fmt.Fprintf(out, "digraph cfg {\n")
n.Walk(nil, func(n *node) { n.Walk(nil, func(n *node) {
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil { if n.kind == basicLit || n.tnext == nil {
return return
} }
var label string var label string

View File

@@ -8,8 +8,8 @@ import (
// variables and functions symbols at package level, prior to CFG. // variables and functions symbols at package level, prior to CFG.
// All function bodies are skipped. GTA is necessary to handle out of // All function bodies are skipped. GTA is necessary to handle out of
// order declarations and multiple source files packages. // order declarations and multiple source files packages.
func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) { func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error) {
sc, _ := interp.initScopePkg(root) sc := interp.initScopePkg(pkgID)
var err error var err error
var iotaValue int var iotaValue int
var revisit []*node var revisit []*node
@@ -30,6 +30,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
case defineStmt: case defineStmt:
var atyp *itype var atyp *itype
if n.nleft+n.nright < len(n.child) { if n.nleft+n.nright < len(n.child) {
// Type is declared explicitly in the assign expression.
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil { if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
return false return false
} }
@@ -51,7 +52,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
val = src.rval val = src.rval
} }
if typ.incomplete { if typ.incomplete {
// Come back when type is known // Come back when type is known.
revisit = append(revisit, n) revisit = append(revisit, n)
return false return false
} }
@@ -59,6 +60,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
err = n.cfgErrorf("use of untyped nil") err = n.cfgErrorf("use of untyped nil")
return false return false
} }
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
}
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val} sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
if n.anc.kind == constDecl { if n.anc.kind == constDecl {
sc.sym[dest.ident].kind = constSym sc.sym[dest.ident].kind = constSym
@@ -76,6 +80,11 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil { if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
return false return false
} }
if n.typ.incomplete {
// Come back when type is known.
revisit = append(revisit, n)
return false
}
} }
for _, c := range n.child[:l] { for _, c := range n.child[:l] {
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ} sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
@@ -142,7 +151,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
default: // import symbols in package namespace default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}} sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
} }
} else if err = interp.importSrc(rpath, ipath, name); err == nil { } else if err = interp.importSrc(rpath, ipath); err == nil {
sc.types = interp.universe.types sc.types = interp.universe.types
switch name { switch name {
case "_": // no import of symbols case "_": // no import of symbols

View File

@@ -283,7 +283,7 @@ func (interp *Interpreter) resizeFrame() {
func (interp *Interpreter) main() *node { func (interp *Interpreter) main() *node {
interp.mutex.RLock() interp.mutex.RLock()
defer interp.mutex.RUnlock() defer interp.mutex.RUnlock()
if m, ok := interp.scopes[mainID]; ok && m.sym[mainID] != nil { if m, ok := interp.scopes[interp.Name]; ok && m.sym[mainID] != nil {
return m.sym[mainID].node return m.sym[mainID].node
} }
return nil return nil
@@ -308,18 +308,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
} }
// Global type analysis // Global type analysis
revisit, err := interp.gta(root, pkgName) revisit, err := interp.gta(root, pkgName, interp.Name)
if err != nil { if err != nil {
return res, err return res, err
} }
for _, n := range revisit { for _, n := range revisit {
if _, err = interp.gta(n, pkgName); err != nil { if _, err = interp.gta(n, pkgName, interp.Name); err != nil {
return res, err return res, err
} }
} }
// Annotate AST with CFG infos // Annotate AST with CFG infos
initNodes, err := interp.cfg(root) initNodes, err := interp.cfg(root, interp.Name)
if err != nil { if err != nil {
return res, err return res, err
} }
@@ -336,7 +336,7 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
interp.mutex.Lock() interp.mutex.Lock()
if interp.universe.sym[pkgName] == nil { if interp.universe.sym[pkgName] == nil {
// Make the package visible under a path identical to its name // Make the package visible under a path identical to its name
interp.srcPkg[pkgName] = interp.scopes[pkgName].sym interp.srcPkg[pkgName] = interp.scopes[interp.Name].sym
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}} interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
} }
interp.mutex.Unlock() interp.mutex.Unlock()

View File

@@ -47,11 +47,17 @@ func TestEvalArithmetic(t *testing.T) {
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"}, {desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1 << 1.0", res: "2"}, {desc: "shl_IF", src: "1 << 1.0", res: "2"},
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"}, {desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1.0 << 1", res: "2"}, {desc: "shl_IF2", src: "1.0 << 1", res: "2"},
{desc: "shr_II", src: "1 >> 8", res: "0"}, {desc: "shr_II", src: "1 >> 8", res: "0"},
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"}, {desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "shr_IF", src: "1 >> 1.0", res: "0"}, {desc: "shr_IF", src: "1 >> 1.0", res: "0"},
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"}, {desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "neg_I", src: "-2", res: "-2"},
{desc: "pos_I", src: "+2", res: "2"},
{desc: "bitnot_I", src: "^2", res: "-3"},
{desc: "bitnot_F", src: "^0.2", err: "1:28: illegal operand type for '^' operator"},
{desc: "not_B", src: "!false", res: "true"},
{desc: "not_I", src: "!0", err: "1:28: illegal operand type for '!' operator"},
}) })
} }

View File

@@ -1864,6 +1864,57 @@ func inc(n *node) {
} }
} }
func bitNotConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(^v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(^v.Uint())
}
}
func negConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(-v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(-v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(-v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(-v.Complex())
}
}
func notConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
n.rval.SetBool(!v.Bool())
}
func posConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(+v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(+v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(+v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(+v.Complex())
}
}
func equal(n *node) { func equal(n *node) {
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
dest := genValue(n) dest := genValue(n)

View File

@@ -24,6 +24,7 @@ var builtin = [...]bltnGenerator{
aAndAssign: andAssign, aAndAssign: andAssign,
aAndNot: andNot, aAndNot: andNot,
aAndNotAssign: andNotAssign, aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call, aCall: call,
aCase: _case, aCase: _case,
aCompositeLit: arrayLit, aCompositeLit: arrayLit,
@@ -40,11 +41,12 @@ var builtin = [...]bltnGenerator{
aLowerEqual: lowerEqual, aLowerEqual: lowerEqual,
aMul: mul, aMul: mul,
aMulAssign: mulAssign, aMulAssign: mulAssign,
aNegate: negate, aNeg: neg,
aNot: not, aNot: not,
aNotEqual: notEqual, aNotEqual: notEqual,
aOr: or, aOr: or,
aOrAssign: orAssign, aOrAssign: orAssign,
aPos: pos,
aQuo: quo, aQuo: quo,
aQuoAssign: quoAssign, aQuoAssign: quoAssign,
aRange: _range, aRange: _range,
@@ -239,7 +241,7 @@ func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
if t.cat == structT && rtype.Kind() == reflect.Interface { if t.cat == structT && rtype.Kind() == reflect.Interface {
return true return true
} }
if t.cat == ptrT { if t.cat == ptrT && t.rtype != nil {
return isRecursiveStruct(t.val, t.rtype.Elem()) return isRecursiveStruct(t.val, t.rtype.Elem())
} }
return false return false
@@ -291,12 +293,17 @@ func assign(n *node) {
} }
if n.nleft == 1 { if n.nleft == 1 {
if s, d, i := svalue[0], dvalue[0], ivalue[0]; i != nil { switch s, d, i := svalue[0], dvalue[0], ivalue[0]; {
case n.child[0].ident == "_":
n.exec = func(f *frame) bltn {
return next
}
case i != nil:
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
d(f).SetMapIndex(i(f), s(f)) d(f).SetMapIndex(i(f), s(f))
return next return next
} }
} else { default:
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
d(f).Set(s(f)) d(f).Set(s(f))
return next return next
@@ -314,7 +321,6 @@ func assign(n *node) {
default: default:
t = typ.TypeOf() t = typ.TypeOf()
} }
//types[i] = n.child[sbase+i].typ.TypeOf()
types[i] = t types[i] = t
} }
@@ -324,10 +330,16 @@ func assign(n *node) {
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue)) t := make([]reflect.Value, len(svalue))
for i, s := range svalue { for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem() t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f)) t[i].Set(s(f))
} }
for i, d := range dvalue { for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil { if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else { } else {
@@ -340,21 +352,24 @@ func assign(n *node) {
} }
func not(n *node) { func not(n *node) {
dest := genValue(n)
value := genValue(n.child[0]) value := genValue(n.child[0])
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
i := n.findex
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if !value(f).Bool() { if !value(f).Bool() {
f.data[i].SetBool(true)
return tnext return tnext
} }
f.data[i].SetBool(false)
return fnext return fnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).Bool()) dest(f).SetBool(!value(f).Bool())
return tnext return tnext
} }
} }
@@ -658,7 +673,7 @@ func call(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] }) values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
} }
default: default:
if c.kind == basicLit { if c.kind == basicLit || c.rval.IsValid() {
var argType reflect.Type var argType reflect.Type
if variadic >= 0 && i >= variadic { if variadic >= 0 && i >= variadic {
argType = n.child[0].typ.arg[variadic].val.TypeOf() argType = n.child[0].typ.arg[variadic].val.TypeOf()
@@ -823,7 +838,9 @@ func callBin(n *node) {
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types // method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
rcvrOffset := 0 rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface { if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
rcvrOffset = 1 if funcType.NumIn() > len(child) {
rcvrOffset = 1
}
} }
for i, c := range child { for i, c := range child {
@@ -843,7 +860,7 @@ func callBin(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] }) values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
} }
default: default:
if c.kind == basicLit { if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{}) // Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type var argType reflect.Type
if variadic >= 0 && i >= variadic { if variadic >= 0 && i >= variadic {
@@ -862,7 +879,6 @@ func callBin(n *node) {
case interfaceT: case interfaceT:
values = append(values, genValueInterfaceValue(c)) values = append(values, genValueInterfaceValue(c))
default: default:
//values = append(values, genValue(c))
values = append(values, genInterfaceWrapper(c, defType)) values = append(values, genInterfaceWrapper(c, defType))
} }
} }
@@ -1246,7 +1262,7 @@ func getIndexSeqMethod(n *node) {
} }
} }
func negate(n *node) { func neg(n *node) {
dest := genValue(n) dest := genValue(n)
value := genValue(n.child[0]) value := genValue(n.child[0])
next := getExec(n.tnext) next := getExec(n.tnext)
@@ -1270,6 +1286,37 @@ func negate(n *node) {
} }
} }
func pos(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
dest(f).Set(value(f))
return next
}
}
func bitNot(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
typ := n.typ.TypeOf()
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.exec = func(f *frame) bltn {
dest(f).SetInt(^value(f).Int())
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.exec = func(f *frame) bltn {
dest(f).SetUint(^value(f).Uint())
return next
}
}
}
func land(n *node) { func land(n *node) {
value0 := genValue(n.child[0]) value0 := genValue(n.child[0])
value1 := genValue(n.child[1]) value1 := genValue(n.child[1])
@@ -1946,7 +1993,7 @@ func _close(n *node) {
} }
func _complex(n *node) { func _complex(n *node) {
i := n.findex dest := genValue(n)
c1, c2 := n.child[1], n.child[2] c1, c2 := n.child[1], n.child[2]
convertLiteralValue(c1, floatType) convertLiteralValue(c1, floatType)
convertLiteralValue(c2, floatType) convertLiteralValue(c2, floatType)
@@ -1956,38 +2003,38 @@ func _complex(n *node) {
if typ := n.typ.TypeOf(); isComplex(typ) { if typ := n.typ.TypeOf(); isComplex(typ) {
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetComplex(complex(value0(f).Float(), value1(f).Float())) dest(f).SetComplex(complex(value0(f).Float(), value1(f).Float()))
return next return next
} }
} else { } else {
// Not a complex type: ignore imaginary part // Not a complex type: ignore imaginary part
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].Set(value0(f).Convert(typ)) dest(f).Set(value0(f).Convert(typ))
return next return next
} }
} }
} }
func _imag(n *node) { func _imag(n *node) {
i := n.findex dest := genValue(n)
convertLiteralValue(n.child[1], complexType) convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetFloat(imag(value(f).Complex())) dest(f).SetFloat(imag(value(f).Complex()))
return next return next
} }
} }
func _real(n *node) { func _real(n *node) {
i := n.findex dest := genValue(n)
convertLiteralValue(n.child[1], complexType) convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetFloat(real(value(f).Complex())) dest(f).SetFloat(real(value(f).Complex()))
return next return next
} }
} }
@@ -2005,12 +2052,12 @@ func _delete(n *node) {
} }
func _len(n *node) { func _len(n *node) {
i := n.findex dest := genValue(n)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetInt(int64(value(f).Len())) dest(f).SetInt(int64(value(f).Len()))
return next return next
} }
} }
@@ -2222,13 +2269,16 @@ func recv2(n *node) {
} }
func convertLiteralValue(n *node, t reflect.Type) { func convertLiteralValue(n *node, t reflect.Type) {
if n.kind != basicLit || t == nil || t.Kind() == reflect.Interface { // Skip non-constant values, undefined target type or interface target type.
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
return return
} }
if n.rval.IsValid() { if n.rval.IsValid() {
// Convert constant value to target type.
n.rval = n.rval.Convert(t) n.rval = n.rval.Convert(t)
} else { } else {
n.rval = reflect.New(t).Elem() // convert to type nil value // Create a zero value of target type.
n.rval = reflect.New(t).Elem()
} }
} }
@@ -2420,19 +2470,21 @@ func isNil(n *node) {
value = genValue(n.child[0]) value = genValue(n.child[0])
} }
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if value(f).IsNil() { if value(f).IsNil() {
dest(f).SetBool(true)
return tnext return tnext
} }
dest(f).SetBool(false)
return fnext return fnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(value(f).IsNil()) dest(f).SetBool(value(f).IsNil())
return tnext return tnext
} }
} }
@@ -2446,19 +2498,21 @@ func isNotNil(n *node) {
value = genValue(n.child[0]) value = genValue(n.child[0])
} }
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if value(f).IsNil() { if value(f).IsNil() {
dest(f).SetBool(false)
return fnext return fnext
} }
dest(f).SetBool(true)
return tnext return tnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).IsNil()) dest(f).SetBool(!value(f).IsNil())
return tnext return tnext
} }
} }

View File

@@ -74,12 +74,15 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout. // execution to the index in frame, created exactly from the types layout.
// //
type scope struct { type scope struct {
anc *scope // Ancestor upper scope anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil def *node // function definition node this scope belongs to, or nil
types []reflect.Type // Frame layout, may be shared by same level scopes loop *node // loop exit node for break statement
level int // Frame level: number of frame indirections to access var during execution loopRestart *node // loop restart node for continue statement
sym map[string]*symbol // Map of symbols defined in this current scope pkgID string // unique id of package in which scope is defined
global bool // true if scope refers to global space (single frame for universe and package level scopes) types []reflect.Type // Frame layout, may be shared by same level scopes
level int // Frame level: number of frame indirections to access var during execution
sym map[string]*symbol // Map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
} }
// push creates a new scope and chain it to the current one // push creates a new scope and chain it to the current one
@@ -95,6 +98,8 @@ func (s *scope) push(indirect bool) *scope {
sc.global = s.global sc.global = s.global
sc.level = s.level sc.level = s.level
} }
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc return &sc
} }
@@ -156,19 +161,15 @@ func (s *scope) add(typ *itype) (index int) {
return return
} }
func (interp *Interpreter) initScopePkg(n *node) (*scope, string) { func (interp *Interpreter) initScopePkg(pkgID string) *scope {
sc := interp.universe sc := interp.universe
pkgName := mainID
if p := fileNode(n); p != nil {
pkgName = p.child[0].ident
}
interp.mutex.Lock() interp.mutex.Lock()
if _, ok := interp.scopes[pkgName]; !ok { if _, ok := interp.scopes[pkgID]; !ok {
interp.scopes[pkgName] = sc.pushBloc() interp.scopes[pkgID] = sc.pushBloc()
} }
sc = interp.scopes[pkgName] sc = interp.scopes[pkgID]
sc.pkgID = pkgID
interp.mutex.Unlock() interp.mutex.Unlock()
return sc, pkgName return sc
} }

View File

@@ -8,7 +8,7 @@ import (
"strings" "strings"
) )
func (interp *Interpreter) importSrc(rPath, path, alias string) error { func (interp *Interpreter) importSrc(rPath, path string) error {
var dir string var dir string
var err error var err error
@@ -29,6 +29,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil { } else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil {
return err return err
} }
if interp.rdir[path] { if interp.rdir[path] {
return fmt.Errorf("import cycle not allowed\n\timports %s", path) return fmt.Errorf("import cycle not allowed\n\timports %s", path)
} }
@@ -66,6 +67,9 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
if root == nil { if root == nil {
continue continue
} }
if interp.astDot {
root.astDot(dotX(), name)
}
if pkgName == "" { if pkgName == "" {
pkgName = pname pkgName = pname
} else if pkgName != pname { } else if pkgName != pname {
@@ -75,7 +79,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
subRPath := effectivePkg(rPath, path) subRPath := effectivePkg(rPath, path)
var list []*node var list []*node
list, err = interp.gta(root, subRPath) list, err = interp.gta(root, subRPath, path)
if err != nil { if err != nil {
return err return err
} }
@@ -85,7 +89,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// revisit incomplete nodes where GTA could not complete // revisit incomplete nodes where GTA could not complete
for pkg, nodes := range revisit { for pkg, nodes := range revisit {
for _, n := range nodes { for _, n := range nodes {
if _, err = interp.gta(n, pkg); err != nil { if _, err = interp.gta(n, pkg, path); err != nil {
return err return err
} }
} }
@@ -94,7 +98,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Generate control flow graphs // Generate control flow graphs
for _, root := range rootNodes { for _, root := range rootNodes {
var nodes []*node var nodes []*node
if nodes, err = interp.cfg(root); err != nil { if nodes, err = interp.cfg(root, path); err != nil {
return err return err
} }
initNodes = append(initNodes, nodes...) initNodes = append(initNodes, nodes...)
@@ -103,13 +107,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Register source package in the interpreter. The package contains only // Register source package in the interpreter. The package contains only
// the global symbols in the package scope. // the global symbols in the package scope.
interp.mutex.Lock() interp.mutex.Lock()
interp.srcPkg[path] = interp.scopes[pkgName].sym interp.srcPkg[path] = interp.scopes[path].sym
// Rename imported pkgName to alias if they are different
if pkgName != alias {
interp.scopes[alias] = interp.scopes[pkgName]
delete(interp.scopes, pkgName)
}
interp.frame.mutex.Lock() interp.frame.mutex.Lock()
interp.resizeFrame() interp.resizeFrame()
@@ -189,7 +187,7 @@ func effectivePkg(root, path string) string {
for i := 0; i < len(splitPath); i++ { for i := 0; i < len(splitPath); i++ {
part := splitPath[len(splitPath)-1-i] part := splitPath[len(splitPath)-1-i]
if part == splitRoot[len(splitRoot)-1-rootIndex] { if part == splitRoot[len(splitRoot)-1-rootIndex] && i != 0 {
prevRootIndex = rootIndex prevRootIndex = rootIndex
rootIndex++ rootIndex++
} else if prevRootIndex == rootIndex { } else if prevRootIndex == rootIndex {

View File

@@ -103,22 +103,23 @@ type structField struct {
// itype defines the internal representation of types in the interpreter // itype defines the internal representation of types in the interpreter
type itype struct { type itype struct {
cat tcat // Type category cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil method []*node // Associated methods or nil
name string // name of type within its package for a defined type name string // name of type within its package for a defined type
path string // for a defined type, the package import path path string // for a defined type, the package import path
size int // Size of array if ArrayT size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations) incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number) untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition sizedef bool // true if array size is computed from type definition
node *node // root AST node of type definition isBinMethod bool // true if the type refers to a bin method function
scope *scope // type declaration scope (in case of re-parse incomplete type) node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
} }
// nodeType returns a type definition for the corresponding AST subtree // nodeType returns a type definition for the corresponding AST subtree
@@ -175,7 +176,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
} }
} else { } else {
// Evaluate constant array size expression // Evaluate constant array size expression
if _, err = interp.cfg(n.child[0]); err != nil { if _, err = interp.cfg(n.child[0], sc.pkgID); err != nil {
return nil, err return nil, err
} }
t.incomplete = true t.incomplete = true
@@ -482,7 +483,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if m, _ := lt.lookupMethod(name); m != nil { if m, _ := lt.lookupMethod(name); m != nil {
t, err = nodeType(interp, sc, m.child[2]) t, err = nodeType(interp, sc, m.child[2])
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok { } else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
t = &itype{cat: valueT, rtype: bm.Type} t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true}
} else if ti := lt.lookupField(name); len(ti) > 0 { } else if ti := lt.lookupField(name); len(ti) > 0 {
t = lt.fieldSeq(ti) t = lt.fieldSeq(ti)
} else if bs, _, ok := lt.lookupBinField(name); ok { } else if bs, _, ok := lt.lookupBinField(name); ok {
@@ -809,6 +810,22 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
return s, index, ok return s, index, ok
} }
// methodCallType returns a method function type without the receiver defined.
// The input type must be a method function type with the receiver as the first input argument.
func (t *itype) methodCallType() reflect.Type {
it := []reflect.Type{}
ni := t.rtype.NumIn()
for i := 1; i < ni; i++ {
it = append(it, t.rtype.In(i))
}
ot := []reflect.Type{}
no := t.rtype.NumOut()
for i := 0; i < no; i++ {
ot = append(ot, t.rtype.Out(i))
}
return reflect.FuncOf(it, ot, t.rtype.IsVariadic())
}
// getMethod returns a pointer to the method definition // getMethod returns a pointer to the method definition
func (t *itype) getMethod(name string) *node { func (t *itype) getMethod(name string) *node {
for _, m := range t.method { for _, m := range t.method {
@@ -885,7 +902,9 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
panic(err) panic(err)
} }
} }
if t.val != nil && defined[t.val.name] { if t.val != nil && defined[t.val.name] && !t.val.incomplete && t.val.rtype == nil {
// Replace reference to self (direct or indirect) by an interface{} to handle
// recursive types with reflect.
t.val.rtype = interf t.val.rtype = interf
} }
switch t.cat { switch t.cat {
@@ -914,7 +933,7 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
case interfaceT: case interfaceT:
t.rtype = interf t.rtype = interf
case mapT: case mapT:
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf()) t.rtype = reflect.MapOf(t.key.refType(defined), t.val.refType(defined))
case ptrT: case ptrT:
t.rtype = reflect.PtrTo(t.val.refType(defined)) t.rtype = reflect.PtrTo(t.val.refType(defined))
case structT: case structT:

View File

@@ -180,11 +180,23 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
} }
} }
func zeroInterfaceValue() reflect.Value {
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
v := reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
return reflect.ValueOf(valueInterface{n, v})
}
func genValueInterfaceValue(n *node) func(*frame) reflect.Value { func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
value := genValue(n) value := genValue(n)
return func(f *frame) reflect.Value { return func(f *frame) reflect.Value {
return value(f).Interface().(valueInterface).value v := value(f)
if v.Interface().(valueInterface).node == nil {
// Uninitialized interface value, set it to a correct zero value.
v.Set(zeroInterfaceValue())
v = value(f)
}
return v.Interface().(valueInterface).value
} }
} }

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14 // +build go1.12,!go1.14
// Package stdlib provides wrappers of standard library packages to be imported natively in Yaegi.
package stdlib package stdlib
import "reflect" import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14 // +build go1.12,!go1.14
// Package syscall provide wrapper of standard library syscall package for native import in Yaegi.
package syscall package syscall
import "reflect" import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14 // +build go1.12,!go1.14
// Package unsafe provides wrapper of standard library unsafe package to be imported natively in Yaegi.
package unsafe package unsafe
import "reflect" import "reflect"