Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca68c6cd95 | ||
|
|
953b122e67 | ||
|
|
9b07e73b5e | ||
|
|
78bbcda1f8 | ||
|
|
6e33f89146 | ||
|
|
d29b0a48ff | ||
|
|
c7c1bea7ef | ||
|
|
1ae2649655 | ||
|
|
0ace9244c4 | ||
|
|
2edd18a0c0 | ||
|
|
cfb73445a2 | ||
|
|
94e0b582ea | ||
|
|
3548c8744e | ||
|
|
d8bdc6670b | ||
|
|
27520f6dae | ||
|
|
7037424edf | ||
|
|
1b971b539c | ||
|
|
681f2f9c40 | ||
|
|
05960316f8 | ||
|
|
902af477b8 | ||
|
|
812e55b95e | ||
|
|
6c339ce562 | ||
|
|
23dfef0ac8 | ||
|
|
4fd6a2dc56 | ||
|
|
92a63dbe09 | ||
|
|
e434892b9a | ||
|
|
712891dd77 | ||
|
|
137b16580c | ||
|
|
b057ada531 | ||
|
|
6d90c5a641 | ||
|
|
5381ee65d1 | ||
|
|
f1cde2be0f | ||
|
|
bb04af2d4d | ||
|
|
9a8a88dcb9 | ||
|
|
f3f54a5302 | ||
|
|
878fcc835c | ||
|
|
a1f2d3bf1d |
@@ -37,6 +37,7 @@
|
||||
"funlen",
|
||||
"gocognit",
|
||||
"stylecheck",
|
||||
"gomnd",
|
||||
]
|
||||
|
||||
[issues]
|
||||
|
||||
@@ -19,8 +19,8 @@ cache:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
env: STABLE=true
|
||||
|
||||
env:
|
||||
|
||||
@@ -13,7 +13,12 @@ discussions.
|
||||
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].
|
||||
|
||||
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
|
||||
[Pull Requests]: https://github.com/containous/yaegi/issues
|
||||
[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/
|
||||
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi
|
||||
|
||||
18
README.md
18
README.md
@@ -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 about Yaegi commands and libraries can be found at usual [godoc.org][docs].
|
||||
|
||||
23
_test/a40.go
Normal file
23
_test/a40.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type rule uint8
|
||||
|
||||
const (
|
||||
r0 rule = iota
|
||||
r1
|
||||
r2
|
||||
)
|
||||
|
||||
var a = [...]int{
|
||||
r0: 1,
|
||||
r1: 12,
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [1 12]
|
||||
10
_test/a41.go
Normal file
10
_test/a41.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a = [...]bool{true, true}
|
||||
|
||||
func main() {
|
||||
println(a[0] && true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
10
_test/and3.go
Normal file
10
_test/and3.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a = true && true
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -9,5 +9,5 @@ func main() {
|
||||
fmt.Println(buf)
|
||||
}
|
||||
|
||||
// Output
|
||||
// Output:
|
||||
// []
|
||||
|
||||
7
_test/b1/foo/foo.go
Normal file
7
_test/b1/foo/foo.go
Normal 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
3
_test/b2/foo/foo.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
var Desc = "in b2/foo"
|
||||
@@ -1,4 +1,4 @@
|
||||
println("Hello")
|
||||
|
||||
// Error:
|
||||
// _test/bad0.go:1:1: expected 'package', found println
|
||||
// 1:1: expected 'package', found println
|
||||
|
||||
13
_test/bin2.go
Normal file
13
_test/bin2.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(math.Abs(-5))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
12
_test/bin3.go
Normal file
12
_test/bin3.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "part1"
|
||||
str += fmt.Sprintf("%s", "part2")
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// part1part2
|
||||
12
_test/chan8.go
Normal file
12
_test/chan8.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
messages := make(chan bool)
|
||||
|
||||
go func() { messages <- true }()
|
||||
|
||||
println(<-messages && true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -7,5 +7,5 @@ func main() {
|
||||
fmt.Printf("%T %v\n", s, s)
|
||||
}
|
||||
|
||||
// Output
|
||||
// Output:
|
||||
// int 2
|
||||
|
||||
16
_test/composite5.go
Normal file
16
_test/composite5.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T struct {
|
||||
m uint16
|
||||
}
|
||||
|
||||
var t = T{1<<2 | 1<<3}
|
||||
|
||||
func main() {
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {12}
|
||||
20
_test/composite6.go
Normal file
20
_test/composite6.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containous/yaegi/_test/ct1"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
m uint16
|
||||
}
|
||||
|
||||
var t = T{1 << ct1.R}
|
||||
|
||||
func main() {
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {2}
|
||||
20
_test/composite7.go
Normal file
20
_test/composite7.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
var tab = []*T{{
|
||||
name: "foo",
|
||||
}, {
|
||||
name: "bar",
|
||||
}}
|
||||
|
||||
func main() {
|
||||
println(len(tab))
|
||||
println(tab[0].name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// foo
|
||||
17
_test/const10.go
Normal file
17
_test/const10.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = a + d
|
||||
d = e + f
|
||||
e = 3
|
||||
f = 4
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 16
|
||||
22
_test/const6.go
Normal file
22
_test/const6.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
maxNonStarters = 30
|
||||
maxBufferSize = maxNonStarters + 2
|
||||
)
|
||||
|
||||
type reorderBuffer struct {
|
||||
rune [maxBufferSize]Properties
|
||||
}
|
||||
|
||||
type Properties struct {
|
||||
pos uint8
|
||||
size uint8
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(len(reorderBuffer{}.rune))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 32
|
||||
19
_test/const7.go
Normal file
19
_test/const7.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
a = iota
|
||||
b
|
||||
c
|
||||
d
|
||||
)
|
||||
|
||||
type T [c]int
|
||||
|
||||
func main() {
|
||||
fmt.Println(T{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [0 0]
|
||||
15
_test/const8.go
Normal file
15
_test/const8.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = 4
|
||||
d = 5
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(a, b, c, d)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2 9 4 5
|
||||
17
_test/const9.go
Normal file
17
_test/const9.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = a + d
|
||||
d = e + f
|
||||
e = b + 2
|
||||
f = 4
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(b)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:2: constant definition loop
|
||||
9
_test/ct1/ct1.go
Normal file
9
_test/ct1/ct1.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package ct1
|
||||
|
||||
type Class uint
|
||||
|
||||
const (
|
||||
L Class = iota
|
||||
R
|
||||
AL
|
||||
)
|
||||
23
_test/defer4.go
Normal file
23
_test/defer4.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type T struct {
|
||||
mu sync.RWMutex
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T) get() string {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.name
|
||||
}
|
||||
|
||||
var d = T{name: "test"}
|
||||
|
||||
func main() {
|
||||
println(d.get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
18
_test/for8.go
Normal file
18
_test/for8.go
Normal 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
12
_test/fun11.go
Normal 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
10
_test/import8.go
Normal 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
12
_test/interface19.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var I interface{}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%T %v\n", I, I)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil> <nil>
|
||||
12
_test/interface20.go
Normal file
12
_test/interface20.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = string("A")
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// A
|
||||
12
_test/interface21.go
Normal file
12
_test/interface21.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 1)
|
||||
s[0] = 1
|
||||
fmt.Println(s[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
12
_test/interface22.go
Normal file
12
_test/interface22.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 0)
|
||||
s = append(s, 1)
|
||||
fmt.Println(s[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
12
_test/interface23.go
Normal file
12
_test/interface23.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
m["A"] = string("A")
|
||||
fmt.Println(m["A"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// A
|
||||
11
_test/interface24.go
Normal file
11
_test/interface24.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
fmt.Println(m["B"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
14
_test/interface25.go
Normal file
14
_test/interface25.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
m["A"] = 1
|
||||
for _, v := range m {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
14
_test/interface26.go
Normal file
14
_test/interface26.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 0)
|
||||
s = append(s, 1)
|
||||
for _, v := range s {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
19
_test/map19.go
Normal file
19
_test/map19.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type cmap struct {
|
||||
servers map[int64]*server
|
||||
}
|
||||
|
||||
type server struct {
|
||||
cm *cmap
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := cmap{}
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[]}
|
||||
10
_test/map20.go
Normal file
10
_test/map20.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a = map[int]bool{1: true, 2: true}
|
||||
|
||||
func main() {
|
||||
println(a[1] && true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
15
_test/map21.go
Normal file
15
_test/map21.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
var ok bool
|
||||
if _, ok = m[1]; ok {
|
||||
println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
14
_test/map22.go
Normal file
14
_test/map22.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s string
|
||||
s, _ = m[1]
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
13
_test/map23.go
Normal file
13
_test/map23.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
_, _ = m[1]
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
16
_test/method29.go
Normal file
16
_test/method29.go
Normal 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
20
_test/method30.go
Normal 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
|
||||
29
_test/method31.go
Normal file
29
_test/method31.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var db dbWrapper
|
||||
|
||||
type dbWrapper struct {
|
||||
DB *cmap
|
||||
}
|
||||
|
||||
func (d *dbWrapper) get() *cmap {
|
||||
return d.DB
|
||||
}
|
||||
|
||||
type cmap struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *cmap) f() {
|
||||
fmt.Println("in f, c", c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
db.DB = &cmap{name: "test"}
|
||||
db.get().f()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in f, c &{test}
|
||||
12
_test/nil1.go
Normal file
12
_test/nil1.go
Normal 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
9
_test/or0.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := false
|
||||
println(c || !c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
9
_test/or1.go
Normal file
9
_test/or1.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := false
|
||||
println(!c || c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
10
_test/or2.go
Normal file
10
_test/or2.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a = false || true
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
26
_test/primes.go
Normal file
26
_test/primes.go
Normal 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
|
||||
10
_test/ptr8.go
Normal file
10
_test/ptr8.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a = func() *bool { b := true; return &b }()
|
||||
|
||||
func main() {
|
||||
println(*a && true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
14
_test/struct31.go
Normal file
14
_test/struct31.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
bool
|
||||
}
|
||||
|
||||
var t = T{true}
|
||||
|
||||
func main() {
|
||||
println(t.bool && true)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
39
_test/struct32.go
Normal file
39
_test/struct32.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
type T2 struct {
|
||||
t1 T1
|
||||
}
|
||||
|
||||
func (t2 *T2) f() {
|
||||
info := t2.t1.info("foo")
|
||||
println(info.F1())
|
||||
}
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func main() {
|
||||
t := &T2{T1{
|
||||
"bar", func(s string) T0 { return t0 },
|
||||
}}
|
||||
|
||||
println("hello")
|
||||
println(t.t1.info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// in F1
|
||||
// true
|
||||
34
_test/struct33.go
Normal file
34
_test/struct33.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func look(s string) T0 { println("in look"); return t0 }
|
||||
|
||||
var table = []*T1{{
|
||||
name: "bar",
|
||||
info: look,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
info := table[0].info
|
||||
println(info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in look
|
||||
// in F1
|
||||
// true
|
||||
16
_test/struct34.go
Normal file
16
_test/struct34.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(*T)
|
||||
}
|
||||
|
||||
func f1(t *T) { t.f = f2 }
|
||||
|
||||
func f2(t *T) { t.f = f1 }
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
16
_test/struct35.go
Normal file
16
_test/struct35.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(*T)
|
||||
}
|
||||
|
||||
func f1(t *T) { t.f = f1 }
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
f1(t)
|
||||
println(t.f != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
21
_test/struct36.go
Normal file
21
_test/struct36.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s S
|
||||
if _, err := s.Get("url"); err != nil {
|
||||
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
17
_test/struct37.go
Normal file
17
_test/struct37.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MyHttpClient struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := new(MyHttpClient)
|
||||
c.Client = new(http.Client)
|
||||
_, err := c.Get("url")
|
||||
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
|
||||
}
|
||||
15
_test/switch23.go
Normal file
15
_test/switch23.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func getType() string { return "T1" }
|
||||
|
||||
func main() {
|
||||
switch getType() {
|
||||
case "T1":
|
||||
println("T1")
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// T1
|
||||
16
_test/switch24.go
Normal file
16
_test/switch24.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a + 2 {
|
||||
case 5:
|
||||
println(5)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// bye
|
||||
18
_test/switch25.go
Normal file
18
_test/switch25.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 2
|
||||
switch {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
println(2)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// bye
|
||||
18
_test/switch26.go
Normal file
18
_test/switch26.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 1
|
||||
switch a := 2; {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
println(2)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 1
|
||||
9
_test/type15.go
Normal file
9
_test/type15.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
err := error(nil)
|
||||
println(err == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
11
_test/type16.go
Normal file
11
_test/type16.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := uint8(15) ^ byte(0)
|
||||
fmt.Printf("%T %v\n", a, a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// uint8 15
|
||||
11
_test/type17.go
Normal file
11
_test/type17.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := int32(15) ^ rune(0)
|
||||
fmt.Printf("%T %v\n", a, a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// int32 15
|
||||
20
_test/type18.go
Normal file
20
_test/type18.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
size int
|
||||
}
|
||||
|
||||
var table = []*T{{
|
||||
name: "foo",
|
||||
size: 2,
|
||||
}}
|
||||
|
||||
var s = table[0].size
|
||||
|
||||
func main() {
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
21
_test/type19.go
Normal file
21
_test/type19.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
size int
|
||||
}
|
||||
|
||||
var table = map[int]*T{
|
||||
0: {
|
||||
name: "foo",
|
||||
size: 2,
|
||||
}}
|
||||
|
||||
var s = table[0].size
|
||||
|
||||
func main() {
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
11
_test/var10.go
Normal file
11
_test/var10.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
var _ = true
|
||||
var _ = "hello"
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
10
_test/var11.go
Normal file
10
_test/var11.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
var a, _, _, b = 1, true, "foo", 2
|
||||
|
||||
func main() {
|
||||
println(a, b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1 2
|
||||
@@ -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
|
||||
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
|
||||
can be invoked directly from the shell.
|
||||
|
||||
@@ -47,8 +47,10 @@ import (
|
||||
func main() {
|
||||
var interactive bool
|
||||
var tags string
|
||||
var cmd string
|
||||
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
flag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
|
||||
fmt.Println("Options:")
|
||||
@@ -62,31 +64,41 @@ func main() {
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
|
||||
if len(args) > 0 {
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
os.Args = os.Args[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
if cmd != `` {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
log.Fatal("Could not read file: ", args[0])
|
||||
}
|
||||
|
||||
s := string(b)
|
||||
if s[:2] == "#!" {
|
||||
// Allow executable go scripts, but fix them prior to parse
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
}
|
||||
|
||||
i.Name = args[0]
|
||||
if _, err := i.Eval(s); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
if interactive {
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == `` {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
os.Args = os.Args[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
b, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
log.Fatal("Could not read file: ", args[0])
|
||||
}
|
||||
|
||||
s := string(b)
|
||||
if s[:2] == "#!" {
|
||||
// Allow executable go scripts, but fix them prior to parse
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
}
|
||||
|
||||
i.Name = args[0]
|
||||
if _, err := i.Eval(s); err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
}
|
||||
}
|
||||
|
||||
if interactive {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
|
||||
}
|
||||
}
|
||||
{{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}}
|
||||
func {{$name}}(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
@@ -793,6 +817,7 @@ type Op struct {
|
||||
Float bool // true if operator applies to float
|
||||
Complex bool // true if operator applies to complex
|
||||
Shift bool // true if operator is a shift operation
|
||||
Bool bool // true if operator applies to bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -805,17 +830,17 @@ func main() {
|
||||
b := &bytes.Buffer{}
|
||||
data := map[string]interface{}{
|
||||
"Arithmetic": map[string]Op{
|
||||
"add": {"+", true, true, true, false},
|
||||
"sub": {"-", false, true, true, false},
|
||||
"mul": {"*", false, true, true, false},
|
||||
"quo": {"/", false, true, true, false},
|
||||
"rem": {"%", false, false, false, false},
|
||||
"shl": {"<<", false, false, false, true},
|
||||
"shr": {">>", false, false, false, true},
|
||||
"and": {"&", false, false, false, false},
|
||||
"or": {"|", false, false, false, false},
|
||||
"xor": {"^", false, false, false, false},
|
||||
"andNot": {"&^", false, false, false, false},
|
||||
"add": {"+", true, true, true, false, false},
|
||||
"sub": {"-", false, true, true, false, false},
|
||||
"mul": {"*", false, true, true, false, false},
|
||||
"quo": {"/", false, true, true, false, false},
|
||||
"rem": {"%", false, false, false, false, false},
|
||||
"shl": {"<<", false, false, false, true, false},
|
||||
"shr": {">>", false, false, false, true, false},
|
||||
"and": {"&", false, false, false, false, false},
|
||||
"or": {"|", false, false, false, false, false},
|
||||
"xor": {"^", false, false, false, false, false},
|
||||
"andNot": {"&^", false, false, false, false, false},
|
||||
},
|
||||
"IncDec": map[string]Op{
|
||||
"inc": {Name: "+"},
|
||||
@@ -829,6 +854,12 @@ func main() {
|
||||
"lowerEqual": {Name: "<=", Complex: false},
|
||||
"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 {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -75,8 +75,6 @@ const (
|
||||
parenExpr
|
||||
rangeStmt
|
||||
returnStmt
|
||||
rvalueExpr
|
||||
rtypeExpr
|
||||
selectStmt
|
||||
selectorExpr
|
||||
selectorImport
|
||||
@@ -154,8 +152,6 @@ var kinds = [...]string{
|
||||
parenExpr: "parenExpr",
|
||||
rangeStmt: "rangeStmt",
|
||||
returnStmt: "returnStmt",
|
||||
rvalueExpr: "rvalueExpr",
|
||||
rtypeExpr: "rtypeExpr",
|
||||
selectStmt: "selectStmt",
|
||||
selectorExpr: "selectorExpr",
|
||||
selectorImport: "selectorImport",
|
||||
@@ -199,11 +195,11 @@ const (
|
||||
aAndAssign
|
||||
aAndNot
|
||||
aAndNotAssign
|
||||
aBitNot
|
||||
aCall
|
||||
aCase
|
||||
aCompositeLit
|
||||
aDec
|
||||
aDefer
|
||||
aEqual
|
||||
aGreater
|
||||
aGreaterEqual
|
||||
@@ -217,11 +213,12 @@ const (
|
||||
aMethod
|
||||
aMul
|
||||
aMulAssign
|
||||
aNegate
|
||||
aNeg
|
||||
aNot
|
||||
aNotEqual
|
||||
aOr
|
||||
aOrAssign
|
||||
aPos
|
||||
aQuo
|
||||
aQuoAssign
|
||||
aRange
|
||||
@@ -255,11 +252,11 @@ var actions = [...]string{
|
||||
aAndAssign: "&=",
|
||||
aAndNot: "&^",
|
||||
aAndNotAssign: "&^=",
|
||||
aBitNot: "^",
|
||||
aCall: "call",
|
||||
aCase: "case",
|
||||
aCompositeLit: "compositeLit",
|
||||
aDec: "--",
|
||||
aDefer: "defer",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGetFunc: "getFunc",
|
||||
@@ -271,11 +268,12 @@ var actions = [...]string{
|
||||
aMethod: "Method",
|
||||
aMul: "*",
|
||||
aMulAssign: "*=",
|
||||
aNegate: "-",
|
||||
aNeg: "-",
|
||||
aNot: "!",
|
||||
aNotEqual: "!=",
|
||||
aOr: "|",
|
||||
aOrAssign: "|=",
|
||||
aPos: "+",
|
||||
aQuo: "/",
|
||||
aQuoAssign: "/=",
|
||||
aRange: "range",
|
||||
@@ -569,7 +567,7 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
st.push(addChild(&root, anc, pos, declStmt, aNop), nod)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
st.push(addChild(&root, anc, pos, deferStmt, aDefer), nod)
|
||||
st.push(addChild(&root, anc, pos, deferStmt, aNop), nod)
|
||||
|
||||
case *ast.Ellipsis:
|
||||
st.push(addChild(&root, anc, pos, ellipsisExpr, aNop), nod)
|
||||
@@ -771,6 +769,8 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
var kind = unaryExpr
|
||||
var act action
|
||||
switch a.Op {
|
||||
case token.ADD:
|
||||
act = aPos
|
||||
case token.AND:
|
||||
kind = addressExpr
|
||||
act = aAddr
|
||||
@@ -779,7 +779,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
case token.NOT:
|
||||
act = aNot
|
||||
case token.SUB:
|
||||
act = aNegate
|
||||
act = aNeg
|
||||
case token.XOR:
|
||||
act = aBitNot
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, kind, act), nod)
|
||||
|
||||
|
||||
258
interp/cfg.go
258
interp/cfg.go
@@ -10,7 +10,12 @@ import (
|
||||
)
|
||||
|
||||
// A cfgError represents an error during CFG build stage
|
||||
type cfgError error
|
||||
type cfgError struct {
|
||||
*node
|
||||
error
|
||||
}
|
||||
|
||||
func (c *cfgError) Error() string { return c.error.Error() }
|
||||
|
||||
var constOp = map[action]func(*node){
|
||||
aAdd: addConst,
|
||||
@@ -24,6 +29,10 @@ var constOp = map[action]func(*node){
|
||||
aShr: shrConst,
|
||||
aAndNot: andNotConst,
|
||||
aXor: xorConst,
|
||||
aNot: notConst,
|
||||
aBitNot: bitNotConst,
|
||||
aNeg: negConst,
|
||||
aPos: posConst,
|
||||
}
|
||||
|
||||
var constBltn = map[string]func(*node){
|
||||
@@ -38,9 +47,8 @@ var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run
|
||||
func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
sc, pkgName := interp.initScopePkg(root)
|
||||
var loop, loopRestart *node
|
||||
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
var initNodes []*node
|
||||
var iotaValue int
|
||||
var err error
|
||||
@@ -228,16 +236,22 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
// Propagate type to children, to handle implicit types
|
||||
for _, c := range n.child {
|
||||
c.typ = n.typ
|
||||
switch c.kind {
|
||||
case binaryExpr, unaryExpr:
|
||||
// Do not attempt to propagate composite type to operator expressions,
|
||||
// it breaks constant folding.
|
||||
default:
|
||||
c.typ = n.typ
|
||||
}
|
||||
}
|
||||
|
||||
case forStmt0, forRangeStmt:
|
||||
loop, loopRestart = n, n.child[0]
|
||||
sc = sc.pushBloc()
|
||||
sc.loop, sc.loopRestart = n, n.child[0]
|
||||
|
||||
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
|
||||
loop, loopRestart = n, n.lastChild()
|
||||
sc = sc.pushBloc()
|
||||
sc.loop, sc.loopRestart = n, n.lastChild()
|
||||
|
||||
case funcLit:
|
||||
n.typ = nil // to force nodeType to recompute the type
|
||||
@@ -309,7 +323,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
c[i], c[l] = c[l], c[i]
|
||||
}
|
||||
sc = sc.pushBloc()
|
||||
loop = n
|
||||
sc.loop = n
|
||||
|
||||
case importSpec:
|
||||
var name, ipath string
|
||||
@@ -407,7 +421,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if src.typ, err = nodeType(interp, sc, src); err != nil {
|
||||
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 {
|
||||
dest.typ.size = arrayTypeLen(src)
|
||||
@@ -416,7 +434,8 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if sc.global {
|
||||
// Do not overload existing symbols (defined in GTA) in global scope
|
||||
sym, _, _ = sc.lookup(dest.ident)
|
||||
} else {
|
||||
}
|
||||
if sym == nil {
|
||||
sym = &symbol{index: sc.add(dest.typ), kind: varSym, typ: dest.typ}
|
||||
sc.sym[dest.ident] = sym
|
||||
}
|
||||
@@ -445,7 +464,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
default:
|
||||
// Detect invalid float truncate
|
||||
// Detect invalid float truncate.
|
||||
if isInt(t0) && isFloat(t1) {
|
||||
err = src.cfgErrorf("invalid float truncate")
|
||||
return
|
||||
@@ -456,7 +475,8 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
switch {
|
||||
case n.action == aAssign && src.action == aCall:
|
||||
case n.action == aAssign && src.action == aCall && dest.typ.cat != interfaceT:
|
||||
// Call action may perform the assignment directly.
|
||||
n.gen = nop
|
||||
src.level = level
|
||||
src.findex = dest.findex
|
||||
@@ -464,7 +484,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
src.typ = dest.typ
|
||||
}
|
||||
case n.action == aAssign && src.action == aRecv:
|
||||
// Assign by reading from a receiving channel
|
||||
// Assign by reading from a receiving channel.
|
||||
n.gen = nop
|
||||
src.findex = dest.findex // Set recv address to LHS
|
||||
dest.typ = src.typ
|
||||
@@ -473,16 +493,16 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case src.kind == basicLit:
|
||||
// TODO: perform constant folding and propagation here
|
||||
// TODO: perform constant folding and propagation here.
|
||||
switch {
|
||||
case dest.typ.cat == interfaceT:
|
||||
case isComplex(dest.typ.TypeOf()):
|
||||
// value set in genValue
|
||||
// Value set in genValue.
|
||||
case !src.rval.IsValid():
|
||||
// Assign to nil
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
default:
|
||||
// Convert literal value to destination type
|
||||
// Convert literal value to destination type.
|
||||
src.rval = src.rval.Convert(dest.typ.TypeOf())
|
||||
src.typ = dest.typ
|
||||
}
|
||||
@@ -496,9 +516,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if isMapEntry(dest) {
|
||||
dest.gen = nop // skip getIndexMap
|
||||
}
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
iotaValue++
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
iotaValue++
|
||||
}
|
||||
}
|
||||
|
||||
case incDecStmt:
|
||||
@@ -694,14 +715,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
n.tnext = loop
|
||||
n.tnext = sc.loop
|
||||
}
|
||||
|
||||
case continueStmt:
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
n.tnext = loopRestart
|
||||
n.tnext = sc.loopRestart
|
||||
}
|
||||
|
||||
case gotoStmt:
|
||||
@@ -736,7 +757,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
|
||||
err = n.cfgErrorf("truncated to integer")
|
||||
}
|
||||
if isInterface(n.child[0].typ) {
|
||||
if isInterface(n.child[0].typ) && !n.child[1].isNil() {
|
||||
// Convert to interface: just check that all required methods are defined by concrete type.
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
if !c1.typ.implements(c0.typ) {
|
||||
@@ -756,10 +777,20 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
case isBinCall(n):
|
||||
n.gen = callBin
|
||||
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
if funcType := n.child[0].typ.val; funcType != nil {
|
||||
// Use the original unwrapped function type, to allow future field and
|
||||
// methods resolutions, otherwise impossible on the opaque bin type.
|
||||
n.typ = funcType.ret[0]
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < len(funcType.ret); i++ {
|
||||
sc.add(funcType.ret[i])
|
||||
}
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -809,7 +840,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
// TODO: Check that composite literal expr matches corresponding type
|
||||
n.gen = compositeGenerator(n)
|
||||
n.gen = compositeGenerator(n, sc)
|
||||
|
||||
case fallthroughtStmt:
|
||||
if n.anc.kind != caseBody {
|
||||
@@ -825,7 +856,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
body := n.child[0]
|
||||
n.start = body.start
|
||||
body.tnext = n.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt1: // for cond {}
|
||||
@@ -837,7 +867,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt2: // for init; cond; {}
|
||||
@@ -850,7 +879,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3: // for ; cond; post {}
|
||||
@@ -863,7 +891,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
cond.fnext = n
|
||||
body.tnext = post.start
|
||||
post.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3a: // for int; ; post {}
|
||||
@@ -872,7 +899,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
init.tnext = body.start
|
||||
body.tnext = post.start
|
||||
post.tnext = body.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt4: // for init; cond; post {}
|
||||
@@ -886,11 +912,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
cond.fnext = n
|
||||
body.tnext = post.start
|
||||
post.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forRangeStmt:
|
||||
loop, loopRestart = nil, nil
|
||||
n.start = n.child[0].start
|
||||
n.child[0].fnext = n
|
||||
sc = sc.pop()
|
||||
@@ -901,10 +925,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if !isMethod(n) {
|
||||
interp.scopes[pkgName].sym[funcName].index = -1 // to force value to n.val
|
||||
interp.scopes[pkgName].sym[funcName].typ = n.typ
|
||||
interp.scopes[pkgName].sym[funcName].kind = funcSym
|
||||
interp.scopes[pkgName].sym[funcName].node = n
|
||||
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
|
||||
interp.scopes[pkgID].sym[funcName].typ = n.typ
|
||||
interp.scopes[pkgID].sym[funcName].kind = funcSym
|
||||
interp.scopes[pkgID].sym[funcName].node = n
|
||||
}
|
||||
|
||||
case funcLit:
|
||||
@@ -912,13 +936,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
sc = sc.pop()
|
||||
err = genRun(n)
|
||||
|
||||
case goStmt:
|
||||
case deferStmt, goStmt:
|
||||
wireChild(n)
|
||||
|
||||
case identExpr:
|
||||
if isKey(n) || isNewDefine(n, sc) {
|
||||
break
|
||||
} else if sym, level, ok := sc.lookup(n.ident); ok {
|
||||
}
|
||||
if sym, level, ok := sc.lookup(n.ident); ok {
|
||||
// Found symbol, populate node info
|
||||
n.typ, n.findex, n.level = sym.typ, sym.index, level
|
||||
if n.findex < 0 {
|
||||
@@ -935,11 +960,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
case n.ident == "nil":
|
||||
n.kind = basicLit
|
||||
case sym.kind == binSym:
|
||||
if sym.rval.IsValid() {
|
||||
n.kind = rvalueExpr
|
||||
} else {
|
||||
n.kind = rtypeExpr
|
||||
}
|
||||
n.typ = sym.typ
|
||||
n.rval = sym.rval
|
||||
case sym.kind == bltnSym:
|
||||
@@ -1019,6 +1039,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.child[1].tnext = n
|
||||
n.typ = n.child[0].typ
|
||||
n.findex = sc.add(n.typ)
|
||||
if n.start.action == aNop {
|
||||
n.start.gen = branch
|
||||
}
|
||||
|
||||
case lorExpr:
|
||||
n.start = n.child[0].start
|
||||
@@ -1027,6 +1050,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.child[1].tnext = n
|
||||
n.typ = n.child[0].typ
|
||||
n.findex = sc.add(n.typ)
|
||||
if n.start.action == aNop {
|
||||
n.start.gen = branch
|
||||
}
|
||||
|
||||
case parenExpr:
|
||||
wireChild(n)
|
||||
@@ -1095,7 +1121,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
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:
|
||||
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
@@ -1149,10 +1175,8 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
pkg := n.child[0].sym.typ.path
|
||||
if s, ok := interp.binPkg[pkg][name]; ok {
|
||||
if isBinType(s) {
|
||||
n.kind = rtypeExpr
|
||||
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
|
||||
} else {
|
||||
n.kind = rvalueExpr
|
||||
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
|
||||
n.rval = s
|
||||
}
|
||||
@@ -1169,6 +1193,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.gen = nop
|
||||
n.typ = sym.typ
|
||||
n.sym = sym
|
||||
n.rval = sym.rval
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
|
||||
}
|
||||
@@ -1189,11 +1214,12 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
if isPtr {
|
||||
if isPtr && n.typ.fieldSeq(lind).cat != ptrT {
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
} else {
|
||||
n.gen = getIndexSeqMethod
|
||||
}
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = &itype{cat: valueT, rtype: m.Type}
|
||||
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
||||
@@ -1210,7 +1236,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
default:
|
||||
n.gen = getIndexSeq
|
||||
@@ -1218,7 +1244,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
}
|
||||
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
|
||||
@@ -1301,16 +1327,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
c := clauses[l-1]
|
||||
c.tnext = c.lastChild().start
|
||||
if n.child[0].action == aAssign &&
|
||||
(n.child[0].child[0].kind != typeAssertExpr || len(n.child[0].child[0].child) > 1) {
|
||||
// switch init statement is defined
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
} else {
|
||||
n.start = sbn.start
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
sc = sc.pop()
|
||||
loop = nil
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sbn := n.lastChild() // switch block node
|
||||
@@ -1336,18 +1355,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
// If last case body statement is a fallthrough, then jump to next case body
|
||||
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
} else {
|
||||
body.tnext = n
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
if n.child[0].action == aAssign {
|
||||
// switch init statement is defined
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
} else {
|
||||
n.start = sbn.start
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
sc = sc.pop()
|
||||
loop = nil
|
||||
|
||||
case typeAssertExpr:
|
||||
if len(n.child) > 1 {
|
||||
@@ -1375,16 +1390,60 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
case unaryExpr:
|
||||
wireChild(n)
|
||||
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
|
||||
if n.typ.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
|
||||
} else {
|
||||
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)
|
||||
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:
|
||||
n.gen = reset
|
||||
@@ -1395,8 +1454,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[:l] {
|
||||
index := sc.add(n.typ)
|
||||
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
|
||||
var index int
|
||||
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.findex = index
|
||||
}
|
||||
@@ -1482,13 +1547,13 @@ func childPos(n *node) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (n *node) cfgErrorf(format string, a ...interface{}) cfgError {
|
||||
func (n *node) cfgErrorf(format string, a ...interface{}) *cfgError {
|
||||
a = append([]interface{}{n.interp.fset.Position(n.pos)}, a...)
|
||||
return cfgError(fmt.Errorf("%s: "+format, a...))
|
||||
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
|
||||
}
|
||||
|
||||
func genRun(nod *node) error {
|
||||
var err cfgError
|
||||
var err error
|
||||
|
||||
nod.Walk(func(n *node) bool {
|
||||
if err != nil {
|
||||
@@ -1526,7 +1591,7 @@ func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect
|
||||
// isType returns true if node refers to a type definition, false otherwise
|
||||
func (n *node) isType(sc *scope) bool {
|
||||
switch n.kind {
|
||||
case arrayType, chanType, funcType, interfaceType, mapType, structType, rtypeExpr:
|
||||
case arrayType, chanType, funcType, interfaceType, mapType, structType:
|
||||
return true
|
||||
case parenExpr, starExpr:
|
||||
if len(n.child) == 1 {
|
||||
@@ -1641,6 +1706,9 @@ func (n *node) isNatural() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isNil returns true if node is a literal nil value, false otherwise
|
||||
func (n *node) isNil() bool { return n.kind == basicLit && !n.rval.IsValid() }
|
||||
|
||||
// fieldType returns the nth parameter field node (type) of a fieldList node
|
||||
func (n *node) fieldType(m int) *node {
|
||||
k := 0
|
||||
@@ -1675,6 +1743,10 @@ func isKey(n *node) bool {
|
||||
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
|
||||
}
|
||||
|
||||
func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition
|
||||
func isNewDefine(n *node, sc *scope) bool {
|
||||
if n.ident == "_" {
|
||||
@@ -1739,13 +1811,6 @@ func getExec(n *node) bltn {
|
||||
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
|
||||
// from the entry point (first node to exec).
|
||||
func setExec(n *node) {
|
||||
@@ -1796,21 +1861,32 @@ func gotoLabel(s *symbol) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
func compositeGenerator(n *node, sc *scope) (gen bltnGenerator) {
|
||||
switch n.typ.cat {
|
||||
case aliasT, ptrT:
|
||||
n.typ.val.untyped = n.typ.untyped
|
||||
n.typ = n.typ.val
|
||||
gen = compositeGenerator(n)
|
||||
gen = compositeGenerator(n, sc)
|
||||
case arrayT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
gen = mapLit
|
||||
case structT:
|
||||
if len(n.child) > 0 && n.lastChild().kind == keyValueExpr {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeLit
|
||||
switch {
|
||||
case len(n.child) == 0:
|
||||
gen = compositeLitNotype
|
||||
case n.lastChild().kind == keyValueExpr:
|
||||
if n.child[0].isType(sc) {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeSparseNotype
|
||||
}
|
||||
default:
|
||||
if n.child[0].isType(sc) {
|
||||
gen = compositeLit
|
||||
} else {
|
||||
gen = compositeLitNotype
|
||||
}
|
||||
}
|
||||
case valueT:
|
||||
switch k := n.typ.rtype.Kind(); k {
|
||||
@@ -1822,7 +1898,7 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
|
||||
}
|
||||
}
|
||||
return
|
||||
return gen
|
||||
}
|
||||
|
||||
// arrayTypeLen returns the node's array length. If the expression is an
|
||||
|
||||
@@ -4,6 +4,18 @@ Package interp provides a complete Go interpreter.
|
||||
For the Go language itself, refer to the official Go specification
|
||||
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 allow to control which files in imported source
|
||||
|
||||
@@ -38,7 +38,7 @@ func (n *node) astDot(out io.Writer, name string) {
|
||||
func (n *node) cfgDot(out io.Writer) {
|
||||
fmt.Fprintf(out, "digraph cfg {\n")
|
||||
n.Walk(nil, func(n *node) {
|
||||
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil {
|
||||
if n.kind == basicLit || n.tnext == nil {
|
||||
return
|
||||
}
|
||||
var label string
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
// variables and functions symbols at package level, prior to CFG.
|
||||
// All function bodies are skipped. GTA is necessary to handle out of
|
||||
// order declarations and multiple source files packages.
|
||||
func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
sc, _ := interp.initScopePkg(root)
|
||||
func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
var err error
|
||||
var iotaValue int
|
||||
var revisit []*node
|
||||
@@ -21,6 +21,12 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
switch n.kind {
|
||||
case constDecl:
|
||||
iotaValue = 0
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
// values which may be necessary in further declarations.
|
||||
if _, err = interp.cfg(n, pkgID); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
|
||||
case blockStmt:
|
||||
if n != root {
|
||||
@@ -44,6 +50,14 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
val := reflect.ValueOf(iotaValue)
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, pkgID); err2 != nil {
|
||||
// Constant value can not be computed yet.
|
||||
// Come back when child dependencies are known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
}
|
||||
typ := atyp
|
||||
if typ == nil {
|
||||
if typ, err = nodeType(interp, sc, src); err != nil {
|
||||
@@ -52,7 +66,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
val = src.rval
|
||||
}
|
||||
if typ.incomplete {
|
||||
// Come back when type is known
|
||||
// Come back when type is known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
@@ -60,7 +74,12 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
err = n.cfgErrorf("use of untyped nil")
|
||||
return false
|
||||
}
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
if typ.isBinMethod {
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
|
||||
}
|
||||
if sc.sym[dest.ident] == nil {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
iotaValue++
|
||||
@@ -77,6 +96,11 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
|
||||
return false
|
||||
}
|
||||
if n.typ.incomplete {
|
||||
// Come back when type is known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[:l] {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
|
||||
@@ -117,6 +141,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
// Add a function symbol in the package name space
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if n.typ.incomplete {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
return false
|
||||
|
||||
case importSpec:
|
||||
@@ -143,7 +170,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
default: // import symbols in package namespace
|
||||
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
|
||||
switch name {
|
||||
case "_": // no import of symbols
|
||||
@@ -194,3 +221,42 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
|
||||
}
|
||||
return revisit, err
|
||||
}
|
||||
|
||||
// gtaRetry (re)applies gta until all global constants and types are defined.
|
||||
func (interp *Interpreter) gtaRetry(nodes []*node, rpath, pkgID string) error {
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
list, err := interp.gta(n, rpath, pkgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revisit = append(revisit, list...)
|
||||
}
|
||||
|
||||
if len(revisit) == 0 || equalNodes(nodes, revisit) {
|
||||
break
|
||||
}
|
||||
|
||||
nodes = revisit
|
||||
revisit = []*node{}
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return revisit[0].cfgErrorf("constant definition loop")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// equalNodes returns true if two slices of nodes are identical.
|
||||
func equalNodes(a, b []*node) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, n := range a {
|
||||
if n != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -158,6 +160,24 @@ type _error struct {
|
||||
|
||||
func (w _error) Error() string { return w.WError() }
|
||||
|
||||
// Panic is an error recovered from a panic call in interpreted code.
|
||||
type Panic struct {
|
||||
// Value is the recovered value of a call to panic.
|
||||
Value interface{}
|
||||
|
||||
// Callers is the call stack obtained from the recover call.
|
||||
// It may be used as the parameter to runtime.CallersFrames.
|
||||
Callers []uintptr
|
||||
|
||||
// Stack is the call stack buffer for debug.
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
// TODO: Capture interpreter stack frames also and remove
|
||||
// fmt.Println(n.cfgErrorf("panic")) in runCfg.
|
||||
|
||||
func (e Panic) Error() string { return fmt.Sprint(e.Value) }
|
||||
|
||||
// Walk traverses AST n in depth first order, call cbin function
|
||||
// at node entry and cbout function at node exit.
|
||||
func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
|
||||
@@ -216,7 +236,7 @@ func initUniverse() *scope {
|
||||
sc := &scope{global: true, sym: map[string]*symbol{
|
||||
// predefined Go types
|
||||
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool"}},
|
||||
"byte": {kind: typeSym, typ: &itype{cat: byteT, name: "byte"}},
|
||||
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
|
||||
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64"}},
|
||||
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128"}},
|
||||
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error"}},
|
||||
@@ -228,7 +248,7 @@ func initUniverse() *scope {
|
||||
"int32": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
|
||||
"int64": {kind: typeSym, typ: &itype{cat: int64T, name: "int64"}},
|
||||
"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT}},
|
||||
"rune": {kind: typeSym, typ: &itype{cat: runeT, name: "rune"}},
|
||||
"rune": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
|
||||
"string": {kind: typeSym, typ: &itype{cat: stringT, name: "string"}},
|
||||
"uint": {kind: typeSym, typ: &itype{cat: uintT, name: "uint"}},
|
||||
"uint8": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
|
||||
@@ -283,7 +303,7 @@ func (interp *Interpreter) resizeFrame() {
|
||||
func (interp *Interpreter) main() *node {
|
||||
interp.mutex.RLock()
|
||||
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 nil
|
||||
@@ -291,10 +311,17 @@ func (interp *Interpreter) main() *node {
|
||||
|
||||
// Eval evaluates Go code represented as a string. It returns a map on
|
||||
// current interpreted package exported symbols
|
||||
func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
var res reflect.Value
|
||||
func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
var pc [64]uintptr // 64 frames should be enough.
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
}()
|
||||
|
||||
// Parse source to AST
|
||||
// Parse source to AST.
|
||||
pkgName, root, err := interp.ast(src, interp.Name)
|
||||
if err != nil || root == nil {
|
||||
return res, err
|
||||
@@ -307,19 +334,13 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Global type analysis
|
||||
revisit, err := interp.gta(root, pkgName)
|
||||
if err != nil {
|
||||
// Perform global types analysis.
|
||||
if err = interp.gtaRetry([]*node{root}, pkgName, interp.Name); err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, n := range revisit {
|
||||
if _, err = interp.gta(n, pkgName); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// Annotate AST with CFG infos
|
||||
initNodes, err := interp.cfg(root)
|
||||
initNodes, err := interp.cfg(root, interp.Name)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -336,7 +357,7 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
interp.mutex.Lock()
|
||||
if interp.universe.sym[pkgName] == nil {
|
||||
// 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.mutex.Unlock()
|
||||
@@ -446,12 +467,15 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
v, err := interp.EvalWithContext(ctx, src)
|
||||
signal.Reset()
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
switch e := err.(type) {
|
||||
case scanner.ErrorList:
|
||||
// Early failure in the scanner: the source is incomplete
|
||||
// and no AST could be produced, neither compiled / run.
|
||||
// Get one more line, and retry
|
||||
continue
|
||||
case Panic:
|
||||
fmt.Fprintln(out, e.Value)
|
||||
fmt.Fprintln(out, string(e.Stack))
|
||||
default:
|
||||
fmt.Fprintln(out, err)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) != ".go" ||
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
file.Name() == "export0.go" || // non-main package
|
||||
file.Name() == "for7.go" || // expect error
|
||||
@@ -138,6 +139,11 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp: "1:1: expected 'package', found println",
|
||||
expectedExec: "1:1: expected 'package', found println",
|
||||
},
|
||||
{
|
||||
fileName: "const9.go",
|
||||
expectedInterp: "5:2: constant definition loop",
|
||||
expectedExec: "5:2: constant definition loop",
|
||||
},
|
||||
{
|
||||
fileName: "if2.go",
|
||||
expectedInterp: "7:5: non-bool used as if condition",
|
||||
|
||||
@@ -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_IF", src: "1 << 1.0", res: "2"},
|
||||
{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_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
|
||||
{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: "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"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,11 +78,11 @@ func TestEvalBuiltin(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `a := []int{}; a = append(a, 1); a`, res: "[1]"},
|
||||
{src: `a := []int{1}; a = append(a, 2, 3); a`, res: "[1 2 3]"},
|
||||
{src: `a := []int{1}; b := []int{2, 3}; a = append(a, b...); a`, res: "[1 2 3]"},
|
||||
{src: `b := []int{1}; b = append(a, 2, 3); b`, res: "[1 2 3]"},
|
||||
{src: `c := []int{1}; d := []int{2, 3}; c = append(c, d...); c`, res: "[1 2 3]"},
|
||||
{src: `string(append([]byte("hello "), "world"...))`, res: "hello world"},
|
||||
{src: `a := "world"; string(append([]byte("hello "), a...))`, res: "hello world"},
|
||||
{src: `a := []byte("Hello"); copy(a, "world"); string(a)`, res: "world"},
|
||||
{src: `e := "world"; string(append([]byte("hello "), e...))`, res: "hello world"},
|
||||
{src: `f := []byte("Hello"); copy(f, "world"); string(f)`, res: "world"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -519,7 +525,11 @@ func eval(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
|
||||
t.Helper()
|
||||
res, err := i.Eval(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Logf("Error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -535,7 +545,11 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("got an error %v", err)
|
||||
t.Logf("got an error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%v", res) != expectedRes {
|
||||
|
||||
51
interp/op.go
51
interp/op.go
@@ -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) {
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
456
interp/run.go
456
interp/run.go
@@ -24,11 +24,11 @@ var builtin = [...]bltnGenerator{
|
||||
aAndAssign: andAssign,
|
||||
aAndNot: andNot,
|
||||
aAndNotAssign: andNotAssign,
|
||||
aBitNot: bitNot,
|
||||
aCall: call,
|
||||
aCase: _case,
|
||||
aCompositeLit: arrayLit,
|
||||
aDec: dec,
|
||||
aDefer: _defer,
|
||||
aEqual: equal,
|
||||
aGetFunc: getFunc,
|
||||
aGreater: greater,
|
||||
@@ -40,11 +40,12 @@ var builtin = [...]bltnGenerator{
|
||||
aLowerEqual: lowerEqual,
|
||||
aMul: mul,
|
||||
aMulAssign: mulAssign,
|
||||
aNegate: negate,
|
||||
aNeg: neg,
|
||||
aNot: not,
|
||||
aNotEqual: notEqual,
|
||||
aOr: or,
|
||||
aOrAssign: orAssign,
|
||||
aPos: pos,
|
||||
aQuo: quo,
|
||||
aQuoAssign: quoAssign,
|
||||
aRange: _range,
|
||||
@@ -214,7 +215,7 @@ func convert(n *node) {
|
||||
typ := n.child[0].typ.TypeOf()
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if c.kind == basicLit && !c.rval.IsValid() { // convert nil to type
|
||||
if c.isNil() { // convert nil to type
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ).Elem())
|
||||
return next
|
||||
@@ -262,7 +263,9 @@ func assign(n *node) {
|
||||
svalue[i] = genValueInterface(src)
|
||||
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
|
||||
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
|
||||
case dest.typ.cat == valueT && src.typ.cat == funcT:
|
||||
case src.typ.cat == funcT && dest.typ.cat == valueT:
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case src.typ.cat == funcT && isField(dest):
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case dest.typ.cat == funcT && src.typ.cat == valueT:
|
||||
svalue[i] = genValueNode(src)
|
||||
@@ -291,12 +294,17 @@ func assign(n *node) {
|
||||
}
|
||||
|
||||
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 {
|
||||
d(f).SetMapIndex(i(f), s(f))
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
d(f).Set(s(f))
|
||||
return next
|
||||
@@ -314,7 +322,6 @@ func assign(n *node) {
|
||||
default:
|
||||
t = typ.TypeOf()
|
||||
}
|
||||
//types[i] = n.child[sbase+i].typ.TypeOf()
|
||||
types[i] = t
|
||||
}
|
||||
|
||||
@@ -324,10 +331,16 @@ func assign(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
t := make([]reflect.Value, len(svalue))
|
||||
for i, s := range svalue {
|
||||
if n.child[i].ident == "_" {
|
||||
continue
|
||||
}
|
||||
t[i] = reflect.New(types[i]).Elem()
|
||||
t[i].Set(s(f))
|
||||
}
|
||||
for i, d := range dvalue {
|
||||
if n.child[i].ident == "_" {
|
||||
continue
|
||||
}
|
||||
if j := ivalue[i]; j != nil {
|
||||
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
|
||||
} else {
|
||||
@@ -340,6 +353,7 @@ func assign(n *node) {
|
||||
}
|
||||
|
||||
func not(n *node) {
|
||||
dest := genValue(n)
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
@@ -347,14 +361,15 @@ func not(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if !value(f).Bool() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetBool(!value(f).Bool())
|
||||
dest(f).SetBool(!value(f).Bool())
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -374,17 +389,18 @@ func addr(n *node) {
|
||||
func deref(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).Elem().Bool() {
|
||||
f.data[i] = value(f).Elem()
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem()
|
||||
return tnext
|
||||
@@ -466,7 +482,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if def, ok = n.val.(*node); !ok {
|
||||
return genValueAsFunctionWrapper(n)
|
||||
}
|
||||
setExec(def.child[3].start)
|
||||
start := def.child[3].start
|
||||
numRet := len(def.typ.ret)
|
||||
var rcvr func(*frame) reflect.Value
|
||||
@@ -580,47 +595,6 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func _defer(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
values := make([]func(*frame) reflect.Value, len(n.child[0].child))
|
||||
var method func(*frame) reflect.Value
|
||||
|
||||
for i, c := range n.child[0].child {
|
||||
if c.typ.cat == funcT {
|
||||
values[i] = genFunctionWrapper(c)
|
||||
} else {
|
||||
if c.recv != nil {
|
||||
// defer a method on a binary obj
|
||||
mi := c.val.(int)
|
||||
m := genValue(c.child[0])
|
||||
method = func(f *frame) reflect.Value { return m(f).Method(mi) }
|
||||
}
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
|
||||
if method != nil {
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values))
|
||||
val[0] = method(f)
|
||||
for i, v := range values[1:] {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
val[i] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func call(n *node) {
|
||||
goroutine := n.anc.kind == goStmt
|
||||
var method bool
|
||||
@@ -658,7 +632,7 @@ func call(n *node) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
}
|
||||
default:
|
||||
if c.kind == basicLit {
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
var argType reflect.Type
|
||||
if variadic >= 0 && i >= variadic {
|
||||
argType = n.child[0].typ.arg[variadic].val.TypeOf()
|
||||
@@ -692,20 +666,40 @@ func call(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
if n.anc.kind == deferStmt {
|
||||
// Store function call in frame for deferred execution.
|
||||
value = genFunctionWrapper(n.child[0])
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values)+1)
|
||||
val[0] = value(f)
|
||||
for i, v := range values {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
def := value(f).Interface().(*node)
|
||||
var def *node
|
||||
var ok bool
|
||||
bf := value(f)
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
}
|
||||
|
||||
// Call bin func if defined
|
||||
if def.rval.IsValid() {
|
||||
if bf.IsValid() {
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
if goroutine {
|
||||
go def.rval.Call(in)
|
||||
go bf.Call(in)
|
||||
return tnext
|
||||
}
|
||||
out := def.rval.Call(in)
|
||||
out := bf.Call(in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
@@ -808,7 +802,7 @@ func pindex(i, variadic int) int {
|
||||
return variadic
|
||||
}
|
||||
|
||||
// Call a function from a bin import, accessible through reflect
|
||||
// Callbin calls a function from a bin import, accessible through reflect.
|
||||
func callBin(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
fnext := getExec(n.fnext)
|
||||
@@ -820,10 +814,12 @@ func callBin(n *node) {
|
||||
if funcType.IsVariadic() {
|
||||
variadic = funcType.NumIn() - 1
|
||||
}
|
||||
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
|
||||
// A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types.
|
||||
rcvrOffset := 0
|
||||
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
|
||||
rcvrOffset = 1
|
||||
if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) {
|
||||
if funcType.NumIn() > len(child) {
|
||||
rcvrOffset = 1
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range child {
|
||||
@@ -843,7 +839,7 @@ func callBin(n *node) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
}
|
||||
default:
|
||||
if c.kind == basicLit {
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
// Convert literal value (untyped) to function argument type (if not an interface{})
|
||||
var argType reflect.Type
|
||||
if variadic >= 0 && i >= variadic {
|
||||
@@ -862,7 +858,6 @@ func callBin(n *node) {
|
||||
case interfaceT:
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
default:
|
||||
//values = append(values, genValue(c))
|
||||
values = append(values, genInterfaceWrapper(c, defType))
|
||||
}
|
||||
}
|
||||
@@ -870,8 +865,19 @@ func callBin(n *node) {
|
||||
l := len(values)
|
||||
|
||||
switch {
|
||||
case n.anc.kind == deferStmt:
|
||||
// Store function call in frame for deferred execution.
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, l+1)
|
||||
val[0] = value(f)
|
||||
for i, v := range values {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
case n.anc.kind == goStmt:
|
||||
// Execute function in a goroutine, discard results
|
||||
// Execute function in a goroutine, discard results.
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
@@ -881,7 +887,7 @@ func callBin(n *node) {
|
||||
return tnext
|
||||
}
|
||||
case fnext != nil:
|
||||
// Handle branching according to boolean result
|
||||
// Handle branching according to boolean result.
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
@@ -894,8 +900,10 @@ func callBin(n *node) {
|
||||
return fnext
|
||||
}
|
||||
default:
|
||||
switch n.anc.kind {
|
||||
case defineStmt, assignStmt, defineXStmt, assignXStmt:
|
||||
switch n.anc.action {
|
||||
case aAssign, aAssignX:
|
||||
// The function call is part of an assign expression, we write results direcly
|
||||
// to assigned location, to avoid an additional assign operation.
|
||||
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
@@ -962,21 +970,21 @@ func getIndexBinPtrMethod(n *node) {
|
||||
func getIndexArray(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
value0 := genValueArray(n.child[0]) // array
|
||||
i := n.findex
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant array index
|
||||
ai := int(vInt(n.child[1].rval))
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value0(f).Index(ai).Bool() {
|
||||
f.data[i] = value0(f).Index(ai)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Can not use .Set due to constraint of being able to assign an array element
|
||||
f.data[i] = value0(f).Index(ai)
|
||||
return tnext
|
||||
}
|
||||
@@ -988,16 +996,15 @@ func getIndexArray(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, vi := value1(f)
|
||||
if value0(f).Index(int(vi)).Bool() {
|
||||
f.data[i] = value0(f).Index(int(vi))
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, vi := value1(f)
|
||||
// Can not use .Set due to constraint of being able to assign an array element
|
||||
f.data[i] = value0(f).Index(int(vi))
|
||||
return tnext
|
||||
}
|
||||
@@ -1010,20 +1017,33 @@ func getIndexMap(n *node) {
|
||||
dest := genValue(n)
|
||||
value0 := genValue(n.child[0]) // map
|
||||
tnext := getExec(n.tnext)
|
||||
z := reflect.New(n.child[0].typ.TypeOf().Elem()).Elem()
|
||||
z := reflect.New(n.child[0].typ.frameType().Elem()).Elem()
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
mi := n.child[1].rval
|
||||
|
||||
if n.fnext != nil {
|
||||
switch {
|
||||
case n.fnext != nil:
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() && v.Bool() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).Set(z)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
case n.typ.cat == interfaceT:
|
||||
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
} else {
|
||||
dest(f).Set(z)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
@@ -1036,15 +1056,28 @@ func getIndexMap(n *node) {
|
||||
} else {
|
||||
value1 := genValue(n.child[1]) // map index
|
||||
|
||||
if n.fnext != nil {
|
||||
switch {
|
||||
case n.fnext != nil:
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() && v.Bool() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).Set(z)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
case n.typ.cat == interfaceT:
|
||||
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
} else {
|
||||
dest(f).Set(z)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
@@ -1063,26 +1096,77 @@ func getIndexMap2(n *node) {
|
||||
value0 := genValue(n.child[0]) // map
|
||||
value2 := genValue(n.anc.child[1]) // status
|
||||
next := getExec(n.tnext)
|
||||
typ := n.anc.child[0].typ
|
||||
doValue := n.anc.child[0].ident != "_"
|
||||
doStatus := n.anc.child[1].ident != "_"
|
||||
|
||||
if !doValue && !doStatus {
|
||||
nop(n)
|
||||
return
|
||||
}
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
mi := n.child[1].rval
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
switch {
|
||||
case !doValue:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
value1 := genValue(n.child[1]) // map index
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
switch {
|
||||
case !doValue:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1139,17 +1223,28 @@ func getIndexSeq(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
|
||||
// Note:
|
||||
// Here we have to store the result using
|
||||
// f.data[i] = value(...)
|
||||
// instead of normal
|
||||
// dest(f).Set(value(...)
|
||||
// because the value returned by FieldByIndex() must be preserved
|
||||
// for possible future Set operations on the struct field (avoid a
|
||||
// dereference from Set, resulting in setting a copy of the
|
||||
// original field).
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).FieldByIndex(index).Bool() {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
return tnext
|
||||
@@ -1167,17 +1262,18 @@ func getPtrIndexSeq(n *node) {
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
i := n.findex
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).Elem().FieldByIndex(index).Bool() {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
return tnext
|
||||
@@ -1189,17 +1285,38 @@ func getIndexSeqField(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
i := n.findex
|
||||
next := getExec(n.tnext)
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
return next
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
return next
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1246,7 +1363,7 @@ func getIndexSeqMethod(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func negate(n *node) {
|
||||
func neg(n *node) {
|
||||
dest := genValue(n)
|
||||
value := genValue(n.child[0])
|
||||
next := getExec(n.tnext)
|
||||
@@ -1270,6 +1387,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) {
|
||||
value0 := genValue(n.child[0])
|
||||
value1 := genValue(n.child[1])
|
||||
@@ -1414,7 +1562,7 @@ func arrayLit(n *node) {
|
||||
if c.kind == keyValueExpr {
|
||||
convertLiteralValue(c.child[1], rtype)
|
||||
values[i] = genValue(c.child[1])
|
||||
index[i] = int(c.child[0].rval.Int())
|
||||
index[i] = int(vInt(c.child[0].rval))
|
||||
} else {
|
||||
convertLiteralValue(c, rtype)
|
||||
values[i] = genValue(c)
|
||||
@@ -1545,12 +1693,12 @@ func destType(n *node) *itype {
|
||||
}
|
||||
}
|
||||
|
||||
// compositeLit creates and populates a struct object
|
||||
func compositeLit(n *node) {
|
||||
// doCompositeLit creates and populates a struct object
|
||||
func doCompositeLit(n *node, hasType bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
destInterface := destType(n).cat == interfaceT
|
||||
@@ -1582,12 +1730,15 @@ func compositeLit(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// compositeSparse creates a struct Object, filling fields from sparse key-values
|
||||
func compositeSparse(n *node) {
|
||||
func compositeLit(n *node) { doCompositeLit(n, true) }
|
||||
func compositeLitNotype(n *node) { doCompositeLit(n, false) }
|
||||
|
||||
// doCompositeSparse creates a struct Object, filling fields from sparse key-values
|
||||
func doCompositeSparse(n *node, hasType bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
|
||||
@@ -1617,6 +1768,9 @@ func compositeSparse(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeSparse(n *node) { doCompositeSparse(n, true) }
|
||||
func compositeSparseNotype(n *node) { doCompositeSparse(n, false) }
|
||||
|
||||
func empty(n *node) {}
|
||||
|
||||
var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
|
||||
@@ -1702,14 +1856,26 @@ func rangeMap(n *node) {
|
||||
if len(n.child) == 4 {
|
||||
index1 := n.child[1].findex // map value location in frame
|
||||
value = genValue(n.child[2]) // map
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
if n.child[1].typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value().Elem())
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value())
|
||||
return tnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value())
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
value = genValue(n.child[1]) // map
|
||||
@@ -1877,6 +2043,8 @@ func _append(n *node) {
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
values[i] = genValueInterface(arg)
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueInterfacePtr(arg)
|
||||
case arg.typ.untyped:
|
||||
@@ -1897,6 +2065,8 @@ func _append(n *node) {
|
||||
} else {
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueInterfacePtr(n.child[2])
|
||||
case n.child[2].typ.untyped:
|
||||
@@ -1946,7 +2116,7 @@ func _close(n *node) {
|
||||
}
|
||||
|
||||
func _complex(n *node) {
|
||||
i := n.findex
|
||||
dest := genValue(n)
|
||||
c1, c2 := n.child[1], n.child[2]
|
||||
convertLiteralValue(c1, floatType)
|
||||
convertLiteralValue(c2, floatType)
|
||||
@@ -1956,38 +2126,38 @@ func _complex(n *node) {
|
||||
|
||||
if typ := n.typ.TypeOf(); isComplex(typ) {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
// Not a complex type: ignore imaginary part
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].Set(value0(f).Convert(typ))
|
||||
dest(f).Set(value0(f).Convert(typ))
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _imag(n *node) {
|
||||
i := n.findex
|
||||
dest := genValue(n)
|
||||
convertLiteralValue(n.child[1], complexType)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetFloat(imag(value(f).Complex()))
|
||||
dest(f).SetFloat(imag(value(f).Complex()))
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func _real(n *node) {
|
||||
i := n.findex
|
||||
dest := genValue(n)
|
||||
convertLiteralValue(n.child[1], complexType)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetFloat(real(value(f).Complex()))
|
||||
dest(f).SetFloat(real(value(f).Complex()))
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2005,12 +2175,12 @@ func _delete(n *node) {
|
||||
}
|
||||
|
||||
func _len(n *node) {
|
||||
i := n.findex
|
||||
dest := genValue(n)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetInt(int64(value(f).Len()))
|
||||
dest(f).SetInt(int64(value(f).Len()))
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2030,7 +2200,7 @@ func _new(n *node) {
|
||||
func _make(n *node) {
|
||||
dest := genValue(n)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.child[1].typ.TypeOf()
|
||||
typ := n.child[1].typ.frameType()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
@@ -2123,16 +2293,18 @@ func reset(n *node) {
|
||||
func recv(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
|
||||
if n.interp.cancelChan {
|
||||
// Cancellable channel read
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
ch := value(f)
|
||||
// Fast: channel read doesn't block
|
||||
if x, ok := ch.TryRecv(); ok {
|
||||
if x.Bool() {
|
||||
var ok bool
|
||||
ch := value(f)
|
||||
if f.data[i], ok = ch.TryRecv(); ok {
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2148,7 +2320,6 @@ func recv(n *node) {
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Fast: channel read doesn't block
|
||||
var ok bool
|
||||
@@ -2170,7 +2341,7 @@ func recv(n *node) {
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v, _ := value(f).Recv(); v.Bool() {
|
||||
if f.data[i], _ = value(f).Recv(); f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2222,13 +2393,16 @@ func recv2(n *node) {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if n.rval.IsValid() {
|
||||
// Convert constant value to target type.
|
||||
n.rval = n.rval.Convert(t)
|
||||
} 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 +2594,21 @@ func isNil(n *node) {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetBool(value(f).IsNil())
|
||||
dest(f).SetBool(value(f).IsNil())
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2446,19 +2622,21 @@ func isNotNil(n *node) {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i].SetBool(!value(f).IsNil())
|
||||
dest(f).SetBool(!value(f).IsNil())
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,12 +74,15 @@ type symbol struct {
|
||||
// execution to the index in frame, created exactly from the types layout.
|
||||
//
|
||||
type scope struct {
|
||||
anc *scope // Ancestor upper scope
|
||||
def *node // function definition node this scope belongs to, or nil
|
||||
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)
|
||||
anc *scope // Ancestor upper scope
|
||||
def *node // function definition node this scope belongs to, or nil
|
||||
loop *node // loop exit node for break statement
|
||||
loopRestart *node // loop restart node for continue statement
|
||||
pkgID string // unique id of package in which scope is defined
|
||||
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
|
||||
@@ -95,6 +98,8 @@ func (s *scope) push(indirect bool) *scope {
|
||||
sc.global = s.global
|
||||
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
|
||||
}
|
||||
|
||||
@@ -156,19 +161,15 @@ func (s *scope) add(typ *itype) (index int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (interp *Interpreter) initScopePkg(n *node) (*scope, string) {
|
||||
func (interp *Interpreter) initScopePkg(pkgID string) *scope {
|
||||
sc := interp.universe
|
||||
pkgName := mainID
|
||||
|
||||
if p := fileNode(n); p != nil {
|
||||
pkgName = p.child[0].ident
|
||||
}
|
||||
|
||||
interp.mutex.Lock()
|
||||
if _, ok := interp.scopes[pkgName]; !ok {
|
||||
interp.scopes[pkgName] = sc.pushBloc()
|
||||
if _, ok := interp.scopes[pkgID]; !ok {
|
||||
interp.scopes[pkgID] = sc.pushBloc()
|
||||
}
|
||||
sc = interp.scopes[pkgName]
|
||||
sc = interp.scopes[pkgID]
|
||||
sc.pkgID = pkgID
|
||||
interp.mutex.Unlock()
|
||||
return sc, pkgName
|
||||
return sc
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
func (interp *Interpreter) importSrc(rPath, path string) error {
|
||||
var dir string
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
if interp.rdir[path] {
|
||||
return fmt.Errorf("import cycle not allowed\n\timports %s", path)
|
||||
}
|
||||
@@ -46,7 +47,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
var root *node
|
||||
var pkgName string
|
||||
|
||||
// Parse source files
|
||||
// Parse source files.
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if skipFile(&interp.context, name) {
|
||||
@@ -66,6 +67,9 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
if root == nil {
|
||||
continue
|
||||
}
|
||||
if interp.astDot {
|
||||
root.astDot(dotX(), name)
|
||||
}
|
||||
if pkgName == "" {
|
||||
pkgName = pname
|
||||
} else if pkgName != pname {
|
||||
@@ -75,26 +79,24 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
|
||||
subRPath := effectivePkg(rPath, path)
|
||||
var list []*node
|
||||
list, err = interp.gta(root, subRPath)
|
||||
list, err = interp.gta(root, subRPath, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revisit[subRPath] = append(revisit[subRPath], list...)
|
||||
}
|
||||
|
||||
// revisit incomplete nodes where GTA could not complete
|
||||
// Revisit incomplete nodes where GTA could not complete.
|
||||
for pkg, nodes := range revisit {
|
||||
for _, n := range nodes {
|
||||
if _, err = interp.gta(n, pkg); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = interp.gtaRetry(nodes, pkg, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate control flow graphs
|
||||
for _, root := range rootNodes {
|
||||
var nodes []*node
|
||||
if nodes, err = interp.cfg(root); err != nil {
|
||||
if nodes, err = interp.cfg(root, path); err != nil {
|
||||
return err
|
||||
}
|
||||
initNodes = append(initNodes, nodes...)
|
||||
@@ -103,13 +105,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
|
||||
// Register source package in the interpreter. The package contains only
|
||||
// the global symbols in the package scope.
|
||||
interp.mutex.Lock()
|
||||
interp.srcPkg[path] = interp.scopes[pkgName].sym
|
||||
|
||||
// Rename imported pkgName to alias if they are different
|
||||
if pkgName != alias {
|
||||
interp.scopes[alias] = interp.scopes[pkgName]
|
||||
delete(interp.scopes, pkgName)
|
||||
}
|
||||
interp.srcPkg[path] = interp.scopes[path].sym
|
||||
|
||||
interp.frame.mutex.Lock()
|
||||
interp.resizeFrame()
|
||||
@@ -189,7 +185,7 @@ func effectivePkg(root, path string) string {
|
||||
for i := 0; i < len(splitPath); 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
|
||||
rootIndex++
|
||||
} else if prevRootIndex == rootIndex {
|
||||
|
||||
143
interp/type.go
143
interp/type.go
@@ -17,7 +17,6 @@ const (
|
||||
binPkgT
|
||||
boolT
|
||||
builtinT
|
||||
byteT
|
||||
chanT
|
||||
complex64T
|
||||
complex128T
|
||||
@@ -33,7 +32,6 @@ const (
|
||||
int64T
|
||||
mapT
|
||||
ptrT
|
||||
runeT
|
||||
srcPkgT
|
||||
stringT
|
||||
structT
|
||||
@@ -54,7 +52,6 @@ var cats = [...]string{
|
||||
arrayT: "arrayT",
|
||||
binT: "binT",
|
||||
binPkgT: "binPkgT",
|
||||
byteT: "byteT",
|
||||
boolT: "boolT",
|
||||
builtinT: "builtinT",
|
||||
chanT: "chanT",
|
||||
@@ -72,7 +69,6 @@ var cats = [...]string{
|
||||
int64T: "int64T",
|
||||
mapT: "mapT",
|
||||
ptrT: "ptrT",
|
||||
runeT: "runeT",
|
||||
srcPkgT: "srcPkgT",
|
||||
stringT: "stringT",
|
||||
structT: "structT",
|
||||
@@ -103,22 +99,24 @@ type structField struct {
|
||||
|
||||
// itype defines the internal representation of types in the interpreter
|
||||
type itype struct {
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
method []*node // Associated methods or nil
|
||||
name string // name of type within its package for a defined type
|
||||
path string // for a defined type, the package import path
|
||||
size int // Size of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
untyped bool // true for a literal value (string or number)
|
||||
sizedef bool // true if array size is computed from type definition
|
||||
node *node // root AST node of type definition
|
||||
scope *scope // type declaration scope (in case of re-parse incomplete type)
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
method []*node // Associated methods or nil
|
||||
name string // name of type within its package for a defined type
|
||||
path string // for a defined type, the package import path
|
||||
size int // Size of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
recursive bool // true if the type has an element which refer to itself
|
||||
untyped bool // true for a literal value (string or number)
|
||||
sizedef bool // true if array size is computed from type definition
|
||||
isBinMethod bool // true if the type refers to a bin method function
|
||||
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
|
||||
@@ -142,7 +140,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var err cfgError
|
||||
var err error
|
||||
switch n.kind {
|
||||
case addressExpr, starExpr:
|
||||
t.cat = ptrT
|
||||
@@ -175,7 +173,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
t.incomplete = true
|
||||
@@ -199,8 +197,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t.cat = boolT
|
||||
t.name = "bool"
|
||||
case byte:
|
||||
t.cat = byteT
|
||||
t.name = "byte"
|
||||
t.cat = uint8T
|
||||
t.name = "uint8"
|
||||
t.untyped = true
|
||||
case complex64:
|
||||
t.cat = complex64T
|
||||
@@ -226,8 +224,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t.name = "uint"
|
||||
t.untyped = true
|
||||
case rune:
|
||||
t.cat = runeT
|
||||
t.name = "rune"
|
||||
t.cat = int32T
|
||||
t.name = "int32"
|
||||
t.untyped = true
|
||||
case string:
|
||||
t.cat = stringT
|
||||
@@ -409,6 +407,20 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t}
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
var lt *itype
|
||||
if lt, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lt.incomplete {
|
||||
t.incomplete = true
|
||||
break
|
||||
}
|
||||
switch lt.cat {
|
||||
case arrayT, mapT:
|
||||
t = lt.val
|
||||
}
|
||||
|
||||
case interfaceType:
|
||||
t.cat = interfaceT
|
||||
if sname := typeName(n); sname != "" {
|
||||
@@ -434,6 +446,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
}
|
||||
|
||||
case landExpr, lorExpr:
|
||||
t.cat = boolT
|
||||
|
||||
case mapType:
|
||||
t.cat = mapT
|
||||
if t.key, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
@@ -482,7 +497,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if m, _ := lt.lookupMethod(name); m != nil {
|
||||
t, err = nodeType(interp, sc, m.child[2])
|
||||
} 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 {
|
||||
t = lt.fieldSeq(ti)
|
||||
} else if bs, _, ok := lt.lookupBinField(name); ok {
|
||||
@@ -593,7 +608,6 @@ var zeroValues [maxT]reflect.Value
|
||||
|
||||
func init() {
|
||||
zeroValues[boolT] = reflect.ValueOf(false)
|
||||
zeroValues[byteT] = reflect.ValueOf(byte(0))
|
||||
zeroValues[complex64T] = reflect.ValueOf(complex64(0))
|
||||
zeroValues[complex128T] = reflect.ValueOf(complex128(0))
|
||||
zeroValues[errorT] = reflect.ValueOf(new(error)).Elem()
|
||||
@@ -604,7 +618,6 @@ func init() {
|
||||
zeroValues[int16T] = reflect.ValueOf(int16(0))
|
||||
zeroValues[int32T] = reflect.ValueOf(int32(0))
|
||||
zeroValues[int64T] = reflect.ValueOf(int64(0))
|
||||
zeroValues[runeT] = reflect.ValueOf(rune(0))
|
||||
zeroValues[stringT] = reflect.ValueOf("")
|
||||
zeroValues[uintT] = reflect.ValueOf(uint(0))
|
||||
zeroValues[uint8T] = reflect.ValueOf(uint8(0))
|
||||
@@ -616,11 +629,13 @@ func init() {
|
||||
|
||||
// if type is incomplete, re-parse it.
|
||||
func (t *itype) finalize() (*itype, error) {
|
||||
var err cfgError
|
||||
var err error
|
||||
if t.incomplete {
|
||||
sym, _, found := t.scope.lookup(t.name)
|
||||
if found && !sym.typ.incomplete {
|
||||
sym.typ.method = append(sym.typ.method, t.method...)
|
||||
t.method = sym.typ.method
|
||||
t.incomplete = false
|
||||
return sym.typ, nil
|
||||
}
|
||||
m := t.method
|
||||
@@ -809,6 +824,22 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
|
||||
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
|
||||
func (t *itype) getMethod(name string) *node {
|
||||
for _, m := range t.method {
|
||||
@@ -841,13 +872,11 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
|
||||
}
|
||||
|
||||
// lookupBinMethod returns a method and a path to access a field in a struct object (the receiver)
|
||||
func (t *itype) lookupBinMethod(name string) (reflect.Method, []int, bool, bool) {
|
||||
var isPtr bool
|
||||
func (t *itype) lookupBinMethod(name string) (m reflect.Method, index []int, isPtr bool, ok bool) {
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinMethod(name)
|
||||
}
|
||||
var index []int
|
||||
m, ok := t.TypeOf().MethodByName(name)
|
||||
m, ok = t.TypeOf().MethodByName(name)
|
||||
if !ok {
|
||||
m, ok = reflect.PtrTo(t.TypeOf()).MethodByName(name)
|
||||
isPtr = ok
|
||||
@@ -874,7 +903,7 @@ func exportName(s string) string {
|
||||
|
||||
var interf = reflect.TypeOf(new(interface{})).Elem()
|
||||
|
||||
func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.Type {
|
||||
if t.rtype != nil {
|
||||
return t.rtype
|
||||
}
|
||||
@@ -885,50 +914,56 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if t.val != nil && defined[t.val.name] && !t.val.incomplete && t.val.rtype == nil {
|
||||
if t.val != nil && defined[t.val.name] != nil && !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
|
||||
defined[t.val.name].recursive = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
t.rtype = t.val.refType(defined)
|
||||
t.rtype = t.val.refType(defined, wrapRecursive)
|
||||
case arrayT, variadicT:
|
||||
if t.sizedef {
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined))
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined, wrapRecursive))
|
||||
} else {
|
||||
t.rtype = reflect.SliceOf(t.val.refType(defined))
|
||||
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
|
||||
}
|
||||
case chanT:
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined))
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
|
||||
case errorT:
|
||||
t.rtype = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
in := make([]reflect.Type, len(t.arg))
|
||||
out := make([]reflect.Type, len(t.ret))
|
||||
//wrap := false
|
||||
for i, v := range t.arg {
|
||||
in[i] = v.refType(defined)
|
||||
in[i] = v.refType(defined, true)
|
||||
}
|
||||
for i, v := range t.ret {
|
||||
out[i] = v.refType(defined)
|
||||
out[i] = v.refType(defined, true)
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, false)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
|
||||
t.rtype = reflect.MapOf(t.key.refType(defined, wrapRecursive), t.val.refType(defined, wrapRecursive))
|
||||
case ptrT:
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined))
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined, wrapRecursive))
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
defined[t.name] = true
|
||||
defined[t.name] = t
|
||||
}
|
||||
var fields []reflect.StructField
|
||||
for _, f := range t.field {
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined), Tag: reflect.StructTag(f.tag)}
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined, wrapRecursive), Tag: reflect.StructTag(f.tag)}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if t.recursive && wrapRecursive {
|
||||
t.rtype = interf
|
||||
} else {
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
}
|
||||
default:
|
||||
if z, _ := t.zero(); z.IsValid() {
|
||||
t.rtype = z.Type()
|
||||
@@ -939,7 +974,7 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
|
||||
// TypeOf returns the reflection type of dynamic interpreter type t.
|
||||
func (t *itype) TypeOf() reflect.Type {
|
||||
return t.refType(map[string]bool{})
|
||||
return t.refType(map[string]*itype{}, false)
|
||||
}
|
||||
|
||||
func (t *itype) frameType() (r reflect.Type) {
|
||||
@@ -1003,7 +1038,17 @@ func isInterface(t *itype) bool {
|
||||
func isStruct(t *itype) bool {
|
||||
// Test first for a struct category, because a recursive interpreter struct may be
|
||||
// represented by an interface{} at reflect level.
|
||||
return t.cat == structT || t.TypeOf().Kind() == reflect.Struct
|
||||
switch t.cat {
|
||||
case structT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
return isStruct(t.val)
|
||||
case valueT:
|
||||
k := t.rtype.Kind()
|
||||
return k == reflect.Struct || (k == reflect.Ptr && t.rtype.Elem().Kind() == reflect.Struct)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
|
||||
|
||||
@@ -90,9 +90,6 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
v = reflect.ValueOf(n.val)
|
||||
}
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
case rvalueExpr:
|
||||
v := n.rval
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
default:
|
||||
if n.rval.IsValid() {
|
||||
v := n.rval
|
||||
@@ -167,7 +164,7 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
nod := n
|
||||
for {
|
||||
for v.IsValid() {
|
||||
// traverse interface indirections to find out concrete type
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
@@ -180,11 +177,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 {
|
||||
value := genValue(n)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports bufio'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports bytes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -54,6 +54,7 @@ func init() {
|
||||
"ToTitleSpecial": reflect.ValueOf(bytes.ToTitleSpecial),
|
||||
"ToUpper": reflect.ValueOf(bytes.ToUpper),
|
||||
"ToUpperSpecial": reflect.ValueOf(bytes.ToUpperSpecial),
|
||||
"ToValidUTF8": reflect.ValueOf(bytes.ToValidUTF8),
|
||||
"Trim": reflect.ValueOf(bytes.Trim),
|
||||
"TrimFunc": reflect.ValueOf(bytes.TrimFunc),
|
||||
"TrimLeft": reflect.ValueOf(bytes.TrimLeft),
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/flate'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/gzip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/lzw'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/zlib'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports container/heap'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports container/list'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports container/ring'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports context'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports crypto'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports crypto/aes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports crypto/cipher'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user