Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e78650d359 | ||
|
|
7327ff2811 | ||
|
|
ebde09b47d | ||
|
|
4995654e04 | ||
|
|
0a99eb48c3 | ||
|
|
4a22635585 | ||
|
|
b52dd8cc08 | ||
|
|
daaeac6e2c | ||
|
|
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
|
||||
|
||||
20
README.md
20
README.md
@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
||||
* Works everywhere Go works
|
||||
* All Go & runtime resources accessible from script (with control)
|
||||
* Security: `unsafe` and `syscall` packages neither used nor exported by default
|
||||
* Support Go 1.12 and Go 1.13 (the latest 2 major releases)
|
||||
* Support Go 1.13 and Go 1.14 (the latest 2 major releases)
|
||||
|
||||
## Install
|
||||
|
||||
@@ -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
|
||||
59
_test/cli2.go
Normal file
59
_test/cli2.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
ln net.Listener
|
||||
}
|
||||
|
||||
func (t *T) Close() {
|
||||
t.ln.Close()
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func server(ln net.Listener, ready chan bool) {
|
||||
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
var r1 *http.Request = r
|
||||
fmt.Fprintln(w, "Welcome to my website!", r1.RequestURI)
|
||||
})
|
||||
|
||||
go http.Serve(ln, nil)
|
||||
ready <- true
|
||||
}
|
||||
|
||||
func main() {
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
t := &T{ln}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer t.Close()
|
||||
// defer ln.Close()
|
||||
|
||||
ready := make(chan bool)
|
||||
go server(ln, ready)
|
||||
<-ready
|
||||
|
||||
client(fmt.Sprintf("http://%s/hello", ln.Addr().String()))
|
||||
http.DefaultServeMux = &http.ServeMux{}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Welcome to my website! /hello
|
||||
@@ -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
|
||||
13
_test/for10.go
Normal file
13
_test/for10.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; false; {
|
||||
println("nok", a)
|
||||
a++
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
14
_test/for11.go
Normal file
14
_test/for11.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
for ; true; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// nok 0
|
||||
// bye 0
|
||||
12
_test/for12.go
Normal file
12
_test/for12.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; false; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
13
_test/for13.go
Normal file
13
_test/for13.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
for ; false; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye 0
|
||||
16
_test/for14.go
Normal file
16
_test/for14.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; true; a++ {
|
||||
println(a)
|
||||
if a > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
// bye
|
||||
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/for9.go
Normal file
12
_test/for9.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for false {
|
||||
println("nok")
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
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
|
||||
17
_test/if3.go
Normal file
17
_test/if3.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
if false {
|
||||
println("false")
|
||||
a = 1
|
||||
} else {
|
||||
println("true")
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// -1
|
||||
19
_test/if4.go
Normal file
19
_test/if4.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
const bad = false
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
if bad {
|
||||
println("false")
|
||||
a = 1
|
||||
} else {
|
||||
println("true")
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// -1
|
||||
12
_test/if5.go
Normal file
12
_test/if5.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if true {
|
||||
println("ok")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
// bye
|
||||
11
_test/if6.go
Normal file
11
_test/if6.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if false {
|
||||
println("nok")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
15
_test/if7.go
Normal file
15
_test/if7.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
b := false
|
||||
if (b) {
|
||||
a = 1
|
||||
} else {
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// -1
|
||||
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
|
||||
12
_test/interface27.go
Normal file
12
_test/interface27.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var errs = map[int]error{0: nil}
|
||||
|
||||
func main() {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[0:<nil>]
|
||||
12
_test/interface28.go
Normal file
12
_test/interface28.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var errs = []error{nil}
|
||||
|
||||
func main() {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [<nil>]
|
||||
9
_test/interface29.go
Normal file
9
_test/interface29.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
println(a == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
9
_test/interface30.go
Normal file
9
_test/interface30.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
println(a != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
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"))
|
||||
}
|
||||
24
_test/struct38.go
Normal file
24
_test/struct38.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(t *T1)
|
||||
y *xxx
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
var (
|
||||
x1 *T1 = x
|
||||
x = &T1{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
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
|
||||
16
_test/switch27.go
Normal file
16
_test/switch27.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
//a := false
|
||||
switch false {
|
||||
case true:
|
||||
println("true")
|
||||
case false:
|
||||
println("false")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// bye
|
||||
15
_test/switch28.go
Normal file
15
_test/switch28.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
switch {
|
||||
case true:
|
||||
println("true")
|
||||
case false:
|
||||
println("false")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// bye
|
||||
14
_test/switch29.go
Normal file
14
_test/switch29.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a {
|
||||
case 3:
|
||||
println("three")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// three
|
||||
// bye
|
||||
16
_test/switch30.go
Normal file
16
_test/switch30.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a {
|
||||
default:
|
||||
//println("default")
|
||||
case 3:
|
||||
println("three")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// three
|
||||
// bye
|
||||
10
_test/switch31.go
Normal file
10
_test/switch31.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
switch {
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
11
_test/switch32.go
Normal file
11
_test/switch32.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 1
|
||||
switch a {
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye 1
|
||||
11
_test/switch33.go
Normal file
11
_test/switch33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
switch a.(type) {
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
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)
|
||||
|
||||
|
||||
441
interp/cfg.go
441
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
|
||||
@@ -303,13 +317,13 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
sc = sc.pushBloc()
|
||||
|
||||
case switchStmt, switchIfStmt, typeSwitch:
|
||||
// Make sure default clause is in last position
|
||||
// Make sure default clause is in last position.
|
||||
c := n.lastChild().child
|
||||
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
|
||||
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 {}
|
||||
@@ -833,11 +863,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = body.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = body.start
|
||||
body.tnext = body.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = body.start
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt2: // for init; cond; {}
|
||||
@@ -846,11 +883,20 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = body.start
|
||||
body.tnext = body.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3: // for ; cond; post {}
|
||||
@@ -858,21 +904,27 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = body.start
|
||||
post.tnext = body.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = post.start
|
||||
post.tnext = cond.start
|
||||
loop, loopRestart = nil, nil
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3a: // for int; ; post {}
|
||||
case forStmt3a: // for init; ; post {}
|
||||
init, post, body := n.child[0], n.child[1], n.child[2]
|
||||
n.start = init.start
|
||||
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 {}
|
||||
@@ -881,16 +933,24 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = body.start
|
||||
post.tnext = body.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
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 +961,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 +972,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 +996,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:
|
||||
@@ -948,10 +1004,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
if sym.kind == varSym && sym.typ != nil && sym.typ.TypeOf().Kind() == reflect.Bool {
|
||||
switch n.anc.kind {
|
||||
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
|
||||
n.gen = branch
|
||||
}
|
||||
fixBranch(n)
|
||||
}
|
||||
}
|
||||
if n.sym != nil {
|
||||
@@ -966,8 +1019,15 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = tbody.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
cond.fnext = n
|
||||
tbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -977,9 +1037,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test and the useless branch.
|
||||
if cond.rval.Bool() {
|
||||
n.start = tbody.start
|
||||
} else {
|
||||
n.start = fbody.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -990,9 +1059,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = init.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = tbody.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = n
|
||||
sc = sc.pop()
|
||||
|
||||
@@ -1002,9 +1080,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = tbody.start
|
||||
} else {
|
||||
init.tnext = fbody.start
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -1019,6 +1106,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 +1117,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 +1188,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 +1242,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 +1260,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 +1281,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 +1303,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 +1311,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 {
|
||||
@@ -1282,40 +1375,56 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
||||
fallthrough
|
||||
|
||||
case switchStmt:
|
||||
sc = sc.pop()
|
||||
sbn := n.lastChild() // switch block node
|
||||
clauses := sbn.child
|
||||
l := len(clauses)
|
||||
// Chain case clauses
|
||||
if l == 0 {
|
||||
// Switch is empty
|
||||
break
|
||||
}
|
||||
// Chain case clauses.
|
||||
for i, c := range clauses[:l-1] {
|
||||
c.fnext = clauses[i+1] // chain to next clause
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
if n.kind == typeSwitch {
|
||||
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
|
||||
}
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
c.fnext = clauses[i+1] // Chain to next clause.
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
body.tnext = n
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
if n.kind == typeSwitch {
|
||||
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
|
||||
}
|
||||
if len(clauses[i+1].child) == 0 {
|
||||
body.tnext = n // Fallthrough to next with empty body, just exit.
|
||||
} else {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
}
|
||||
} else {
|
||||
body.tnext = n // Exit switch at end of clause body.
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
c := clauses[l-1] // Last clause.
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
n.start = sbn.start
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
body.tnext = n
|
||||
}
|
||||
sc = sc.pop()
|
||||
loop = nil
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sc = sc.pop()
|
||||
sbn := n.lastChild() // switch block node
|
||||
clauses := sbn.child
|
||||
l := len(clauses)
|
||||
if l == 0 {
|
||||
// Switch is empty
|
||||
break
|
||||
}
|
||||
// Wire case clauses in reverse order so the next start node is already resolved when used.
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
c := clauses[i]
|
||||
@@ -1336,18 +1445,13 @@ 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
|
||||
}
|
||||
sc = sc.pop()
|
||||
loop = nil
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
case typeAssertExpr:
|
||||
if len(n.child) > 1 {
|
||||
@@ -1375,16 +1479,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 +1543,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 +1636,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 {
|
||||
@@ -1511,11 +1665,27 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find default case clause index of a switch statement, if any
|
||||
// FixBranch sets the branch action to the identExpr node if it is a bool
|
||||
// used in a conditional expression.
|
||||
func fixBranch(n *node) {
|
||||
switch n.anc.kind {
|
||||
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
|
||||
n.gen = branch
|
||||
case parenExpr:
|
||||
fixBranch(n.anc)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefault return the index of default case clause in a switch statement, or -1.
|
||||
func getDefault(n *node) int {
|
||||
for i, c := range n.lastChild().child {
|
||||
if len(c.child) == 1 {
|
||||
switch len(c.child) {
|
||||
case 0:
|
||||
return i
|
||||
case 1:
|
||||
if c.child[0].kind == caseBody {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
@@ -1526,7 +1696,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 +1811,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 +1848,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 +1916,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 +1966,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 +2003,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
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
import "reflect"
|
||||
|
||||
// gta performs a global types analysis on the AST, registering types,
|
||||
// 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 +19,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 +48,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 +64,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 +72,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 +94,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 +139,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 +168,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 +219,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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user