Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6346d11286 | ||
|
|
1edb6a1424 | ||
|
|
a2f56431ea | ||
|
|
42abedb25d | ||
|
|
151699ef9f | ||
|
|
f1f3ca7e06 | ||
|
|
9ddecfa121 | ||
|
|
04770a4b81 | ||
|
|
341c69d922 | ||
|
|
f4cc059e3e | ||
|
|
535e7e1c42 | ||
|
|
cb0f3a77bb | ||
|
|
b1279d0a21 | ||
|
|
f3f9ffaea7 | ||
|
|
e332a6b3be | ||
|
|
358a57b4b9 | ||
|
|
3640f2f820 | ||
|
|
3faa47c61e | ||
|
|
896bfeb5a1 | ||
|
|
1029d102e5 | ||
|
|
065d4fa4d7 | ||
|
|
332becf95d | ||
|
|
da9e6a0d6c | ||
|
|
913680d1ed | ||
|
|
a004809fc2 |
@@ -3,7 +3,7 @@ project_name: yaegi
|
|||||||
builds:
|
builds:
|
||||||
- id: yaegi
|
- id: yaegi
|
||||||
binary: yaegi
|
binary: yaegi
|
||||||
main: ./cmd/yaegi/yaegi.go
|
main: ./cmd/yaegi/
|
||||||
|
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
@@ -28,7 +28,7 @@ builds:
|
|||||||
|
|
||||||
- id: goexports
|
- id: goexports
|
||||||
binary: goexports
|
binary: goexports
|
||||||
main: ./cmd/goexports/goexports.go
|
main: ./cmd/goexports/
|
||||||
|
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ env:
|
|||||||
global:
|
global:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
|
||||||
go_import_path: github.com/containous/yaegi
|
go_import_path: github.com/traefik/yaegi
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
# Install linters and misspell
|
# Install linters and misspell
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Yaegi is an open source project, and your feedback and contributions
|
Yaegi is an open source project, and your feedback and contributions
|
||||||
are needed and always welcome.
|
are needed and always welcome.
|
||||||
|
|
||||||
[Issues] and [Pull Requests] are opened at https://github.com/containous/yaegi.
|
[Issues] and [Pull Requests] are opened at https://github.com/traefik/yaegi.
|
||||||
|
|
||||||
Non trivial changes should be discussed with the project maintainers by
|
Non trivial changes should be discussed with the project maintainers by
|
||||||
opening a [Feature Request] clearly explaining rationale, background
|
opening a [Feature Request] clearly explaining rationale, background
|
||||||
@@ -13,12 +13,12 @@ discussions.
|
|||||||
Once the proposal is approved, a Pull Request can be opened. If you want
|
Once the proposal is approved, a Pull Request can be opened. If you want
|
||||||
to provide early visibility to reviewers, create a [Draft Pull Request].
|
to provide early visibility to reviewers, create a [Draft Pull Request].
|
||||||
|
|
||||||
We will also require you to sign the [Containous Contributor License Agreement]
|
We will also require you to sign the [Traefik Contributor License Agreement]
|
||||||
after you submit your first pull request to this project. The link to sign the
|
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.
|
agreement will be presented to you in the web interface of the pull request.
|
||||||
|
|
||||||
[Issues]: https://github.com/containous/yaegi/issues
|
[Issues]: https://github.com/traefik/yaegi/issues
|
||||||
[Pull Requests]: https://github.com/containous/yaegi/issues
|
[Pull Requests]: https://github.com/traefik/yaegi/issues
|
||||||
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
|
[Feature Request]: https://github.com/traefik/yaegi/issues/new?template=feature_request.md
|
||||||
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
|
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
|
||||||
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi
|
[Traefik Labs Contributor License Agreement]: https://cla-assistant.io/traefik/yaegi
|
||||||
|
|||||||
1
LICENSE
1
LICENSE
@@ -187,6 +187,7 @@
|
|||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2019 Containous SAS
|
Copyright 2019 Containous SAS
|
||||||
|
Copyright 2020 Traefik Labs SAS
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -2,9 +2,9 @@
|
|||||||
<img width="400" src="doc/images/yaegi.png" alt="Yaegi" title="Yaegi" />
|
<img width="400" src="doc/images/yaegi.png" alt="Yaegi" title="Yaegi" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/containous/yaegi/releases)
|
[](https://github.com/traefik/yaegi/releases)
|
||||||
[](https://travis-ci.com/containous/yaegi)
|
[](https://travis-ci.com/traefik/yaegi)
|
||||||
[](https://godoc.org/github.com/containous/yaegi)
|
[](https://godoc.org/github.com/traefik/yaegi)
|
||||||
[](https://community.containo.us/c/yaegi)
|
[](https://community.containo.us/c/yaegi)
|
||||||
|
|
||||||
Yaegi is Another Elegant Go Interpreter.
|
Yaegi is Another Elegant Go Interpreter.
|
||||||
@@ -25,13 +25,13 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
|||||||
### Go package
|
### Go package
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/containous/yaegi/interp"
|
import "github.com/traefik/yaegi/interp"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Command-line executable
|
### Command-line executable
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get -u github.com/containous/yaegi/cmd/yaegi
|
go get -u github.com/traefik/yaegi/cmd/yaegi
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
|
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
|
||||||
@@ -47,8 +47,8 @@ Create an interpreter with `New()`, run Go code with `Eval()`:
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
"github.com/containous/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -81,7 +81,7 @@ The following program is compiled ahead of time, except `bar()` which is interpr
|
|||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/interp"
|
import "github.com/traefik/yaegi/interp"
|
||||||
|
|
||||||
const src = `package foo
|
const src = `package foo
|
||||||
func Bar(s string) string { return s + "-Foo" }`
|
func Bar(s string) string { return s + "-Foo" }`
|
||||||
@@ -155,11 +155,11 @@ Documentation about Yaegi commands and libraries can be found at usual [godoc.or
|
|||||||
|
|
||||||
Beside the known [bugs] which are supposed to be fixed in the short term, there are some limitations not planned to be addressed soon:
|
Beside the known [bugs] which are supposed to be fixed in the short term, there are some limitations not planned to be addressed soon:
|
||||||
|
|
||||||
- assembly files (`.s`) are not supported
|
- Assembly files (`.s`) are not supported.
|
||||||
- calling C code is not supported (no virtual "C" package)
|
- Calling C code is not supported (no virtual "C" package).
|
||||||
- interfaces to be used from the pre-compiled code can not be added dynamically, as it is required to pre-compile interface wrappers
|
- Interfaces to be used from the pre-compiled code can not be added dynamically, as it is required to pre-compile interface wrappers.
|
||||||
- representation of types by `reflect` and printing values using %T may give different results between compiled mode and interpreted mode
|
- Representation of types by `reflect` and printing values using %T may give different results between compiled mode and interpreted mode.
|
||||||
- interpreting computation intensive code is likely to remain significantly slower than in compiled mode
|
- Interpreting computation intensive code is likely to remain significantly slower than in compiled mode.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ Beside the known [bugs] which are supposed to be fixed in the short term, there
|
|||||||
[Apache 2.0][License].
|
[Apache 2.0][License].
|
||||||
|
|
||||||
[specs]: https://golang.org/ref/spec
|
[specs]: https://golang.org/ref/spec
|
||||||
[docs]: https://godoc.org/github.com/containous/yaegi
|
[docs]: https://godoc.org/github.com/traefik/yaegi
|
||||||
[license]: https://github.com/containous/yaegi/blob/master/LICENSE
|
[license]: https://github.com/traefik/yaegi/blob/master/LICENSE
|
||||||
[github]: https://github.com/containous/yaegi
|
[github]: https://github.com/traefik/yaegi
|
||||||
[bugs]: https://github.com/containous/yaegi/issues?q=is%3Aissue+is%3Aopen+label%3Abug
|
[bugs]: https://github.com/traefik/yaegi/issues?q=is%3Aissue+is%3Aopen+label%3Abug
|
||||||
|
|||||||
16
_test/a42.go
Normal file
16
_test/a42.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var b [8]byte
|
||||||
|
binary.LittleEndian.PutUint64(b[:], uint64(1))
|
||||||
|
|
||||||
|
fmt.Println(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 0 0 0 0 0 0 0]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package foo
|
package foo
|
||||||
|
|
||||||
import bar "github.com/containous/yaegi/_test/b2/foo"
|
import bar "github.com/traefik/yaegi/_test/b2/foo"
|
||||||
|
|
||||||
var Desc = "in b1/foo"
|
var Desc = "in b1/foo"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package c1
|
package c1
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/c2"
|
import "github.com/traefik/yaegi/_test/c2"
|
||||||
|
|
||||||
var C1 = c2.C2 + "x"
|
var C1 = c2.C2 + "x"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package c2
|
package c2
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/c1"
|
import "github.com/traefik/yaegi/_test/c1"
|
||||||
|
|
||||||
var C2 = c1.C1 + "Y"
|
var C2 = c1.C1 + "Y"
|
||||||
|
|||||||
51
_test/cli4.go
Normal file
51
_test/cli4.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mw1 struct {
|
||||||
|
next http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mw1) ServeHTTP(rw http.ResponseWriter, rq *http.Request) {
|
||||||
|
m.next.ServeHTTP(rw, rq)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mw0 struct{}
|
||||||
|
|
||||||
|
func (m *mw0) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "Welcome to my website!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m0 := &mw0{}
|
||||||
|
m1 := &mw1{m0}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", m1.ServeHTTP)
|
||||||
|
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client(server.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Welcome to my website!
|
||||||
51
_test/cli5.go
Normal file
51
_test/cli5.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mw1 struct {
|
||||||
|
next http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mw1) ServeHTTP(rw http.ResponseWriter, rq *http.Request) {
|
||||||
|
m.next.ServeHTTP(rw, rq)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mw0 struct{}
|
||||||
|
|
||||||
|
func (m *mw0) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "Welcome to my website!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m0 := &mw0{}
|
||||||
|
m1 := &mw1{next: m0}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", m1.ServeHTTP)
|
||||||
|
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client(server.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Welcome to my website!
|
||||||
52
_test/cli6.go
Normal file
52
_test/cli6.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
type mw1 struct {
|
||||||
|
next http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mw1) ServeHTTP(rw http.ResponseWriter, rq *http.Request) {
|
||||||
|
t := &T{
|
||||||
|
ResponseWriter: rw,
|
||||||
|
}
|
||||||
|
x := t.Header()
|
||||||
|
fmt.Fprint(rw, "Welcome to my website!", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m1 := &mw1{}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", m1.ServeHTTP)
|
||||||
|
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client(server.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Welcome to my website!map[]
|
||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containous/yaegi/_test/ct1"
|
"github.com/traefik/yaegi/_test/ct1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type T struct {
|
type T struct {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.Lshortfile)
|
log.SetFlags(log.Lshortfile)
|
||||||
i := interp.New(interp.Options{})
|
i := interp.New(interp.Options{Stdout: os.Stdout})
|
||||||
if _, err := i.Eval(`func f() (int, int) { return 1, 2 }`); err != nil {
|
if _, err := i.Eval(`func f() (int, int) { return 1, 2 }`); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package foo
|
package foo
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/foo/boo"
|
import "github.com/traefik/yaegi/_test/foo/boo"
|
||||||
|
|
||||||
var Bar = "BARR"
|
var Bar = "BARR"
|
||||||
var Boo = boo.Boo
|
var Boo = boo.Boo
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package foo
|
package foo
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/foo/boo"
|
import "github.com/traefik/yaegi/_test/foo/boo"
|
||||||
|
|
||||||
var Bir = boo.Boo + "22"
|
var Bir = boo.Boo + "22"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/foo"
|
import "github.com/traefik/yaegi/_test/foo"
|
||||||
|
|
||||||
func main() { println(foo.Bar, foo.Boo) }
|
func main() { println(foo.Bar, foo.Boo) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/p1"
|
import "github.com/traefik/yaegi/_test/p1"
|
||||||
|
|
||||||
func main() { println("num:", p1.Uint32()) }
|
func main() { println("num:", p1.Uint32()) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import boo "github.com/containous/yaegi/_test/foo"
|
import boo "github.com/traefik/yaegi/_test/foo"
|
||||||
|
|
||||||
func main() { println(boo.Bar, boo.Boo, boo.Bir) }
|
func main() { println(boo.Bar, boo.Boo, boo.Bir) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/c1"
|
import "github.com/traefik/yaegi/_test/c1"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println(c1.C1)
|
println(c1.C1)
|
||||||
@@ -8,4 +8,4 @@ func main() {
|
|||||||
|
|
||||||
// Error:
|
// Error:
|
||||||
// import cycle not allowed
|
// import cycle not allowed
|
||||||
// imports github.com/containous/yaegi/_test/c1
|
// imports github.com/traefik/yaegi/_test/c1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/foo-bar"
|
import bar "github.com/traefik/yaegi/_test/foo-bar"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println(bar.Name)
|
println(bar.Name)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/b1/foo"
|
import "github.com/traefik/yaegi/_test/b1/foo"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println(foo.Desc)
|
println(foo.Desc)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/baz-bat"
|
import "github.com/traefik/yaegi/_test/baz-bat"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println(baz.Name)
|
println(baz.Name)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.Lshortfile)
|
log.SetFlags(log.Lshortfile)
|
||||||
i := interp.New(interp.Options{})
|
i := interp.New(interp.Options{})
|
||||||
i.Use(interp.Symbols)
|
i.Use(interp.Symbols)
|
||||||
if _, err := i.Eval(`import "github.com/containous/yaegi/interp"`); err != nil {
|
if _, err := i.Eval(`import "github.com/traefik/yaegi/interp"`); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err := i.Eval(`i := interp.New(interp.Options{})`); err != nil {
|
if _, err := i.Eval(`i := interp.New(interp.Options{})`); err != nil {
|
||||||
@@ -20,6 +20,3 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output:
|
|
||||||
// 42
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
i := interp.New(interp.Opt{})
|
i := interp.New(interp.Opt{})
|
||||||
i.Use(interp.ExportValue, interp.ExportType)
|
i.Use(interp.ExportValue, interp.ExportType)
|
||||||
i.Eval(`import "github.com/containous/yaegi/interp"`)
|
i.Eval(`import "github.com/traefik/yaegi/interp"`)
|
||||||
i.Eval(`i := interp.New(interp.Opt{})`)
|
i.Eval(`i := interp.New(interp.Opt{})`)
|
||||||
i.Eval(`i.Eval("println(42)")`)
|
i.Eval(`i.Eval("println(42)")`)
|
||||||
}
|
}
|
||||||
|
|||||||
16
_test/ipp_as_key.go
Normal file
16
_test/ipp_as_key.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"guthib.com/toto" // pkg name is actually titi
|
||||||
|
"guthib.com/tata" // pkg name is actually tutu
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("Hello", titi.Quux())
|
||||||
|
println("Hello", tutu.Quux())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOPATH:testdata/redeclaration-global7
|
||||||
|
// Output:
|
||||||
|
// Hello bar
|
||||||
|
// Hello baz
|
||||||
14
_test/m1/main.go
Normal file
14
_test/m1/main.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("vim-go")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWeird(t *testing.T) {
|
||||||
|
fmt.Println("in TestWeird")
|
||||||
|
}
|
||||||
17
_test/m1/main_test.go
Normal file
17
_test/m1/main_test.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
fmt.Println("in test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMain(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rand.Int()
|
||||||
|
}
|
||||||
|
}
|
||||||
14
_test/method35.go
Normal file
14
_test/method35.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
_, err = strconv.Atoi("erwer")
|
||||||
|
if _, ok := err.(*strconv.NumError); ok {
|
||||||
|
println("here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// here
|
||||||
16
_test/pkgname0.go
Normal file
16
_test/pkgname0.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"guthib.com/bar" // pkg name is actually quux
|
||||||
|
baz "guthib.com/baz" // pkg name is also quux, force it to baz.
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("Hello", quux.Quux())
|
||||||
|
println("Hello", baz.Quux())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOPATH:testdata/redeclaration-global7
|
||||||
|
// Output:
|
||||||
|
// Hello bar
|
||||||
|
// Hello baz
|
||||||
13
_test/pkgname1.go
Normal file
13
_test/pkgname1.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"guthib.com/bar" // pkg name is actually quux
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("Hello", bar.Quux()) // bar should not be a known symbol.
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOPATH:testdata/redeclaration-global7
|
||||||
|
// Error:
|
||||||
|
// ../_test/pkgname1.go:8:19: undefined: bar
|
||||||
13
_test/pkgname2.go
Normal file
13
_test/pkgname2.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"guthib.com/toto" // pkg name is actually titi
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("Hello", titi.Quux())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOPATH:testdata/redeclaration-global7
|
||||||
|
// Output:
|
||||||
|
// Hello bar
|
||||||
@@ -12,4 +12,5 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error:
|
// Error:
|
||||||
// ../_test/redeclaration-global5.go:5:1: time redeclared in this block
|
// ../_test/redeclaration-global5.go:5:6: time redeclared in this block
|
||||||
|
// previous declaration at ../_test/redeclaration-global5.go:3:5
|
||||||
|
|||||||
14
_test/redeclaration-global7.go
Normal file
14
_test/redeclaration-global7.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"guthib.com/bar" // pkg name is actually quux
|
||||||
|
"guthib.com/baz" // pkg name is also quux
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("Hello", quux.Quux())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOPATH:testdata/redeclaration-global7
|
||||||
|
// Error:
|
||||||
|
// ../_test/redeclaration-global7.go:5:2: quux/redeclaration-global7.go redeclared as imported package name
|
||||||
22
_test/struct55.go
Normal file
22
_test/struct55.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
m []*log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
l.m[0].Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := &Logger{m: []*log.Logger{log.New(os.Stdout, "", log.Lmsgprefix)}}
|
||||||
|
l.Infof("test %s", "test")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// test test
|
||||||
23
_test/struct56.go
Normal file
23
_test/struct56.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
IA InnerA
|
||||||
|
}
|
||||||
|
|
||||||
|
type InnerA struct {
|
||||||
|
Timestamp int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &A{}
|
||||||
|
b, _ := json.Marshal(a)
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {"IA":{"Timestamp":0}}
|
||||||
23
_test/struct57.go
Normal file
23
_test/struct57.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
InnerA
|
||||||
|
}
|
||||||
|
|
||||||
|
type InnerA struct {
|
||||||
|
Timestamp int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &A{}
|
||||||
|
b, _ := json.Marshal(a)
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {"Timestamp":0}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import _ "github.com/containous/yaegi/_test/ct"
|
import _ "github.com/traefik/yaegi/_test/ct"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("bye")
|
println("bye")
|
||||||
|
|||||||
5
_test/testdata/redeclaration-global7/src/guthib.com/bar/quux.go
vendored
Normal file
5
_test/testdata/redeclaration-global7/src/guthib.com/bar/quux.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package quux
|
||||||
|
|
||||||
|
func Quux() string {
|
||||||
|
return "bar"
|
||||||
|
}
|
||||||
5
_test/testdata/redeclaration-global7/src/guthib.com/baz/quux.go
vendored
Normal file
5
_test/testdata/redeclaration-global7/src/guthib.com/baz/quux.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package quux
|
||||||
|
|
||||||
|
func Quux() string {
|
||||||
|
return "baz"
|
||||||
|
}
|
||||||
7
_test/testdata/redeclaration-global7/src/guthib.com/tata/tutu.go
vendored
Normal file
7
_test/testdata/redeclaration-global7/src/guthib.com/tata/tutu.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package tutu
|
||||||
|
|
||||||
|
import "guthib.com/baz"
|
||||||
|
|
||||||
|
func Quux() string {
|
||||||
|
return quux.Quux()
|
||||||
|
}
|
||||||
7
_test/testdata/redeclaration-global7/src/guthib.com/toto/titi.go
vendored
Normal file
7
_test/testdata/redeclaration-global7/src/guthib.com/toto/titi.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package titi
|
||||||
|
|
||||||
|
import "guthib.com/bar"
|
||||||
|
|
||||||
|
func Quux() string {
|
||||||
|
return quux.Quux()
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/containous/yaegi/_test/vars"
|
import "github.com/traefik/yaegi/_test/vars"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println(vars.A)
|
println(vars.A)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Usage:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
goexports github.com/containous/yaegi/interp
|
goexports github.com/traefik/yaegi/interp
|
||||||
|
|
||||||
The same goexport program is used for all target operating systems and architectures.
|
The same goexport program is used for all target operating systems and architectures.
|
||||||
The GOOS and GOARCH environment variables set the desired target.
|
The GOOS and GOARCH environment variables set the desired target.
|
||||||
@@ -30,7 +30,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/yaegi/extract"
|
"github.com/traefik/yaegi/extract"
|
||||||
)
|
)
|
||||||
|
|
||||||
// genLicense generates the correct LICENSE header text from the provided
|
// genLicense generates the correct LICENSE header text from the provided
|
||||||
@@ -108,9 +108,9 @@ func main() {
|
|||||||
|
|
||||||
var oFile string
|
var oFile string
|
||||||
if pkgIdent == "syscall" {
|
if pkgIdent == "syscall" {
|
||||||
oFile = strings.Replace(importPath, "/", "_", -1) + "_" + goos + "_" + goarch + ".go"
|
oFile = strings.ReplaceAll(importPath, "/", "_") + "_" + goos + "_" + goarch + ".go"
|
||||||
} else {
|
} else {
|
||||||
oFile = strings.Replace(importPath, "/", "_", -1) + ".go"
|
oFile = strings.ReplaceAll(importPath, "/", "_") + ".go"
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := runtime.Version()
|
prefix := runtime.Version()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/yaegi/extract"
|
"github.com/traefik/yaegi/extract"
|
||||||
)
|
)
|
||||||
|
|
||||||
func extractCmd(arg []string) error {
|
func extractCmd(arg []string) error {
|
||||||
@@ -58,7 +58,7 @@ func extractCmd(arg []string) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
oFile := strings.Replace(importPath, "/", "_", -1) + ".go"
|
oFile := strings.ReplaceAll(importPath, "/", "_") + ".go"
|
||||||
f, err := os.Create(oFile)
|
f, err := os.Create(oFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func help(arg []string) error {
|
|||||||
case Run:
|
case Run:
|
||||||
return run([]string{"-h"})
|
return run([]string{"-h"})
|
||||||
case Test:
|
case Test:
|
||||||
return fmt.Errorf("help: test not implemented")
|
return test([]string{"-h"})
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
|
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
"github.com/containous/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
"github.com/containous/yaegi/stdlib/syscall"
|
"github.com/traefik/yaegi/stdlib/syscall"
|
||||||
"github.com/containous/yaegi/stdlib/unrestricted"
|
"github.com/traefik/yaegi/stdlib/unrestricted"
|
||||||
"github.com/containous/yaegi/stdlib/unsafe"
|
"github.com/traefik/yaegi/stdlib/unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func run(arg []string) error {
|
func run(arg []string) error {
|
||||||
@@ -56,14 +56,16 @@ func run(arg []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cmd != "" {
|
if cmd != "" {
|
||||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
_, err = i.Eval(cmd)
|
||||||
|
showError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
if interactive || cmd == "" {
|
if interactive || cmd == "" {
|
||||||
i.REPL(os.Stdin, os.Stdout)
|
_, err = i.REPL()
|
||||||
|
showError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip first os arg to set command line as expected by interpreted main
|
// Skip first os arg to set command line as expected by interpreted main
|
||||||
@@ -71,40 +73,27 @@ func run(arg []string) error {
|
|||||||
os.Args = arg[1:]
|
os.Args = arg[1:]
|
||||||
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
|
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
|
||||||
|
|
||||||
if isPackageName(path) {
|
if isFile(path) {
|
||||||
err = runPackage(i, path)
|
err = runFile(i, path)
|
||||||
} else {
|
} else {
|
||||||
if isDir(path) {
|
_, err = i.EvalPath(path)
|
||||||
err = runDir(i, path)
|
|
||||||
} else {
|
|
||||||
err = runFile(i, path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
showError(err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if interactive {
|
if interactive {
|
||||||
i.REPL(os.Stdin, os.Stdout)
|
_, err = i.REPL()
|
||||||
|
showError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPackageName(path string) bool {
|
func isFile(path string) bool {
|
||||||
return !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "./") && !strings.HasPrefix(path, "../")
|
fi, err := os.Stat(path)
|
||||||
}
|
return err == nil && fi.Mode().IsRegular()
|
||||||
|
|
||||||
func isDir(path string) bool {
|
|
||||||
fi, err := os.Lstat(path)
|
|
||||||
return err == nil && fi.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func runPackage(i *interp.Interpreter, path string) error {
|
|
||||||
return fmt.Errorf("runPackage not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDir(i *interp.Interpreter, path string) error {
|
|
||||||
return fmt.Errorf("runDir not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFile(i *interp.Interpreter, path string) error {
|
func runFile(i *interp.Interpreter, path string) error {
|
||||||
@@ -116,17 +105,21 @@ func runFile(i *interp.Interpreter, path string) error {
|
|||||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||||
s = strings.Replace(s, "#!", "//", 1)
|
s = strings.Replace(s, "#!", "//", 1)
|
||||||
i.REPL(strings.NewReader(s), os.Stdout)
|
_, err = i.Eval(s)
|
||||||
} else {
|
return err
|
||||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
}
|
||||||
i.Name = path
|
|
||||||
_, err := i.Eval(s)
|
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||||
if err != nil {
|
_, err = i.EvalPath(path)
|
||||||
fmt.Println(err)
|
return err
|
||||||
if p, ok := err.(interp.Panic); ok {
|
}
|
||||||
fmt.Println(string(p.Stack))
|
|
||||||
}
|
func showError(err error) {
|
||||||
}
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
if p, ok := err.(interp.Panic); ok {
|
||||||
|
fmt.Fprintln(os.Stderr, string(p.Stack))
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
131
cmd/yaegi/test.go
Normal file
131
cmd/yaegi/test.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/traefik/yaegi/interp"
|
||||||
|
"github.com/traefik/yaegi/stdlib"
|
||||||
|
"github.com/traefik/yaegi/stdlib/syscall"
|
||||||
|
"github.com/traefik/yaegi/stdlib/unrestricted"
|
||||||
|
"github.com/traefik/yaegi/stdlib/unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func test(arg []string) (err error) {
|
||||||
|
var (
|
||||||
|
bench string
|
||||||
|
benchmem bool
|
||||||
|
benchtime string
|
||||||
|
count string
|
||||||
|
cpu string
|
||||||
|
failfast bool
|
||||||
|
run string
|
||||||
|
short bool
|
||||||
|
tags string
|
||||||
|
useUnrestricted bool
|
||||||
|
useUnsafe bool
|
||||||
|
useSyscall bool
|
||||||
|
timeout string
|
||||||
|
verbose bool
|
||||||
|
)
|
||||||
|
|
||||||
|
tflag := flag.NewFlagSet("test", flag.ContinueOnError)
|
||||||
|
tflag.StringVar(&bench, "bench", "", "Run only those benchmarks matching a regular expression.")
|
||||||
|
tflag.BoolVar(&benchmem, "benchmem", false, "Print memory allocation statistics for benchmarks.")
|
||||||
|
tflag.StringVar(&benchtime, "benchtime", "", "Run enough iterations of each benchmark to take t.")
|
||||||
|
tflag.StringVar(&count, "count", "", "Run each test and benchmark n times (default 1).")
|
||||||
|
tflag.StringVar(&cpu, "cpu", "", "Specify a list of GOMAXPROCS values for which the tests or benchmarks should be executed.")
|
||||||
|
tflag.BoolVar(&failfast, "failfast", false, "Do not start new tests after the first test failure.")
|
||||||
|
tflag.StringVar(&run, "run", "", "Run only those tests matching a regular expression.")
|
||||||
|
tflag.BoolVar(&short, "short", false, "Tell long-running tests to shorten their run time.")
|
||||||
|
tflag.StringVar(&tags, "tags", "", "Set a list of build tags.")
|
||||||
|
tflag.StringVar(&timeout, "timeout", "", "If a test binary runs longer than duration d, panic.")
|
||||||
|
tflag.BoolVar(&useUnrestricted, "unrestricted", false, "Include unrestricted symbols.")
|
||||||
|
tflag.BoolVar(&useUnsafe, "unsafe", false, "Include usafe symbols.")
|
||||||
|
tflag.BoolVar(&useSyscall, "syscall", false, "Include syscall symbols.")
|
||||||
|
tflag.BoolVar(&verbose, "v", false, "Verbose output: log all tests as they are run.")
|
||||||
|
tflag.Usage = func() {
|
||||||
|
fmt.Println("Usage: yaegi test [options] [path]")
|
||||||
|
fmt.Println("Options:")
|
||||||
|
tflag.PrintDefaults()
|
||||||
|
}
|
||||||
|
if err = tflag.Parse(arg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
args := tflag.Args()
|
||||||
|
path := "."
|
||||||
|
if len(args) > 0 {
|
||||||
|
path = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite os.Args with correct flags to setup testing.Init.
|
||||||
|
tf := []string{""}
|
||||||
|
if bench != "" {
|
||||||
|
tf = append(tf, "-test.bench", bench)
|
||||||
|
}
|
||||||
|
if benchmem {
|
||||||
|
tf = append(tf, "-test.benchmem")
|
||||||
|
}
|
||||||
|
if benchtime != "" {
|
||||||
|
tf = append(tf, "-test.benchtime", benchtime)
|
||||||
|
}
|
||||||
|
if count != "" {
|
||||||
|
tf = append(tf, "-test.count", count)
|
||||||
|
}
|
||||||
|
if cpu != "" {
|
||||||
|
tf = append(tf, "-test.cpu", cpu)
|
||||||
|
}
|
||||||
|
if failfast {
|
||||||
|
tf = append(tf, "-test.failfast")
|
||||||
|
}
|
||||||
|
if run != "" {
|
||||||
|
tf = append(tf, "-test.run", run)
|
||||||
|
}
|
||||||
|
if short {
|
||||||
|
tf = append(tf, "-test.short")
|
||||||
|
}
|
||||||
|
if timeout != "" {
|
||||||
|
tf = append(tf, "-test.timeout", timeout)
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
tf = append(tf, "-test.v")
|
||||||
|
}
|
||||||
|
testing.Init()
|
||||||
|
os.Args = tf
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||||
|
i.Use(stdlib.Symbols)
|
||||||
|
i.Use(interp.Symbols)
|
||||||
|
if useSyscall {
|
||||||
|
i.Use(syscall.Symbols)
|
||||||
|
}
|
||||||
|
if useUnrestricted {
|
||||||
|
i.Use(unrestricted.Symbols)
|
||||||
|
}
|
||||||
|
if useUnsafe {
|
||||||
|
i.Use(unsafe.Symbols)
|
||||||
|
}
|
||||||
|
if err = i.EvalTest(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarks := []testing.InternalBenchmark{}
|
||||||
|
tests := []testing.InternalTest{}
|
||||||
|
for name, sym := range i.Symbols(path) {
|
||||||
|
switch fun := sym.Interface().(type) {
|
||||||
|
case func(*testing.B):
|
||||||
|
benchmarks = append(benchmarks, testing.InternalBenchmark{name, fun})
|
||||||
|
case func(*testing.T):
|
||||||
|
tests = append(tests, testing.InternalTest{name, fun})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testing.Main(regexp.MatchString, tests, benchmarks, nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@ func main() {
|
|||||||
case Run:
|
case Run:
|
||||||
err = run(os.Args[2:])
|
err = run(os.Args[2:])
|
||||||
case Test:
|
case Test:
|
||||||
err = fmt.Errorf("test not implemented")
|
err = test(os.Args[2:])
|
||||||
default:
|
default:
|
||||||
// If no command is given, fallback to default "run" command.
|
// If no command is given, fallback to default "run" command.
|
||||||
// This allows scripts starting with "#!/usr/bin/env yaegi",
|
// This allows scripts starting with "#!/usr/bin/env yaegi",
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -108,8 +110,8 @@ func TestYaegiCmdCancel(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if outBuf.String() != "context canceled\n" {
|
if strings.TrimSuffix(errBuf.String(), "\n") != context.Canceled.Error() {
|
||||||
t.Errorf("unexpected output: %q", &outBuf)
|
t.Errorf("unexpected error: %q", &errBuf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package clos1
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
"github.com/containous/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFunctionCall(t *testing.T) {
|
func TestFunctionCall(t *testing.T) {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
"github.com/containous/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetFunc(t *testing.T) {
|
func TestGetFunc(t *testing.T) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package fromage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
return fmt.Sprintf("Fromage %s", cheese.Hello())
|
return fmt.Sprintf("Fromage %s", cheese.Hello())
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package fromage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
"guthib.com/containous/fromage/couteau"
|
"guthib.com/traefik/fromage/couteau"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
return fmt.Sprintf("Fromage %s %s", couteau.Hello(), cheese.Hello())
|
return fmt.Sprintf("Fromage %s %s", couteau.Hello(), cheese.Hello())
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package cheese
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"guthib.com/containous/cheese/vin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Hello() string {
|
|
||||||
return fmt.Sprintf("Cheese %s", vin.Hello())
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,8 @@ package fromage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
"guthib.com/containous/fromage/couteau"
|
"guthib.com/traefik/fromage/couteau"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
@@ -3,7 +3,7 @@ package cheese
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese/vin"
|
"guthib.com/traefik/cheese/vin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package cheese
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese/vin"
|
"guthib.com/traefik/cheese/vin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
@@ -3,8 +3,8 @@ package fromage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
"guthib.com/containous/fromage/couteau"
|
"guthib.com/traefik/fromage/couteau"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package fromage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
"guthib.com/containous/fromage/couteau"
|
"guthib.com/traefik/fromage/couteau"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
11
example/pkg/_pkg6/src/guthib.com/traefik/cheese/cheese.go
Normal file
11
example/pkg/_pkg6/src/guthib.com/traefik/cheese/cheese.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package cheese
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"guthib.com/traefik/cheese/vin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Hello() string {
|
||||||
|
return fmt.Sprintf("Cheese %s", vin.Hello())
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/vin"
|
"guthib.com/traefik/vin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Here() string {
|
func Here() string {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package cheese
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/fromage"
|
"guthib.com/traefik/fromage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
return fmt.Sprintf("cheese %s", fromage.Hello())
|
return fmt.Sprintf("cheese %s", fromage.Hello())
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@ package vin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"guthib.com/containous/cheese"
|
"guthib.com/traefik/cheese"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hello() string {
|
func Hello() string {
|
||||||
return fmt.Sprintf("vin %s", cheese.Hello())
|
return fmt.Sprintf("vin %s", cheese.Hello())
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,12 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
"github.com/containous/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPackages(t *testing.T) {
|
func TestPackages(t *testing.T) {
|
||||||
@@ -105,46 +103,16 @@ func TestPackages(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init go interpreter
|
var stdout, stderr bytes.Buffer
|
||||||
i := interp.New(interp.Options{GoPath: goPath})
|
i := interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
|
||||||
i.Use(stdlib.Symbols) // Use binary standard library
|
i.Use(stdlib.Symbols) // Use binary standard library
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
if test.evalFile != "" {
|
if test.evalFile != "" {
|
||||||
// setting i.Name as this is how it's actually done in cmd/yaegi
|
if _, err := i.EvalPath(test.evalFile); err != nil {
|
||||||
i.Name = test.evalFile
|
|
||||||
data, err := ioutil.ReadFile(test.evalFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mpl): this is brittle if we do concurrent tests and stuff, do better later.
|
|
||||||
stdout := os.Stdout
|
|
||||||
defer func() { os.Stdout = stdout }()
|
|
||||||
pr, pw, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
os.Stdout = pw
|
|
||||||
|
|
||||||
if _, err := i.Eval(string(data)); err != nil {
|
|
||||||
fatalStderrf(t, "%v", err)
|
fatalStderrf(t, "%v", err)
|
||||||
}
|
}
|
||||||
|
msg = stdout.String()
|
||||||
var buf bytes.Buffer
|
|
||||||
errC := make(chan error)
|
|
||||||
go func() {
|
|
||||||
_, err := io.Copy(&buf, pr)
|
|
||||||
errC <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := pw.Close(); err != nil {
|
|
||||||
fatalStderrf(t, "%v", err)
|
|
||||||
}
|
|
||||||
if err := <-errC; err != nil {
|
|
||||||
fatalStderrf(t, "%v", err)
|
|
||||||
}
|
|
||||||
msg = buf.String()
|
|
||||||
} else {
|
} else {
|
||||||
// Load pkg from sources
|
// Load pkg from sources
|
||||||
topImport := "github.com/foo/pkg"
|
topImport := "github.com/foo/pkg"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
const model = `// Code generated by 'github.com/containous/yaegi/extract {{.PkgName}}'. DO NOT EDIT.
|
const model = `// Code generated by 'github.com/traefik/yaegi/extract {{.PkgName}}'. DO NOT EDIT.
|
||||||
|
|
||||||
{{.License}}
|
{{.License}}
|
||||||
|
|
||||||
@@ -216,6 +216,19 @@ func genContent(dest, importPath, license string, p *types.Package, skip map[str
|
|||||||
buildTags += ",!windows,!nacl,!plan9"
|
buildTags += ",!windows,!nacl,!plan9"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if importPath == "syscall" {
|
||||||
|
// As per https://golang.org/cmd/go/#hdr-Build_constraints,
|
||||||
|
// using GOOS=android also matches tags and files for GOOS=linux,
|
||||||
|
// so exclude it explicitly to avoid collisions (issue #843).
|
||||||
|
// Also using GOOS=illumos matches tags and files for GOOS=solaris.
|
||||||
|
switch os.Getenv("GOOS") {
|
||||||
|
case "android":
|
||||||
|
buildTags += ",!linux"
|
||||||
|
case "illumos":
|
||||||
|
buildTags += ",!solaris"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Dest": dest,
|
"Dest": dest,
|
||||||
@@ -247,6 +260,9 @@ func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
|||||||
str string
|
str string
|
||||||
)
|
)
|
||||||
switch val.Kind() {
|
switch val.Kind() {
|
||||||
|
case constant.String:
|
||||||
|
tok = "STRING"
|
||||||
|
str = val.ExactString()
|
||||||
case constant.Int:
|
case constant.Int:
|
||||||
tok = "INT"
|
tok = "INT"
|
||||||
str = val.ExactString()
|
str = val.ExactString()
|
||||||
@@ -269,7 +285,7 @@ func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
|||||||
imports["go/constant"] = true
|
imports["go/constant"] = true
|
||||||
imports["go/token"] = true
|
imports["go/token"] = true
|
||||||
|
|
||||||
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
|
return fmt.Sprintf("constant.MakeFromLiteral(%q, token.%s, 0)", str, tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
// importPath checks whether pkgIdent is an existing directory relative to
|
// importPath checks whether pkgIdent is an existing directory relative to
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var expectedOutput = `// Code generated by 'github.com/containous/yaegi/extract guthib.com/baz'. DO NOT EDIT.
|
var expectedOutput = `// Code generated by 'github.com/traefik/yaegi/extract guthib.com/baz'. DO NOT EDIT.
|
||||||
|
|
||||||
// +build BUILD_TAGS
|
// +build BUILD_TAGS
|
||||||
|
|
||||||
|
|||||||
10
generate.go
10
generate.go
@@ -1,7 +1,7 @@
|
|||||||
package yaegi
|
package yaegi
|
||||||
|
|
||||||
//go:generate go generate github.com/containous/yaegi/interp
|
//go:generate go generate github.com/traefik/yaegi/interp
|
||||||
//go:generate go generate github.com/containous/yaegi/cmd/goexports
|
//go:generate go generate github.com/traefik/yaegi/cmd/goexports
|
||||||
//go:generate go generate github.com/containous/yaegi/stdlib
|
//go:generate go generate github.com/traefik/yaegi/stdlib
|
||||||
//go:generate go generate github.com/containous/yaegi/stdlib/syscall
|
//go:generate go generate github.com/traefik/yaegi/stdlib/syscall
|
||||||
//go:generate go generate github.com/containous/yaegi/stdlib/unsafe
|
//go:generate go generate github.com/traefik/yaegi/stdlib/unsafe
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -1,3 +1,3 @@
|
|||||||
module github.com/containous/yaegi
|
module github.com/traefik/yaegi
|
||||||
|
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func {{$name}}(n *node) {
|
|||||||
case reflect.String:
|
case reflect.String:
|
||||||
switch {
|
switch {
|
||||||
case c0.rval.IsValid():
|
case c0.rval.IsValid():
|
||||||
s0 := c0.rval.String()
|
s0 := vString(c0.rval)
|
||||||
v1 := genValue(c1)
|
v1 := genValue(c1)
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
dest(f).SetString(s0 {{$op.Name}} v1(f).String())
|
dest(f).SetString(s0 {{$op.Name}} v1(f).String())
|
||||||
@@ -40,7 +40,7 @@ func {{$name}}(n *node) {
|
|||||||
}
|
}
|
||||||
case c1.rval.IsValid():
|
case c1.rval.IsValid():
|
||||||
v0 := genValue(c0)
|
v0 := genValue(c0)
|
||||||
s1 := c1.rval.String()
|
s1 := vString(c1.rval)
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
dest(f).SetString(v0(f).String() {{$op.Name}} s1)
|
dest(f).SetString(v0(f).String() {{$op.Name}} s1)
|
||||||
return next
|
return next
|
||||||
@@ -202,7 +202,7 @@ func {{$name}}Const(n *node) {
|
|||||||
{{- end}}
|
{{- end}}
|
||||||
{{- if $op.Str}}
|
{{- if $op.Str}}
|
||||||
case isString(t):
|
case isString(t):
|
||||||
n.rval.SetString(v0.String() {{$op.Name}} v1.String())
|
n.rval.SetString(vString(v0) {{$op.Name}} vString(v1))
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- if $op.Float}}
|
{{- if $op.Float}}
|
||||||
case isComplex(t):
|
case isComplex(t):
|
||||||
@@ -233,7 +233,7 @@ func {{$name}}Assign(n *node) {
|
|||||||
{{- if $op.Str}}
|
{{- if $op.Str}}
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
v0 := genValueString(c0)
|
v0 := genValueString(c0)
|
||||||
v1 := c1.rval.String()
|
v1 := vString(c1.rval)
|
||||||
n.exec = func(f *frame) bltn {
|
n.exec = func(f *frame) bltn {
|
||||||
v, s := v0(f)
|
v, s := v0(f)
|
||||||
v.SetString(s {{$op.Name}} v1)
|
v.SetString(s {{$op.Name}} v1)
|
||||||
@@ -496,7 +496,7 @@ func {{$name}}(n *node) {
|
|||||||
case isString(t0) || isString(t1):
|
case isString(t0) || isString(t1):
|
||||||
switch {
|
switch {
|
||||||
case c0.rval.IsValid():
|
case c0.rval.IsValid():
|
||||||
s0 := c0.rval.String()
|
s0 := vString(c0.rval)
|
||||||
v1 := genValueString(n.child[1])
|
v1 := genValueString(n.child[1])
|
||||||
if n.fnext != nil {
|
if n.fnext != nil {
|
||||||
fnext := getExec(n.fnext)
|
fnext := getExec(n.fnext)
|
||||||
@@ -517,7 +517,7 @@ func {{$name}}(n *node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case c1.rval.IsValid():
|
case c1.rval.IsValid():
|
||||||
s1 := c1.rval.String()
|
s1 := vString(c1.rval)
|
||||||
v0 := genValueString(n.child[0])
|
v0 := genValueString(n.child[0])
|
||||||
if n.fnext != nil {
|
if n.fnext != nil {
|
||||||
fnext := getExec(n.fnext)
|
fnext := getExec(n.fnext)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -339,30 +341,48 @@ func (interp *Interpreter) firstToken(src string) token.Token {
|
|||||||
return tok
|
return tok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ignoreError(err error, src string) bool {
|
||||||
|
se, ok := err.(scanner.ErrorList)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(se) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ignoreScannerError(se[0], src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapInMain(src string) string {
|
||||||
|
return fmt.Sprintf("package main; func main() {%s}", src)
|
||||||
|
}
|
||||||
|
|
||||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||||
// processing of CFG, in order to accommodate forward type declarations
|
// processing of CFG, in order to accommodate forward type declarations.
|
||||||
|
|
||||||
// ast parses src string containing Go code and generates the corresponding AST.
|
// ast parses src string containing Go code and generates the corresponding AST.
|
||||||
// The package name and the AST root node are returned.
|
// The package name and the AST root node are returned.
|
||||||
func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
// The given name is used to set the filename of the relevant source file in the
|
||||||
inRepl := name == ""
|
// interpreter's FileSet.
|
||||||
|
func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error) {
|
||||||
var inFunc bool
|
var inFunc bool
|
||||||
var mode parser.Mode
|
mode := parser.DeclarationErrors
|
||||||
|
|
||||||
// Allow incremental parsing of declarations or statements, by inserting
|
// Allow incremental parsing of declarations or statements, by inserting
|
||||||
// them in a pseudo file package or function. Those statements or
|
// them in a pseudo file package or function. Those statements or
|
||||||
// declarations will be always evaluated in the global scope
|
// declarations will be always evaluated in the global scope.
|
||||||
if inRepl {
|
var tok token.Token
|
||||||
switch interp.firstToken(src) {
|
if inc {
|
||||||
|
tok = interp.firstToken(src)
|
||||||
|
switch tok {
|
||||||
case token.PACKAGE:
|
case token.PACKAGE:
|
||||||
// nothing to do
|
// nothing to do.
|
||||||
case token.CONST, token.FUNC, token.IMPORT, token.TYPE, token.VAR:
|
case token.CONST, token.FUNC, token.IMPORT, token.TYPE, token.VAR:
|
||||||
src = "package main;" + src
|
src = "package main;" + src
|
||||||
default:
|
default:
|
||||||
inFunc = true
|
inFunc = true
|
||||||
src = "package main; func main() {" + src + "}"
|
src = wrapInMain(src)
|
||||||
}
|
}
|
||||||
// Parse comments in REPL mode, to allow tag setting
|
// Parse comments in REPL mode, to allow tag setting.
|
||||||
mode |= parser.ParseComments
|
mode |= parser.ParseComments
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +392,22 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
|||||||
|
|
||||||
f, err := parser.ParseFile(interp.fset, name, src, mode)
|
f, err := parser.ParseFile(interp.fset, name, src, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
// only retry if we're on an expression/statement about a func
|
||||||
|
if !inc || tok != token.FUNC {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
// do not bother retrying if we know it's an error we're going to ignore later on.
|
||||||
|
if ignoreError(err, src) {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
// do not lose initial error, in case retrying fails.
|
||||||
|
initialError := err
|
||||||
|
// retry with default source code "wrapping", in the main function scope.
|
||||||
|
src := wrapInMain(strings.TrimPrefix(src, "package main;"))
|
||||||
|
f, err = parser.ParseFile(interp.fset, name, src, mode)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, initialError
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setYaegiTags(&interp.context, f.Comments)
|
setYaegiTags(&interp.context, f.Comments)
|
||||||
@@ -487,20 +522,12 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
|||||||
n.ident = a.Value
|
n.ident = a.Value
|
||||||
switch a.Kind {
|
switch a.Kind {
|
||||||
case token.CHAR:
|
case token.CHAR:
|
||||||
|
// Char cannot be converted to a const here as we cannot tell the type.
|
||||||
v, _, _, _ := strconv.UnquoteChar(a.Value[1:len(a.Value)-1], '\'')
|
v, _, _, _ := strconv.UnquoteChar(a.Value[1:len(a.Value)-1], '\'')
|
||||||
n.rval = reflect.ValueOf(v)
|
n.rval = reflect.ValueOf(v)
|
||||||
case token.FLOAT:
|
case token.FLOAT, token.IMAG, token.INT, token.STRING:
|
||||||
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
||||||
n.rval = reflect.ValueOf(v)
|
n.rval = reflect.ValueOf(v)
|
||||||
case token.IMAG:
|
|
||||||
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
|
||||||
n.rval = reflect.ValueOf(v)
|
|
||||||
case token.INT:
|
|
||||||
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
|
||||||
n.rval = reflect.ValueOf(v)
|
|
||||||
case token.STRING:
|
|
||||||
v, _ := strconv.Unquote(a.Value)
|
|
||||||
n.rval = reflect.ValueOf(v)
|
|
||||||
}
|
}
|
||||||
st.push(n, nod)
|
st.push(n, nod)
|
||||||
|
|
||||||
@@ -859,10 +886,13 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
|||||||
})
|
})
|
||||||
if inFunc {
|
if inFunc {
|
||||||
// Incremental parsing: statements were inserted in a pseudo function.
|
// Incremental parsing: statements were inserted in a pseudo function.
|
||||||
// Set root to function body so its statements are evaluated in global scope
|
// Set root to function body so its statements are evaluated in global scope.
|
||||||
root = root.child[1].child[3]
|
root = root.child[1].child[3]
|
||||||
root.anc = nil
|
root.anc = nil
|
||||||
}
|
}
|
||||||
|
if pkgName == "" {
|
||||||
|
return "", root, errors.New("no package name found")
|
||||||
|
}
|
||||||
return pkgName, root, err
|
return pkgName, root, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -129,12 +130,15 @@ func goMinorVersion(ctx *build.Context) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skipFile returns true if file should be skipped.
|
// skipFile returns true if file should be skipped.
|
||||||
func skipFile(ctx *build.Context, p string) bool {
|
func skipFile(ctx *build.Context, p string, skipTest bool) bool {
|
||||||
if !strings.HasSuffix(p, ".go") {
|
if !strings.HasSuffix(p, ".go") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
p = strings.TrimSuffix(path.Base(p), ".go")
|
p = strings.TrimSuffix(path.Base(p), ".go")
|
||||||
if strings.HasSuffix(p, "_test") {
|
if pp := filepath.Base(p); strings.HasPrefix(pp, "_") || strings.HasPrefix(pp, ".") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if skipTest && strings.HasSuffix(p, "_test") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
i := strings.Index(p, "_")
|
i := strings.Index(p, "_")
|
||||||
@@ -158,6 +162,7 @@ var knownOs = map[string]bool{
|
|||||||
"darwin": true,
|
"darwin": true,
|
||||||
"dragonfly": true,
|
"dragonfly": true,
|
||||||
"freebsd": true,
|
"freebsd": true,
|
||||||
|
"illumos": true,
|
||||||
"js": true,
|
"js": true,
|
||||||
"linux": true,
|
"linux": true,
|
||||||
"nacl": true,
|
"nacl": true,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func TestBuildFile(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.src, func(t *testing.T) {
|
t.Run(test.src, func(t *testing.T) {
|
||||||
if r := skipFile(&ctx, test.src); r != test.res {
|
if r := skipFile(&ctx, test.src, NoTest); r != test.res {
|
||||||
t.Errorf("got %v, want %v", r, test.res)
|
t.Errorf("got %v, want %v", r, test.res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
249
interp/cfg.go
249
interp/cfg.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,9 +39,9 @@ var constOp = map[action]func(*node){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var constBltn = map[string]func(*node){
|
var constBltn = map[string]func(*node){
|
||||||
"complex": complexConst,
|
bltnComplex: complexConst,
|
||||||
"imag": imagConst,
|
bltnImag: imagConst,
|
||||||
"real": realConst,
|
bltnReal: realConst,
|
||||||
}
|
}
|
||||||
|
|
||||||
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||||
@@ -51,8 +52,8 @@ const nilIdent = "nil"
|
|||||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||||
// variables. A list of nodes of init functions is returned.
|
// variables. A list of nodes of init functions is returned.
|
||||||
// Following this pass, the CFG is ready to run.
|
// Following this pass, the CFG is ready to run.
|
||||||
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||||
sc := interp.initScopePkg(pkgID)
|
sc := interp.initScopePkg(importPath)
|
||||||
check := typecheck{}
|
check := typecheck{}
|
||||||
var initNodes []*node
|
var initNodes []*node
|
||||||
var err error
|
var err error
|
||||||
@@ -333,6 +334,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
recvTypeNode.typ = typ
|
recvTypeNode.typ = typ
|
||||||
|
n.child[2].typ.recv = typ
|
||||||
|
n.typ.recv = typ
|
||||||
index := sc.add(typ)
|
index := sc.add(typ)
|
||||||
if len(fr.child) > 1 {
|
if len(fr.child) > 1 {
|
||||||
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
|
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
|
||||||
@@ -365,19 +368,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
sc.loop = n
|
sc.loop = n
|
||||||
|
|
||||||
case importSpec:
|
case importSpec:
|
||||||
var name, ipath string
|
// already all done in gta
|
||||||
if len(n.child) == 2 {
|
|
||||||
ipath = n.child[1].rval.String()
|
|
||||||
name = n.child[0].ident
|
|
||||||
} else {
|
|
||||||
ipath = n.child[0].rval.String()
|
|
||||||
name = identifier.FindString(ipath)
|
|
||||||
}
|
|
||||||
if interp.binPkg[ipath] != nil && name != "." {
|
|
||||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
|
|
||||||
} else {
|
|
||||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath}}
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case typeSpec:
|
case typeSpec:
|
||||||
@@ -395,12 +386,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := sc.sym[typeName]; exists {
|
|
||||||
// TODO(mpl): find the exact location of the previous declaration
|
|
||||||
err = n.cfgErrorf("%s redeclared in this block", typeName)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.child[1].kind == identExpr {
|
if n.child[1].kind == identExpr {
|
||||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||||
} else {
|
} else {
|
||||||
@@ -415,7 +400,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
// values which may be used in further declarations.
|
// values which may be used in further declarations.
|
||||||
if !sc.global {
|
if !sc.global {
|
||||||
for _, c := range n.child {
|
for _, c := range n.child {
|
||||||
if _, err = interp.cfg(c, pkgID); err != nil {
|
if _, err = interp.cfg(c, importPath); err != nil {
|
||||||
// No error processing here, to allow recovery in subtree nodes.
|
// No error processing here, to allow recovery in subtree nodes.
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -477,6 +462,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
wireChild(n)
|
wireChild(n)
|
||||||
for i := 0; i < n.nleft; i++ {
|
for i := 0; i < n.nleft; i++ {
|
||||||
dest, src := n.child[i], n.child[sbase+i]
|
dest, src := n.child[i], n.child[sbase+i]
|
||||||
|
updateSym := false
|
||||||
var sym *symbol
|
var sym *symbol
|
||||||
var level int
|
var level int
|
||||||
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
||||||
@@ -513,7 +499,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
dest.val = src.val
|
dest.val = src.val
|
||||||
dest.recv = src.recv
|
dest.recv = src.recv
|
||||||
dest.findex = sym.index
|
dest.findex = sym.index
|
||||||
sym.rval = src.rval
|
updateSym = true
|
||||||
} else {
|
} else {
|
||||||
sym, level, _ = sc.lookup(dest.ident)
|
sym, level, _ = sc.lookup(dest.ident)
|
||||||
}
|
}
|
||||||
@@ -523,6 +509,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if updateSym {
|
||||||
|
sym.typ = dest.typ
|
||||||
|
sym.rval = src.rval
|
||||||
|
// As we are updating the sym type, we need to update the sc.type
|
||||||
|
// when the sym has an index.
|
||||||
|
if sym.index >= 0 {
|
||||||
|
sc.types[sym.index] = sym.typ.frameType()
|
||||||
|
}
|
||||||
|
}
|
||||||
n.findex = dest.findex
|
n.findex = dest.findex
|
||||||
n.level = dest.level
|
n.level = dest.level
|
||||||
|
|
||||||
@@ -798,6 +793,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
wireChild(n)
|
wireChild(n)
|
||||||
switch {
|
switch {
|
||||||
case interp.isBuiltinCall(n):
|
case interp.isBuiltinCall(n):
|
||||||
|
err = check.builtin(n.child[0].ident, n, n.child[1:], n.action == aCallSlice)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
n.gen = n.child[0].sym.builtin
|
n.gen = n.child[0].sym.builtin
|
||||||
n.child[0].typ = &itype{cat: builtinT}
|
n.child[0].typ = &itype{cat: builtinT}
|
||||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||||
@@ -816,57 +816,53 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
|
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
|
||||||
op(n) // pre-compute non-assigned constant :
|
op(n) // pre-compute non-assigned constant :
|
||||||
}
|
}
|
||||||
|
|
||||||
case n.child[0].isType(sc):
|
case n.child[0].isType(sc):
|
||||||
// Type conversion expression
|
// Type conversion expression
|
||||||
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
|
c0, c1 := n.child[0], n.child[1]
|
||||||
err = n.cfgErrorf("truncated to integer")
|
switch len(n.child) {
|
||||||
|
case 1:
|
||||||
|
err = n.cfgErrorf("missing argument in conversion to %s", c0.typ.id())
|
||||||
|
case 2:
|
||||||
|
err = check.conversion(c1, c0.typ)
|
||||||
|
default:
|
||||||
|
err = n.cfgErrorf("too many arguments in conversion to %s", c0.typ.id())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
n.action = aConvert
|
n.action = aConvert
|
||||||
switch {
|
switch {
|
||||||
case isInterface(n.child[0].typ) && !n.child[1].isNil():
|
case isInterface(c0.typ) && !c1.isNil():
|
||||||
// Convert to interface: just check that all required methods are defined by concrete type.
|
// 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) {
|
if !c1.typ.implements(c0.typ) {
|
||||||
err = n.cfgErrorf("type %v does not implement interface %v", c1.typ.id(), c0.typ.id())
|
err = n.cfgErrorf("type %v does not implement interface %v", c1.typ.id(), c0.typ.id())
|
||||||
}
|
}
|
||||||
// Pass value as is
|
// Pass value as is
|
||||||
n.gen = nop
|
n.gen = nop
|
||||||
n.typ = n.child[1].typ
|
n.typ = c1.typ
|
||||||
n.findex = n.child[1].findex
|
n.findex = c1.findex
|
||||||
n.level = n.child[1].level
|
n.level = c1.level
|
||||||
n.val = n.child[1].val
|
n.val = c1.val
|
||||||
n.rval = n.child[1].rval
|
n.rval = c1.rval
|
||||||
case n.child[1].rval.IsValid() && isConstType(n.child[0].typ):
|
case c1.rval.IsValid() && isConstType(c0.typ):
|
||||||
n.gen = nop
|
n.gen = nop
|
||||||
n.findex = -1
|
n.findex = -1
|
||||||
n.typ = n.child[0].typ
|
n.typ = c0.typ
|
||||||
n.rval = n.child[1].rval
|
n.rval = c1.rval
|
||||||
convertConstantValue(n)
|
|
||||||
default:
|
default:
|
||||||
n.gen = convert
|
n.gen = convert
|
||||||
n.typ = n.child[0].typ
|
n.typ = c0.typ
|
||||||
n.findex = sc.add(n.typ)
|
n.findex = sc.add(n.typ)
|
||||||
}
|
}
|
||||||
case isBinCall(n):
|
case isBinCall(n):
|
||||||
n.gen = callBin
|
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||||
typ := n.child[0].typ.rtype
|
if err != nil {
|
||||||
numIn := len(n.child) - 1
|
|
||||||
tni := typ.NumIn()
|
|
||||||
if numIn == 1 && isCall(n.child[1]) {
|
|
||||||
numIn = n.child[1].typ.numOut()
|
|
||||||
}
|
|
||||||
if n.child[0].action == aGetMethod {
|
|
||||||
tni-- // The first argument is the method receiver.
|
|
||||||
}
|
|
||||||
if typ.IsVariadic() {
|
|
||||||
tni-- // The last argument could be empty.
|
|
||||||
}
|
|
||||||
if numIn < tni {
|
|
||||||
err = n.cfgErrorf("not enough arguments in call to %v", n.child[0].name())
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.gen = callBin
|
||||||
|
typ := n.child[0].typ.rtype
|
||||||
if typ.NumOut() > 0 {
|
if typ.NumOut() > 0 {
|
||||||
if funcType := n.child[0].typ.val; funcType != nil {
|
if funcType := n.child[0].typ.val; funcType != nil {
|
||||||
// Use the original unwrapped function type, to allow future field and
|
// Use the original unwrapped function type, to allow future field and
|
||||||
@@ -889,6 +885,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if n.child[0].action == aGetFunc {
|
if n.child[0].action == aGetFunc {
|
||||||
// Allocate a frame entry to store the anonymous function definition.
|
// Allocate a frame entry to store the anonymous function definition.
|
||||||
sc.add(n.child[0].typ)
|
sc.add(n.child[0].typ)
|
||||||
@@ -946,18 +947,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
case compositeLitExpr:
|
case compositeLitExpr:
|
||||||
wireChild(n)
|
wireChild(n)
|
||||||
|
|
||||||
underlying := func(t *itype) *itype {
|
|
||||||
for {
|
|
||||||
switch t.cat {
|
|
||||||
case ptrT, aliasT:
|
|
||||||
t = t.val
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
child := n.child
|
child := n.child
|
||||||
if n.nleft > 0 {
|
if n.nleft > 0 {
|
||||||
child = child[1:]
|
child = child[1:]
|
||||||
@@ -965,9 +954,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
|
|
||||||
switch n.typ.cat {
|
switch n.typ.cat {
|
||||||
case arrayT:
|
case arrayT:
|
||||||
err = check.arrayLitExpr(child, underlying(n.typ.val), n.typ.size)
|
err = check.arrayLitExpr(child, n.typ.val, n.typ.size)
|
||||||
case mapT:
|
case mapT:
|
||||||
err = check.mapLitExpr(child, n.typ.key, underlying(n.typ.val))
|
err = check.mapLitExpr(child, n.typ.key, n.typ.val)
|
||||||
case structT:
|
case structT:
|
||||||
err = check.structLitExpr(child, n.typ)
|
err = check.structLitExpr(child, n.typ)
|
||||||
case valueT:
|
case valueT:
|
||||||
@@ -987,7 +976,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
|
|
||||||
n.findex = sc.add(n.typ)
|
n.findex = sc.add(n.typ)
|
||||||
// TODO: Check that composite literal expr matches corresponding type
|
// TODO: Check that composite literal expr matches corresponding type
|
||||||
n.gen = compositeGenerator(n)
|
n.gen = compositeGenerator(n, n.typ)
|
||||||
|
|
||||||
case fallthroughtStmt:
|
case fallthroughtStmt:
|
||||||
if n.anc.kind != caseBody {
|
if n.anc.kind != caseBody {
|
||||||
@@ -1351,11 +1340,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
// Search for field must then be performed on type T only (not *T)
|
// Search for field must then be performed on type T only (not *T)
|
||||||
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
|
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
|
||||||
case ok:
|
case ok:
|
||||||
|
hasRecvType := n.typ.rtype.Kind() != reflect.Interface
|
||||||
n.val = method.Index
|
n.val = method.Index
|
||||||
n.gen = getIndexBinMethod
|
n.gen = getIndexBinMethod
|
||||||
n.action = aGetMethod
|
n.action = aGetMethod
|
||||||
n.recv = &receiver{node: n.child[0]}
|
n.recv = &receiver{node: n.child[0]}
|
||||||
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
|
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
|
||||||
|
if hasRecvType {
|
||||||
|
n.typ.recv = n.typ
|
||||||
|
}
|
||||||
case n.typ.rtype.Kind() == reflect.Ptr:
|
case n.typ.rtype.Kind() == reflect.Ptr:
|
||||||
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
|
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
|
||||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||||
@@ -1375,7 +1368,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
|
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
|
||||||
n.val = m2.Index
|
n.val = m2.Index
|
||||||
n.gen = getIndexBinPtrMethod
|
n.gen = getIndexBinPtrMethod
|
||||||
n.typ = &itype{cat: valueT, rtype: m2.Type}
|
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}}
|
||||||
n.recv = &receiver{node: n.child[0]}
|
n.recv = &receiver{node: n.child[0]}
|
||||||
n.action = aGetMethod
|
n.action = aGetMethod
|
||||||
} else {
|
} else {
|
||||||
@@ -1389,14 +1382,14 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
// Handle pointer on object defined in runtime
|
// Handle pointer on object defined in runtime
|
||||||
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
||||||
n.val = method.Index
|
n.val = method.Index
|
||||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
n.typ = &itype{cat: valueT, rtype: method.Type, recv: n.typ}
|
||||||
n.recv = &receiver{node: n.child[0]}
|
n.recv = &receiver{node: n.child[0]}
|
||||||
n.gen = getIndexBinMethod
|
n.gen = getIndexBinMethod
|
||||||
n.action = aGetMethod
|
n.action = aGetMethod
|
||||||
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
||||||
n.val = method.Index
|
n.val = method.Index
|
||||||
n.gen = getIndexBinMethod
|
n.gen = getIndexBinMethod
|
||||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
n.typ = &itype{cat: valueT, rtype: method.Type, recv: &itype{cat: valueT, rtype: reflect.PtrTo(n.typ.val.rtype)}}
|
||||||
n.recv = &receiver{node: n.child[0]}
|
n.recv = &receiver{node: n.child[0]}
|
||||||
n.action = aGetMethod
|
n.action = aGetMethod
|
||||||
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||||
@@ -1462,7 +1455,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
}
|
}
|
||||||
n.recv = &receiver{node: n.child[0], index: lind}
|
n.recv = &receiver{node: n.child[0], index: lind}
|
||||||
n.val = append([]int{m.Index}, lind...)
|
n.val = append([]int{m.Index}, lind...)
|
||||||
n.typ = &itype{cat: valueT, rtype: m.Type}
|
n.typ = &itype{cat: valueT, rtype: m.Type, recv: n.child[0].typ}
|
||||||
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
||||||
// Handle struct field
|
// Handle struct field
|
||||||
n.val = ti
|
n.val = ti
|
||||||
@@ -1565,6 +1558,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
default:
|
default:
|
||||||
// dereference expression
|
// dereference expression
|
||||||
wireChild(n)
|
wireChild(n)
|
||||||
|
|
||||||
|
err = check.starExpr(n.child[0])
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if c0 := n.child[0]; c0.typ.cat == valueT {
|
if c0 := n.child[0]; c0.typ.cat == valueT {
|
||||||
n.typ = &itype{cat: valueT, rtype: c0.typ.rtype.Elem()}
|
n.typ = &itype{cat: valueT, rtype: c0.typ.rtype.Elem()}
|
||||||
} else {
|
} else {
|
||||||
@@ -1675,29 +1674,43 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
n.child[0].tnext = sbn.start
|
n.child[0].tnext = sbn.start
|
||||||
|
|
||||||
case typeAssertExpr:
|
case typeAssertExpr:
|
||||||
if len(n.child) > 1 {
|
if len(n.child) == 1 {
|
||||||
wireChild(n)
|
// The "o.(type)" is handled by typeSwitch.
|
||||||
c1 := n.child[1]
|
|
||||||
if c1.typ == nil {
|
|
||||||
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n.anc.action != aAssignX {
|
|
||||||
if n.child[0].typ.cat == valueT && isFunc(c1.typ) {
|
|
||||||
// Avoid special wrapping of interfaces and func types.
|
|
||||||
n.typ = &itype{cat: valueT, rtype: c1.typ.TypeOf()}
|
|
||||||
} else {
|
|
||||||
n.typ = c1.typ
|
|
||||||
}
|
|
||||||
n.findex = sc.add(n.typ)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n.gen = nop
|
n.gen = nop
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
wireChild(n)
|
||||||
|
c0, c1 := n.child[0], n.child[1]
|
||||||
|
if c1.typ == nil {
|
||||||
|
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = check.typeAssertionExpr(c0, c1.typ)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.anc.action != aAssignX {
|
||||||
|
if c0.typ.cat == valueT && isFunc(c1.typ) {
|
||||||
|
// Avoid special wrapping of interfaces and func types.
|
||||||
|
n.typ = &itype{cat: valueT, rtype: c1.typ.TypeOf()}
|
||||||
|
} else {
|
||||||
|
n.typ = c1.typ
|
||||||
|
}
|
||||||
|
n.findex = sc.add(n.typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
case sliceExpr:
|
case sliceExpr:
|
||||||
wireChild(n)
|
wireChild(n)
|
||||||
|
|
||||||
|
err = check.sliceExpr(n)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1762,19 +1775,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
|||||||
// Global object allocation is already performed in GTA.
|
// Global object allocation is already performed in GTA.
|
||||||
index = sc.sym[c.ident].index
|
index = sc.sym[c.ident].index
|
||||||
} else {
|
} else {
|
||||||
if sym, exists := sc.sym[c.ident]; exists {
|
|
||||||
if sym.typ.node != nil &&
|
|
||||||
sym.typ.node.anc != nil {
|
|
||||||
// for non-predeclared identifiers (struct, map, etc)
|
|
||||||
prevDecl := n.interp.fset.Position(sym.typ.node.anc.pos)
|
|
||||||
err = n.cfgErrorf("%s redeclared in this block\n\tprevious declaration at %v", c.ident, prevDecl)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// for predeclared identifiers (int, string, etc)
|
|
||||||
// TODO(mpl): find the exact location of the previous declaration in all cases.
|
|
||||||
err = n.cfgErrorf("%s redeclared in this block", c.ident)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
index = sc.add(n.typ)
|
index = sc.add(n.typ)
|
||||||
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
|
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
|
||||||
}
|
}
|
||||||
@@ -1870,7 +1870,12 @@ func childPos(n *node) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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...)
|
pos := n.interp.fset.Position(n.pos)
|
||||||
|
posString := n.interp.fset.Position(n.pos).String()
|
||||||
|
if pos.Filename == DefaultSourceName {
|
||||||
|
posString = strings.TrimPrefix(posString, DefaultSourceName+":")
|
||||||
|
}
|
||||||
|
a = append([]interface{}{posString}, a...)
|
||||||
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
|
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2025,14 +2030,21 @@ func (n *node) isType(sc *scope) bool {
|
|||||||
}
|
}
|
||||||
case selectorExpr:
|
case selectorExpr:
|
||||||
pkg, name := n.child[0].ident, n.child[1].ident
|
pkg, name := n.child[0].ident, n.child[1].ident
|
||||||
if sym, _, ok := sc.lookup(pkg); ok && sym.kind == pkgSym {
|
baseName := filepath.Base(n.interp.fset.Position(n.pos).Filename)
|
||||||
path := sym.typ.path
|
suffixedPkg := filepath.Join(pkg, baseName)
|
||||||
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
|
sym, _, ok := sc.lookup(suffixedPkg)
|
||||||
return true // Imported binary type
|
if !ok {
|
||||||
}
|
return false
|
||||||
if p, ok := n.interp.srcPkg[path]; ok && p[name] != nil && p[name].kind == typeSym {
|
}
|
||||||
return true // Imported source type
|
if sym.kind != pkgSym {
|
||||||
}
|
return false
|
||||||
|
}
|
||||||
|
path := sym.typ.path
|
||||||
|
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
|
||||||
|
return true // Imported binary type
|
||||||
|
}
|
||||||
|
if p, ok := n.interp.srcPkg[path]; ok && p[name] != nil && p[name].kind == typeSym {
|
||||||
|
return true // Imported source type
|
||||||
}
|
}
|
||||||
case identExpr:
|
case identExpr:
|
||||||
return sc.getType(n.ident) != nil
|
return sc.getType(n.ident) != nil
|
||||||
@@ -2342,11 +2354,10 @@ func gotoLabel(s *symbol) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compositeGenerator(n *node) (gen bltnGenerator) {
|
func compositeGenerator(n *node, typ *itype) (gen bltnGenerator) {
|
||||||
switch n.typ.cat {
|
switch typ.cat {
|
||||||
case aliasT, ptrT:
|
case aliasT, ptrT:
|
||||||
n.typ = n.typ.val
|
gen = compositeGenerator(n, n.typ.val)
|
||||||
gen = compositeGenerator(n)
|
|
||||||
case arrayT:
|
case arrayT:
|
||||||
gen = arrayLit
|
gen = arrayLit
|
||||||
case mapT:
|
case mapT:
|
||||||
|
|||||||
@@ -44,4 +44,5 @@ And include files containing
|
|||||||
*/
|
*/
|
||||||
package interp
|
package interp
|
||||||
|
|
||||||
// BUG(marc): Type checking is not implemented yet.
|
// BUG(marc): Support for recursive types is incomplete.
|
||||||
|
// BUG(marc): Support of types implementing multiple interfaces is incomplete.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (n *node) astDot(out io.Writer, name string) {
|
|||||||
var label string
|
var label string
|
||||||
switch n.kind {
|
switch n.kind {
|
||||||
case basicLit, identExpr:
|
case basicLit, identExpr:
|
||||||
label = strings.Replace(n.ident, "\"", "\\\"", -1)
|
label = strings.ReplaceAll(n.ident, "\"", "\\\"")
|
||||||
default:
|
default:
|
||||||
if n.action != aNop {
|
if n.action != aNop {
|
||||||
label = n.action.String()
|
label = n.action.String()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/containous/yaegi/interp"
|
"github.com/traefik/yaegi/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generic example.
|
// Generic example.
|
||||||
|
|||||||
17
interp/export_test.go
Normal file
17
interp/export_test.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package interp
|
||||||
|
|
||||||
|
func (interp *Interpreter) Scopes() map[string]map[string]struct{} {
|
||||||
|
scopes := make(map[string]map[string]struct{})
|
||||||
|
for k, v := range interp.scopes {
|
||||||
|
syms := make(map[string]struct{})
|
||||||
|
for kk := range v.sym {
|
||||||
|
syms[kk] = struct{}{}
|
||||||
|
}
|
||||||
|
scopes[k] = syms
|
||||||
|
}
|
||||||
|
return scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (interp *Interpreter) Packages() map[string]string {
|
||||||
|
return interp.pkgNames
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user