Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0cd93a936 | ||
|
|
611a8c37fa | ||
|
|
e71ddc7edd | ||
|
|
1fe75f149d | ||
|
|
cdc352cee2 | ||
|
|
88569f5df7 | ||
|
|
2ac0c6f70b | ||
|
|
bd4ce37baa | ||
|
|
9c4d3d1e5a | ||
|
|
25c681c1e6 | ||
|
|
9c51f6bb69 | ||
|
|
589b2a0cd2 | ||
|
|
68911f8b4e | ||
|
|
e5a7b0de11 | ||
|
|
563270ca02 | ||
|
|
5eecbe515b | ||
|
|
0a79069dfc | ||
|
|
0c8f538cd9 | ||
|
|
ca80ada849 | ||
|
|
3c6df504df | ||
|
|
98eacf3610 | ||
|
|
16ff52a949 | ||
|
|
640d1429e5 | ||
|
|
659913eebe | ||
|
|
b3766509cc | ||
|
|
bc2b224bae | ||
|
|
9d4685deea | ||
|
|
2a70a71dc2 | ||
|
|
851444453c | ||
|
|
a8b1c2a017 | ||
|
|
d229c2a2c7 | ||
|
|
2f2df7a0f8 | ||
|
|
4058fd8c44 | ||
|
|
097a745e72 | ||
|
|
1f514e63a8 | ||
|
|
a15ecb7176 | ||
|
|
d4aa84f729 | ||
|
|
9977ef6fc6 | ||
|
|
39430c34bb | ||
|
|
4f3481b55c | ||
|
|
55f2fe396a | ||
|
|
108b6fd722 | ||
|
|
a3b2737b5c | ||
|
|
d2c4a36c25 | ||
|
|
f5f44f7ddd | ||
|
|
4d013e4686 | ||
|
|
c11d361953 | ||
|
|
c2ad279643 | ||
|
|
9627782394 | ||
|
|
e00b853971 | ||
|
|
7cfa264dbc | ||
|
|
a6c24a0d13 | ||
|
|
f19b7563ea | ||
|
|
0643762852 | ||
|
|
7323d97023 |
@@ -38,6 +38,11 @@
|
||||
"gocognit",
|
||||
"stylecheck",
|
||||
"gomnd",
|
||||
"testpackage",
|
||||
"goerr113",
|
||||
"nestif",
|
||||
"exhaustive",
|
||||
"nlreturn",
|
||||
]
|
||||
|
||||
[issues]
|
||||
|
||||
@@ -19,14 +19,13 @@ cache:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
- go: 1.15.x
|
||||
env: STABLE=true
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
- CI=1
|
||||
|
||||
go_import_path: github.com/containous/yaegi
|
||||
|
||||
|
||||
25
_test/addr0.go
Normal file
25
_test/addr0.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type extendedRequest struct {
|
||||
http.Request
|
||||
|
||||
Data string
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := extendedRequest{}
|
||||
req := &r.Request
|
||||
|
||||
|
||||
fmt.Println(r)
|
||||
fmt.Println(req)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>} }
|
||||
// &{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>}
|
||||
19
_test/assign13.go
Normal file
19
_test/assign13.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getStr() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := make(map[string]string, 0)
|
||||
m["a"] = fmt.Sprintf("%v", 0.1)
|
||||
m["b"] = string(fmt.Sprintf("%v", 0.1))
|
||||
m["c"] = getStr()
|
||||
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[a:0.1 b:0.1 c:test]
|
||||
16
_test/assign14.go
Normal file
16
_test/assign14.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
var optionsG map[string]string = nil
|
||||
|
||||
var roundG = 30
|
||||
|
||||
func main() {
|
||||
dummy := roundG
|
||||
roundG = dummy + 1
|
||||
println(roundG)
|
||||
println(optionsG == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 31
|
||||
// true
|
||||
11
_test/assign15.go
Normal file
11
_test/assign15.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var c chan<- struct{} = make(chan struct{})
|
||||
var d <-chan struct{} = c
|
||||
|
||||
_ = d
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/assign15.go:5:26: cannot use type chan<- struct{} as type <-chan struct{} in assignment
|
||||
12
_test/chan10.go
Normal file
12
_test/chan10.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
var tick <-chan time.Time = time.Tick(time.Millisecond)
|
||||
_ = tick
|
||||
println("success")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// success
|
||||
14
_test/comp2.go
Normal file
14
_test/comp2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type delta int32
|
||||
|
||||
func main() {
|
||||
a := delta(-1)
|
||||
|
||||
println(a != -1)
|
||||
println(a == -1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
15
_test/composite11.go
Normal file
15
_test/composite11.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := color.NRGBA64{1, 1, 1, 1}
|
||||
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {1 1 1 1}
|
||||
13
_test/const14.go
Normal file
13
_test/const14.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "compress/flate"
|
||||
|
||||
func f1(i int) { println("i:", i) }
|
||||
|
||||
func main() {
|
||||
i := flate.BestSpeed
|
||||
f1(i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// i: 1
|
||||
17
_test/const15.go
Normal file
17
_test/const15.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T1 t1
|
||||
|
||||
type t1 int8
|
||||
|
||||
const (
|
||||
P2 T1 = 2
|
||||
P3 T1 = 3
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(P3)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
32
_test/defer5.go
Normal file
32
_test/defer5.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
func f1() {
|
||||
defer println("f1-begin")
|
||||
f2()
|
||||
defer println("f1-end")
|
||||
}
|
||||
|
||||
func f2() {
|
||||
defer println("f2-begin")
|
||||
f3()
|
||||
defer println("f2-end")
|
||||
}
|
||||
|
||||
func f3() {
|
||||
defer println("f3-begin")
|
||||
println("hello")
|
||||
defer println("f3-end")
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// f3-end
|
||||
// f3-begin
|
||||
// f2-end
|
||||
// f2-begin
|
||||
// f1-end
|
||||
// f1-begin
|
||||
27
_test/defer6.go
Normal file
27
_test/defer6.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
func f1() {
|
||||
defer print("f1-begin ")
|
||||
f2()
|
||||
defer print("f1-end ")
|
||||
}
|
||||
|
||||
func f2() {
|
||||
defer print("f2-begin ")
|
||||
f3()
|
||||
defer print("f2-end ")
|
||||
}
|
||||
|
||||
func f3() {
|
||||
defer print("f3-begin ")
|
||||
print("hello ")
|
||||
defer print("f3-end ")
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
println()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello f3-end f3-begin f2-end f2-begin f1-end f1-begin
|
||||
18
_test/defer7.go
Normal file
18
_test/defer7.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f1(in, out []string) {
|
||||
defer copy(out, in)
|
||||
}
|
||||
|
||||
func main() {
|
||||
in := []string{"foo", "bar"}
|
||||
out := make([]string, 2)
|
||||
f1(in, out)
|
||||
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [foo bar]
|
||||
24
_test/defer8.go
Normal file
24
_test/defer8.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f1(m map[string]string) {
|
||||
defer delete(m, "foo")
|
||||
defer delete(m, "test")
|
||||
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "bat",
|
||||
}
|
||||
f1(m)
|
||||
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[baz:bat foo:bar]
|
||||
// map[baz:bat]
|
||||
21
_test/defer9.go
Normal file
21
_test/defer9.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f1(ch chan string) {
|
||||
defer close(ch)
|
||||
|
||||
ch <- "foo"
|
||||
}
|
||||
|
||||
func main() {
|
||||
ch := make(chan string, 1)
|
||||
f1(ch)
|
||||
|
||||
for s := range ch {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
12
_test/init1.go
Normal file
12
_test/init1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func init() {
|
||||
println("here")
|
||||
}
|
||||
|
||||
func main() {
|
||||
init()
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/init1.go:8:2: undefined: init
|
||||
19
_test/interface44.go
Normal file
19
_test/interface44.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i interface{} = S{a: 1}
|
||||
|
||||
s, ok := i.(S)
|
||||
if !ok {
|
||||
println("bad")
|
||||
return
|
||||
}
|
||||
println(s.a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
13
_test/interface45.go
Normal file
13
_test/interface45.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var i interface{} = 1
|
||||
var s struct{}
|
||||
s, _ = i.(struct{})
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
35
_test/interface46.go
Normal file
35
_test/interface46.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I interface {
|
||||
Foo() string
|
||||
}
|
||||
|
||||
type Printer struct {
|
||||
i I
|
||||
}
|
||||
|
||||
func New(i I) *Printer {
|
||||
return &Printer{
|
||||
i: i,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Printer) Print() {
|
||||
fmt.Println(p.i.Foo())
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) Foo() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func main() {
|
||||
g := New(&T{})
|
||||
g.Print()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
29
_test/issue-735.go
Normal file
29
_test/issue-735.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var optionsG map[string]string
|
||||
|
||||
var roundG int = 30
|
||||
|
||||
func strToInt(s string, defaultValue int) int {
|
||||
n, err := strconv.ParseInt(s, 10, 0)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func main() {
|
||||
optionsG := map[string]string{"round": "12", "b": "one"}
|
||||
roundG = strToInt(optionsG["round"], 50)
|
||||
fmt.Println(roundG)
|
||||
fmt.Println(optionsG)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 12
|
||||
// map[b:one round:12]
|
||||
25
_test/issue-770.go
Normal file
25
_test/issue-770.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "reflect"
|
||||
|
||||
type I interface {
|
||||
Foo() int
|
||||
}
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (t T) Foo() int { return 0 }
|
||||
|
||||
func f(v reflect.Value) int {
|
||||
i := v.Interface().(I)
|
||||
return i.Foo()
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
31
_test/issue-772.go
Normal file
31
_test/issue-772.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func main() {
|
||||
tmpl := template.New("name")
|
||||
|
||||
_, err := tmpl.Parse("{{.Data}}")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(os.Stdout, Message{
|
||||
Data: "Hello, World!!",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, World!!
|
||||
18
_test/issue-775.go
Normal file
18
_test/issue-775.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder.Header().Add("Foo", "Bar")
|
||||
|
||||
for key, value := range recorder.Header() {
|
||||
fmt.Println(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Foo [Bar]
|
||||
39
_test/issue-776.go
Normal file
39
_test/issue-776.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Filter is a filter
|
||||
type Filter interface {
|
||||
Foo()
|
||||
}
|
||||
|
||||
// GIFT is a gift
|
||||
type GIFT struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// New is a new filter list
|
||||
func New(filters ...Filter) *GIFT {
|
||||
return &GIFT{
|
||||
Filters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
// List lists filters
|
||||
func (g *GIFT) List() {
|
||||
fmt.Printf("Hello from List!\n")
|
||||
}
|
||||
|
||||
// MyFilter is one of the filters
|
||||
type MyFilter struct{}
|
||||
|
||||
// Foo is a foo
|
||||
func (f *MyFilter) Foo() {}
|
||||
|
||||
func main() {
|
||||
g := New(&MyFilter{})
|
||||
g.List()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from List!
|
||||
14
_test/issue-782.go
Normal file
14
_test/issue-782.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
from := uint32(2)
|
||||
to := uint32(4)
|
||||
b := a[from:to]
|
||||
fmt.Print(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [3 4]
|
||||
39
_test/issue-784.go
Normal file
39
_test/issue-784.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Filter is a filter interface
|
||||
type Filter interface {
|
||||
Bounds(srcBounds string) (dstBounds string)
|
||||
}
|
||||
|
||||
// GIFT type
|
||||
type GIFT struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// New creates a new filter list and initializes it with the given slice of filters.
|
||||
func New(filters ...Filter) *GIFT {
|
||||
return &GIFT{
|
||||
Filters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
// Bounds calculates the appropriate bounds for the result image after applying all the added filters.
|
||||
func (g *GIFT) Bounds(srcBounds string) (dstBounds string) {
|
||||
dstBounds = srcBounds
|
||||
for _, f := range g.Filters {
|
||||
dstBounds = f.Bounds(dstBounds)
|
||||
}
|
||||
return dstBounds
|
||||
}
|
||||
|
||||
func main() {
|
||||
var filters []Filter
|
||||
bounds := "foo"
|
||||
g := New(filters...)
|
||||
fmt.Println(g.Bounds(bounds))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
12
_test/make2.go
Normal file
12
_test/make2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var s uint = 4
|
||||
t := make([]int, s)
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [0 0 0 0]
|
||||
12
_test/math2.go
Normal file
12
_test/math2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
const c uint64 = 2
|
||||
|
||||
func main() {
|
||||
if c&(1<<(uint64(1))) > 0 {
|
||||
println("yes")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// yes
|
||||
32
_test/math3.go
Normal file
32
_test/math3.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func md5Crypt(password, salt, magic []byte) []byte {
|
||||
d := md5.New()
|
||||
d.Write(password)
|
||||
d.Write(magic)
|
||||
d.Write(salt)
|
||||
|
||||
d2 := md5.New()
|
||||
d2.Write(password)
|
||||
d2.Write(salt)
|
||||
|
||||
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
|
||||
d.Write([]byte{mixin[i%16]})
|
||||
}
|
||||
|
||||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := md5Crypt([]byte("1"), []byte("2"), []byte("3"))
|
||||
|
||||
fmt.Println(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [187 141 73 89 101 229 33 106 226 63 117 234 117 149 230 21]
|
||||
@@ -7,4 +7,4 @@ func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:2: illegal operand types for '+=' operator
|
||||
// 5:2: invalid operation: mismatched types int and float64
|
||||
|
||||
17
_test/op6.go
Normal file
17
_test/op6.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T int
|
||||
|
||||
func (t T) Error() string { return "T: error" }
|
||||
|
||||
var invalidT T
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
if err != invalidT {
|
||||
println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
17
_test/op7.go
Normal file
17
_test/op7.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T int
|
||||
|
||||
func (t T) Error() string { return "T: error" }
|
||||
|
||||
var invalidT T
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
if err > invalidT {
|
||||
println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/op7.go:11:5: invalid operation: operator > not defined on error
|
||||
21
_test/op8.go
Normal file
21
_test/op8.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
type I interface {
|
||||
Get() interface{}
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) Get() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i I = T{}
|
||||
var ei interface{}
|
||||
|
||||
println(i != ei)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
11
_test/op9.go
Normal file
11
_test/op9.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var i complex128 = 1i
|
||||
var f complex128 = 0.4i
|
||||
|
||||
print(i > f)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/op9.go:7:8: invalid operation: operator > not defined on complex128
|
||||
11
_test/range9.go
Normal file
11
_test/range9.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var c chan<- struct{} = make(chan struct{})
|
||||
|
||||
for _ = range c {
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/range9.go:6:16: invalid operation: range c receive from send-only channel
|
||||
14
_test/redeclaration-global0.go
Normal file
14
_test/redeclaration-global0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type time int
|
||||
|
||||
var time string
|
||||
|
||||
func main() {
|
||||
time = "hello"
|
||||
println(time)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global0.go:5:5: time redeclared in this block
|
||||
// previous declaration at ../_test/redeclaration-global0.go:3:6
|
||||
12
_test/redeclaration-global1.go
Normal file
12
_test/redeclaration-global1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var time int
|
||||
|
||||
type time string
|
||||
|
||||
func main() {
|
||||
var t time = "hello"
|
||||
println(t)
|
||||
}
|
||||
|
||||
// TODO: expected redeclaration error.
|
||||
14
_test/redeclaration-global2.go
Normal file
14
_test/redeclaration-global2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var t time.Time
|
||||
println(t.String())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global2.go:5:2: time/redeclaration-global2.go redeclared in this block
|
||||
15
_test/redeclaration-global3.go
Normal file
15
_test/redeclaration-global3.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var time string
|
||||
|
||||
func main() {
|
||||
time = "hello"
|
||||
println(t)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global3.go:7:5: time redeclared in this block
|
||||
15
_test/redeclaration-global4.go
Normal file
15
_test/redeclaration-global4.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type time string
|
||||
|
||||
func main() {
|
||||
var t time = "hello"
|
||||
println(t)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global4.go:7:6: time redeclared in this block
|
||||
15
_test/redeclaration-global5.go
Normal file
15
_test/redeclaration-global5.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var time int
|
||||
|
||||
func time() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := time()
|
||||
println(t)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global5.go:5:1: time redeclared in this block
|
||||
17
_test/redeclaration-global6.go
Normal file
17
_test/redeclaration-global6.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func time() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := time()
|
||||
println(t)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration-global6.go:7:1: time redeclared in this block
|
||||
18
_test/restricted0.go
Normal file
18
_test/restricted0.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
log.Fatal("log.Fatal does not exit")
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: log.Fatal does not exit
|
||||
18
_test/restricted1.go
Normal file
18
_test/restricted1.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
os.Exit(1)
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: os.Exit(1)
|
||||
14
_test/restricted2.go
Normal file
14
_test/restricted2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
p, err := os.FindProcess(os.Getpid())
|
||||
fmt.Println(p, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil> restricted
|
||||
23
_test/restricted3.go
Normal file
23
_test/restricted3.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
logger = log.New(&buf, "logger: ", log.Lshortfile)
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r, buf.String())
|
||||
}()
|
||||
logger.Fatal("test log")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: test log logger: restricted.go:39: test log
|
||||
12
_test/rune1.go
Normal file
12
_test/rune1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
t := make([]byte, 2)
|
||||
t[0] = '$'
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [36 0]
|
||||
18
_test/selector-scope0.go
Normal file
18
_test/selector-scope0.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func test(time string, t time.Time) string {
|
||||
return time
|
||||
}
|
||||
|
||||
func main() {
|
||||
str := test("test", time.Now())
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
@@ -1,15 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
Child []*S
|
||||
Name string
|
||||
Child []*S
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{Name: "root"}
|
||||
s.Child = append(s.Child, &S{Name: "child"})
|
||||
println(s.Child[0].Name)
|
||||
a := S{Name: "hello"}
|
||||
a.Child = append(a.Child, &S{Name: "world"})
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
a.Child[0].Child = append([]*S{}, &S{Name: "sunshine"})
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// child
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":null}]}
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":null}]}]}
|
||||
|
||||
33
_test/struct49.go
Normal file
33
_test/struct49.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
ts map[string][]*T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s *S
|
||||
}
|
||||
|
||||
func (c *S) getT(addr string) (t *T, ok bool) {
|
||||
cns, ok := c.ts[addr]
|
||||
if !ok || len(cns) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
t = cns[len(cns)-1]
|
||||
c.ts[addr] = cns[:len(cns)-1]
|
||||
return t, true
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{
|
||||
ts: map[string][]*T{},
|
||||
}
|
||||
s.ts["test"] = append(s.ts["test"], &T{s: s})
|
||||
|
||||
t , ok:= s.getT("test")
|
||||
println(t != nil, ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true true
|
||||
20
_test/struct50.go
Normal file
20
_test/struct50.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child []Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello"}
|
||||
a.Child = append([]Node{}, Node{Name: "world"})
|
||||
fmt.Println(a)
|
||||
a.Child[0].Child = append([]Node{}, Node{Name: "sunshine"})
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {hello [{world []}]}
|
||||
// {hello [{world [{sunshine []}]}]}
|
||||
23
_test/struct51.go
Normal file
23
_test/struct51.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child [2]*Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello"}
|
||||
a.Child[0] = &Node{Name: "world"}
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
a.Child[0].Child[0] = &Node{Name: "sunshine"}
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[null,null]},null]}
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":[null,null]},null]},null]}
|
||||
20
_test/struct52.go
Normal file
20
_test/struct52.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child map[string]Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello", Child: map[string]Node{}}
|
||||
a.Child["1"] = Node{Name: "world", Child: map[string]Node{}}
|
||||
fmt.Println(a)
|
||||
a.Child["1"].Child["1"] = Node{Name: "sunshine", Child: map[string]Node{}}
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {hello map[1:{world map[]}]}
|
||||
// {hello map[1:{world map[1:{sunshine map[]}]}]}
|
||||
23
_test/struct53.go
Normal file
23
_test/struct53.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T1 struct {
|
||||
P []*T
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
P2 *T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
*T1
|
||||
S1 *T
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(T2{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
26
_test/struct54.go
Normal file
26
_test/struct54.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
t *T
|
||||
}
|
||||
|
||||
func newS() *S {
|
||||
return &S{
|
||||
t: &T{u: map[string]*U{}},
|
||||
}
|
||||
}
|
||||
|
||||
type T struct {
|
||||
u map[string]*U
|
||||
}
|
||||
|
||||
type U struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := newS()
|
||||
_ = s
|
||||
|
||||
println("ok")
|
||||
}
|
||||
28
_test/switch38.go
Normal file
28
_test/switch38.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
func isSeparator(c byte) bool {
|
||||
switch c {
|
||||
case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := "max-age=20"
|
||||
for _, c := range []byte(s) {
|
||||
println(string(c), isSeparator(c))
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// m false
|
||||
// a false
|
||||
// x false
|
||||
// - false
|
||||
// a false
|
||||
// g false
|
||||
// e false
|
||||
// = true
|
||||
// 2 false
|
||||
// 0 false
|
||||
@@ -1,5 +1,5 @@
|
||||
// The following comment line has the same effect as 'go run -tags=dummy'
|
||||
//yaegi:tags dummy
|
||||
// yaegi:tags dummy
|
||||
|
||||
package main
|
||||
|
||||
|
||||
18
_test/time13.go
Normal file
18
_test/time13.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dummy = 1
|
||||
|
||||
var t time.Time = time.Date(2007, time.November, 10, 23, 4, 5, 0, time.UTC)
|
||||
|
||||
func main() {
|
||||
t = time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
|
||||
fmt.Println(t.Clock())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 23 4 5
|
||||
44
_test/type25.go
Normal file
44
_test/type25.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return &err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
44
_test/type26.go
Normal file
44
_test/type26.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e *wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e *wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(*wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(&wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
15
_test/unsafe0.go
Normal file
15
_test/unsafe0.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func main() {
|
||||
str := "foobar"
|
||||
|
||||
p := unsafe.Pointer(&str)
|
||||
str2 := *(*string)(p)
|
||||
|
||||
println(str2)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foobar
|
||||
20
_test/unsafe1.go
Normal file
20
_test/unsafe1.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type S struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{Name: "foobar"}
|
||||
|
||||
p := unsafe.Pointer(s)
|
||||
|
||||
s2 := (*S)(p)
|
||||
|
||||
println(s2.Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foobar
|
||||
20
_test/unsafe2.go
Normal file
20
_test/unsafe2.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "foobar"
|
||||
|
||||
ptr := unsafe.Pointer(&str)
|
||||
p := uintptr(ptr)
|
||||
|
||||
s1 := fmt.Sprintf("%x", ptr)
|
||||
s2 := fmt.Sprintf("%x", p)
|
||||
println(s1 == s2)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
26
_test/unsafe3.go
Normal file
26
_test/unsafe3.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const SSize = 16
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var sBuf [SSize]byte
|
||||
s := (*S)(unsafe.Pointer(&sBuf[0]))
|
||||
|
||||
s.X = 2
|
||||
s.Y = 4
|
||||
|
||||
fmt.Println(sBuf)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [2 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0]
|
||||
29
_test/unsafe4.go
Normal file
29
_test/unsafe4.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const SSize = 24
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
func main() {
|
||||
arr := []S{
|
||||
{X: 1},
|
||||
{X: 2},
|
||||
{X: 3},
|
||||
}
|
||||
addr := unsafe.Pointer(&arr[0])
|
||||
s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
|
||||
|
||||
fmt.Println(s.X)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
22
_test/unsafe5.go
Normal file
22
_test/unsafe5.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
func main() {
|
||||
size := unsafe.Sizeof(S{})
|
||||
align := unsafe.Alignof(S{})
|
||||
|
||||
fmt.Println(size, align)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 24 8
|
||||
13
_test/var12.go
Normal file
13
_test/var12.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = b
|
||||
b = "hello"
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
23
_test/var13.go
Normal file
23
_test/var13.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = concat("hello", b)
|
||||
b = concat(" ", c, "!")
|
||||
c = d
|
||||
d = "world"
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
10
_test/var14.go
Normal file
10
_test/var14.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/containous/yaegi/_test/vars"
|
||||
|
||||
func main() {
|
||||
println(vars.A)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
17
_test/variadic8.go
Normal file
17
_test/variadic8.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fn1 := func(args ...*time.Duration) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
fmt.Printf("%T\n", fn1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// func(...*time.Duration) string
|
||||
14
_test/variadic9.go
Normal file
14
_test/variadic9.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(Sprintf("Hello %s", "World!"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello World!
|
||||
14
_test/vars/first.go
Normal file
14
_test/vars/first.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
A = concat("hello", B)
|
||||
C = D
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
6
_test/vars/second.go
Normal file
6
_test/vars/second.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
B = concat(" ", C, "!")
|
||||
D = "world"
|
||||
)
|
||||
@@ -23,268 +23,16 @@ import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/yaegi/extract"
|
||||
)
|
||||
|
||||
const model = `// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
{{.License}}
|
||||
|
||||
{{if .BuildTags}}// +build {{.BuildTags}}{{end}}
|
||||
|
||||
package {{.Dest}}
|
||||
|
||||
import (
|
||||
{{- range $key, $value := .Imports }}
|
||||
{{- if $value}}
|
||||
"{{$key}}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
"{{.PkgName}}"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["{{.PkgName}}"] = map[string]reflect.Value{
|
||||
{{- if .Val}}
|
||||
// function, constant and variable definitions
|
||||
{{range $key, $value := .Val -}}
|
||||
{{- if $value.Addr -}}
|
||||
"{{$key}}": reflect.ValueOf(&{{$value.Name}}).Elem(),
|
||||
{{else -}}
|
||||
"{{$key}}": reflect.ValueOf({{$value.Name}}),
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Typ}}
|
||||
// type definitions
|
||||
{{range $key, $value := .Typ -}}
|
||||
"{{$key}}": reflect.ValueOf((*{{$value}})(nil)),
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Wrap}}
|
||||
// interface wrapper definitions
|
||||
{{range $key, $value := .Wrap -}}
|
||||
"_{{$key}}": reflect.ValueOf((*{{$value.Name}})(nil)),
|
||||
{{end}}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{range $key, $value := .Wrap -}}
|
||||
// {{$value.Name}} is an interface wrapper for {{$key}} type
|
||||
type {{$value.Name}} struct {
|
||||
{{range $m := $value.Method -}}
|
||||
W{{$m.Name}} func{{$m.Param}} {{$m.Result}}
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Val store the value name and addressable status of symbols
|
||||
type Val struct {
|
||||
Name string // "package.name"
|
||||
Addr bool // true if symbol is a Var
|
||||
}
|
||||
|
||||
// Method store information for generating interface wrapper method
|
||||
type Method struct {
|
||||
Name, Param, Result, Arg, Ret string
|
||||
}
|
||||
|
||||
// Wrap store information for generating interface wrapper
|
||||
type Wrap struct {
|
||||
Name string
|
||||
Method []Method
|
||||
}
|
||||
|
||||
func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prefix := "_" + pkgName + "_"
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
|
||||
|
||||
typ := map[string]string{}
|
||||
val := map[string]Val{}
|
||||
wrap := map[string]Wrap{}
|
||||
imports := map[string]bool{}
|
||||
sc := p.Scope()
|
||||
|
||||
for _, pkg := range p.Imports() {
|
||||
imports[pkg.Path()] = false
|
||||
}
|
||||
qualify := func(pkg *types.Package) string {
|
||||
if pkg.Path() != pkgName {
|
||||
imports[pkg.Path()] = true
|
||||
}
|
||||
return pkg.Name()
|
||||
}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
o := sc.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
pname := path.Base(pkgName) + "." + name
|
||||
switch o := o.(type) {
|
||||
case *types.Const:
|
||||
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
|
||||
// convert untyped constant to right type to avoid overflow
|
||||
val[name] = Val{fixConst(pname, o.Val(), imports), false}
|
||||
} else {
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
case *types.Func:
|
||||
val[name] = Val{pname, false}
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
case *types.TypeName:
|
||||
typ[name] = pname
|
||||
if t, ok := o.Type().Underlying().(*types.Interface); ok {
|
||||
var methods []Method
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
f := t.Method(i)
|
||||
if !f.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
sign := f.Type().(*types.Signature)
|
||||
args := make([]string, sign.Params().Len())
|
||||
params := make([]string, len(args))
|
||||
for j := range args {
|
||||
v := sign.Params().At(j)
|
||||
if args[j] = v.Name(); args[j] == "" {
|
||||
args[j] = fmt.Sprintf("a%d", j)
|
||||
}
|
||||
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
arg := "(" + strings.Join(args, ", ") + ")"
|
||||
param := "(" + strings.Join(params, ", ") + ")"
|
||||
|
||||
results := make([]string, sign.Results().Len())
|
||||
for j := range results {
|
||||
v := sign.Results().At(j)
|
||||
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
result := "(" + strings.Join(results, ", ") + ")"
|
||||
|
||||
ret := ""
|
||||
if sign.Results().Len() > 0 {
|
||||
ret = "return"
|
||||
}
|
||||
|
||||
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
||||
}
|
||||
wrap[name] = Wrap{prefix + name, methods}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buildTags string
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
minorRaw := getMinor(parts[1])
|
||||
|
||||
currentGoVersion := parts[0] + "." + minorRaw
|
||||
|
||||
minor, errParse := strconv.Atoi(minorRaw)
|
||||
if errParse != nil {
|
||||
return nil, fmt.Errorf("failed to parse version: %v", errParse)
|
||||
}
|
||||
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
buildTags = currentGoVersion + ",!" + nextGoVersion
|
||||
}
|
||||
|
||||
base := template.New("goexports")
|
||||
parse, err := base.Parse(model)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
|
||||
if pkgName == "log/syslog" {
|
||||
buildTags += ",!windows,!nacl,!plan9"
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": dest,
|
||||
"Imports": imports,
|
||||
"PkgName": pkgName,
|
||||
"Val": val,
|
||||
"Typ": typ,
|
||||
"Wrap": wrap,
|
||||
"BuildTags": buildTags,
|
||||
"License": license,
|
||||
}
|
||||
err = parse.Execute(b, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template error: %v", err)
|
||||
}
|
||||
|
||||
// gofmt
|
||||
source, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format source: %v: %s", err, b.Bytes())
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow
|
||||
func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
var (
|
||||
tok string
|
||||
str string
|
||||
)
|
||||
switch val.Kind() {
|
||||
case constant.Int:
|
||||
tok = "INT"
|
||||
str = val.ExactString()
|
||||
case constant.Float:
|
||||
v := constant.Val(val) // v is *big.Rat or *big.Float
|
||||
f, ok := v.(*big.Float)
|
||||
if !ok {
|
||||
f = new(big.Float).SetRat(v.(*big.Rat))
|
||||
}
|
||||
|
||||
tok = "FLOAT"
|
||||
str = f.Text('g', int(f.Prec()))
|
||||
case constant.Complex:
|
||||
// TODO: not sure how to parse this case
|
||||
fallthrough
|
||||
default:
|
||||
return name
|
||||
}
|
||||
|
||||
imports["go/constant"] = true
|
||||
imports["go/token"] = true
|
||||
|
||||
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
|
||||
}
|
||||
|
||||
// genLicense generates the correct LICENSE header text from the provided
|
||||
// path to a LICENSE file.
|
||||
func genLicense(fname string) (string, error) {
|
||||
@@ -314,9 +62,13 @@ func genLicense(fname string) (string, error) {
|
||||
return license.String(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
licenseFlag := flag.String("license", "", "path to a LICENSE file")
|
||||
var (
|
||||
licenseFlag = flag.String("license", "", "path to a LICENSE file")
|
||||
// TODO: deal with a module that has several packages (so there's only one go.mod file at the root of the project).
|
||||
importPathFlag = flag.String("import_path", "", "the namespace for the symbols extracted from the argument. Not needed if the argument is from the stdlib, or if the name can be found in a go.mod")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
@@ -329,50 +81,57 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dest := path.Base(dir)
|
||||
|
||||
for _, pkg := range flag.Args() {
|
||||
content, err := genContent(dest, pkg, license)
|
||||
ext := extract.Extractor{
|
||||
Dest: path.Base(wd),
|
||||
License: license,
|
||||
}
|
||||
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
|
||||
skip := map[string]bool{}
|
||||
if goos == "solaris" {
|
||||
skip["syscall.RawSyscall6"] = true
|
||||
skip["syscall.Syscall6"] = true
|
||||
}
|
||||
ext.Skip = skip
|
||||
|
||||
for _, pkgIdent := range flag.Args() {
|
||||
var buf bytes.Buffer
|
||||
importPath, err := ext.Extract(pkgIdent, *importPathFlag, &buf)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var oFile string
|
||||
if pkg == "syscall" {
|
||||
goos, arch := os.Getenv("GOOS"), os.Getenv("GOARCH")
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + "_" + goos + "_" + arch + ".go"
|
||||
if pkgIdent == "syscall" {
|
||||
oFile = strings.Replace(importPath, "/", "_", -1) + "_" + goos + "_" + goarch + ".go"
|
||||
} else {
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + ".go"
|
||||
oFile = strings.Replace(importPath, "/", "_", -1) + ".go"
|
||||
}
|
||||
|
||||
prefix := runtime.Version()
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
prefix = parts[0] + "_" + getMinor(parts[1])
|
||||
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(prefix+"_"+oFile, content, 0666)
|
||||
f, err := os.Create(prefix + "_" + oFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(f, &buf); err != nil {
|
||||
_ = f.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMinor(part string) string {
|
||||
minor := part
|
||||
index := strings.Index(minor, "beta")
|
||||
if index < 0 {
|
||||
index = strings.Index(minor, "rc")
|
||||
}
|
||||
if index > 0 {
|
||||
minor = minor[:index]
|
||||
}
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
107
cmd/yaegi/extract.go
Normal file
107
cmd/yaegi/extract.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/yaegi/extract"
|
||||
)
|
||||
|
||||
func extractCmd(arg []string) error {
|
||||
var licensePath string
|
||||
var importPath string
|
||||
|
||||
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
|
||||
eflag.StringVar(&importPath, "import_path", "", "the namespace for the extracted symbols")
|
||||
eflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi extract [options] packages...")
|
||||
fmt.Println("Options:")
|
||||
eflag.PrintDefaults()
|
||||
}
|
||||
|
||||
if err := eflag.Parse(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := eflag.Args()
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("missing package")
|
||||
}
|
||||
|
||||
license, err := genLicense(licensePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := extract.Extractor{
|
||||
Dest: path.Base(wd),
|
||||
License: license,
|
||||
}
|
||||
|
||||
for _, pkgIdent := range args {
|
||||
var buf bytes.Buffer
|
||||
importPath, err := ext.Extract(pkgIdent, importPath, &buf)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
oFile := strings.Replace(importPath, "/", "_", -1) + ".go"
|
||||
f, err := os.Create(oFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(f, &buf); err != nil {
|
||||
_ = f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// genLicense generates the correct LICENSE header text from the provided
|
||||
// path to a LICENSE file.
|
||||
func genLicense(fname string) (string, error) {
|
||||
if fname == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not open LICENSE file: %v", err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
license := new(strings.Builder)
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
txt := sc.Text()
|
||||
if txt != "" {
|
||||
txt = " " + txt
|
||||
}
|
||||
license.WriteString("//" + txt + "\n")
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
|
||||
}
|
||||
|
||||
return license.String(), nil
|
||||
}
|
||||
43
cmd/yaegi/help.go
Normal file
43
cmd/yaegi/help.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const usage = `Yaegi is a Go interpreter.
|
||||
|
||||
Usage:
|
||||
|
||||
yaegi [command] [arguments]
|
||||
|
||||
The commands are:
|
||||
|
||||
extract generate a wrapper file from a source package
|
||||
help print usage information
|
||||
run execute a Go program from source
|
||||
test execute test functions in a Go package
|
||||
|
||||
Use "yaegi help <command>" for more information about a command.
|
||||
|
||||
If no command is given or if the first argument is not a command, then
|
||||
the run command is assumed.
|
||||
`
|
||||
|
||||
func help(arg []string) error {
|
||||
var cmd string
|
||||
if len(arg) > 0 {
|
||||
cmd = arg[0]
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case Extract:
|
||||
return extractCmd([]string{"-h"})
|
||||
case Help, "", "-h", "--help":
|
||||
fmt.Print(usage)
|
||||
return nil
|
||||
case Run:
|
||||
return run([]string{"-h"})
|
||||
case Test:
|
||||
return fmt.Errorf("help: test not implemented")
|
||||
default:
|
||||
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
|
||||
}
|
||||
}
|
||||
132
cmd/yaegi/run.go
Normal file
132
cmd/yaegi/run.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/syscall"
|
||||
"github.com/containous/yaegi/stdlib/unrestricted"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
func run(arg []string) error {
|
||||
var interactive bool
|
||||
var useSyscall bool
|
||||
var useUnrestricted bool
|
||||
var useUnsafe bool
|
||||
var tags string
|
||||
var cmd string
|
||||
var err error
|
||||
|
||||
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
||||
rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
|
||||
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
||||
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
rflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi run [options] [path] [args]")
|
||||
fmt.Println("Options:")
|
||||
rflag.PrintDefaults()
|
||||
}
|
||||
if err = rflag.Parse(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
args := rflag.Args()
|
||||
|
||||
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 useUnsafe {
|
||||
i.Use(unsafe.Symbols)
|
||||
}
|
||||
if useUnrestricted {
|
||||
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
|
||||
i.Use(unrestricted.Symbols)
|
||||
}
|
||||
|
||||
if cmd != "" {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == "" {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
path := args[0]
|
||||
os.Args = arg[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
|
||||
|
||||
if isPackageName(path) {
|
||||
err = runPackage(i, path)
|
||||
} else {
|
||||
if isDir(path) {
|
||||
err = runDir(i, path)
|
||||
} else {
|
||||
err = runFile(i, path)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if interactive {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPackageName(path string) bool {
|
||||
return !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "./") && !strings.HasPrefix(path, "../")
|
||||
}
|
||||
|
||||
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 {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
i.REPL(strings.NewReader(s), os.Stdout)
|
||||
} else {
|
||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||
i.Name = path
|
||||
_, err := i.Eval(s)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -63,9 +63,13 @@ Options:
|
||||
evaluate the string and return.
|
||||
-i
|
||||
start an interactive REPL after file execution.
|
||||
-syscall
|
||||
include syscall symbols.
|
||||
-tags tag,list
|
||||
a comma-separated list of build tags to consider satisfied during
|
||||
the interpretation.
|
||||
-unsafe
|
||||
include unsafe symbols.
|
||||
|
||||
Debugging support (may be removed at any time):
|
||||
YAEGI_AST_DOT=1
|
||||
@@ -81,75 +85,53 @@ Debugging support (may be removed at any time):
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
const (
|
||||
Extract = "extract"
|
||||
Help = "help"
|
||||
Run = "run"
|
||||
Test = "test"
|
||||
)
|
||||
|
||||
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:")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
log.SetFlags(log.Lshortfile)
|
||||
var err error
|
||||
var exitCode int
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
log.SetFlags(log.Lshortfile) // Ease debugging.
|
||||
|
||||
if cmd != `` {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
if len(os.Args) > 1 {
|
||||
cmd = os.Args[1]
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == `` {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
return
|
||||
switch cmd {
|
||||
case Extract:
|
||||
err = extractCmd(os.Args[2:])
|
||||
case Help, "-h", "--help":
|
||||
err = help(os.Args[2:])
|
||||
case Run:
|
||||
err = run(os.Args[2:])
|
||||
case Test:
|
||||
err = fmt.Errorf("test not implemented")
|
||||
default:
|
||||
// If no command is given, fallback to default "run" command.
|
||||
// This allows scripts starting with "#!/usr/bin/env yaegi",
|
||||
// as passing more than 1 argument to #! executable may be not supported
|
||||
// on all platforms.
|
||||
cmd = Run
|
||||
err = run(os.Args[1:])
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
|
||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
i.REPL(strings.NewReader(s), os.Stdout)
|
||||
} else {
|
||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||
i.Name = args[0]
|
||||
_, err := i.Eval(s)
|
||||
if 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)
|
||||
if err != nil && !errors.Is(err, flag.ErrHelp) {
|
||||
err = fmt.Errorf("%s: %w", cmd, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
exitCode = 1
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI.
|
||||
CITimeoutMultiplier = 3
|
||||
)
|
||||
|
||||
|
||||
17
example/pkg/_pkg10/src/github.com/foo/pkg.go
Normal file
17
example/pkg/_pkg10/src/github.com/foo/pkg.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/containous/fromage"
|
||||
)
|
||||
|
||||
func Here() string {
|
||||
return "root"
|
||||
}
|
||||
|
||||
func NewSample() func() string {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%s %s", Here(), fromage.Hello())
|
||||
}
|
||||
}
|
||||
7
example/pkg/_pkg10/src/github.com/foo/vendor/guthib.com/containous/fromage/fromage.go
generated
vendored
Normal file
7
example/pkg/_pkg10/src/github.com/foo/vendor/guthib.com/containous/fromage/fromage.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package fromage
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Hello() string {
|
||||
return fmt.Sprint("Fromage")
|
||||
}
|
||||
11
example/pkg/_pkg11/src/foo/foo.go
Normal file
11
example/pkg/_pkg11/src/foo/foo.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/containous/fromage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Print(fromage.Hello())
|
||||
}
|
||||
7
example/pkg/_pkg11/src/foo/vendor/guthib.com/containous/fromage/fromage.go
generated
vendored
Normal file
7
example/pkg/_pkg11/src/foo/vendor/guthib.com/containous/fromage/fromage.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package fromage
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Hello() string {
|
||||
return fmt.Sprint("Fromage")
|
||||
}
|
||||
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/foo/pkg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%s", pkg.NewSample()())
|
||||
}
|
||||
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/bar"
|
||||
)
|
||||
|
||||
func Here() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func NewSample() func() string {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%s %s", bar.Bar(), Here())
|
||||
}
|
||||
}
|
||||
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package bar
|
||||
|
||||
// Bar is bar
|
||||
func Bar() string {
|
||||
return "Yo"
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -10,9 +15,11 @@ import (
|
||||
|
||||
func TestPackages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
goPath string
|
||||
expected string
|
||||
desc string
|
||||
goPath string
|
||||
expected string
|
||||
topImport string
|
||||
evalFile string
|
||||
}{
|
||||
{
|
||||
desc: "vendor",
|
||||
@@ -64,6 +71,30 @@ func TestPackages(t *testing.T) {
|
||||
goPath: "./_pkg8/",
|
||||
expected: "root Fromage!",
|
||||
},
|
||||
{
|
||||
desc: "at the project root",
|
||||
goPath: "./_pkg10/",
|
||||
expected: "root Fromage",
|
||||
topImport: "github.com/foo",
|
||||
},
|
||||
{
|
||||
desc: "eval main that has vendored dep",
|
||||
goPath: "./_pkg11/",
|
||||
expected: "Fromage",
|
||||
evalFile: "./_pkg11/src/foo/foo.go",
|
||||
},
|
||||
{
|
||||
desc: "vendor dir is a sibling or an uncle",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
topImport: "guthib.com/foo/pkg",
|
||||
},
|
||||
{
|
||||
desc: "eval main with vendor as a sibling",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -78,27 +109,73 @@ func TestPackages(t *testing.T) {
|
||||
i := interp.New(interp.Options{GoPath: goPath})
|
||||
i.Use(stdlib.Symbols) // Use binary standard library
|
||||
|
||||
// Load pkg from sources
|
||||
if _, err = i.Eval(`import "github.com/foo/pkg"`); err != nil {
|
||||
t.Fatal(err)
|
||||
var msg string
|
||||
if test.evalFile != "" {
|
||||
// setting i.Name as this is how it's actually done in cmd/yaegi
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
// Load pkg from sources
|
||||
topImport := "github.com/foo/pkg"
|
||||
if test.topImport != "" {
|
||||
topImport = test.topImport
|
||||
}
|
||||
if _, err = i.Eval(fmt.Sprintf(`import "%s"`, topImport)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
value, err := i.Eval(`pkg.NewSample()`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fn := value.Interface().(func() string)
|
||||
|
||||
msg = fn()
|
||||
}
|
||||
|
||||
value, err := i.Eval(`pkg.NewSample()`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fn := value.Interface().(func() string)
|
||||
|
||||
msg := fn()
|
||||
|
||||
if msg != test.expected {
|
||||
t.Errorf("Got %q, want %q", msg, test.expected)
|
||||
fatalStderrf(t, "Got %q, want %q", msg, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
func TestPackagesError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
409
extract/extract.go
Normal file
409
extract/extract.go
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
Package extract generates wrappers of package exported symbols.
|
||||
*/
|
||||
package extract
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const model = `// Code generated by 'github.com/containous/yaegi/extract {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
{{.License}}
|
||||
|
||||
{{if .BuildTags}}// +build {{.BuildTags}}{{end}}
|
||||
|
||||
package {{.Dest}}
|
||||
|
||||
import (
|
||||
{{- range $key, $value := .Imports }}
|
||||
{{- if $value}}
|
||||
"{{$key}}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
"{{.PkgName}}"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["{{.PkgName}}"] = map[string]reflect.Value{
|
||||
{{- if .Val}}
|
||||
// function, constant and variable definitions
|
||||
{{range $key, $value := .Val -}}
|
||||
{{- if $value.Addr -}}
|
||||
"{{$key}}": reflect.ValueOf(&{{$value.Name}}).Elem(),
|
||||
{{else -}}
|
||||
"{{$key}}": reflect.ValueOf({{$value.Name}}),
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Typ}}
|
||||
// type definitions
|
||||
{{range $key, $value := .Typ -}}
|
||||
"{{$key}}": reflect.ValueOf((*{{$value}})(nil)),
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Wrap}}
|
||||
// interface wrapper definitions
|
||||
{{range $key, $value := .Wrap -}}
|
||||
"_{{$key}}": reflect.ValueOf((*{{$value.Name}})(nil)),
|
||||
{{end}}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{range $key, $value := .Wrap -}}
|
||||
// {{$value.Name}} is an interface wrapper for {{$key}} type
|
||||
type {{$value.Name}} struct {
|
||||
{{range $m := $value.Method -}}
|
||||
W{{$m.Name}} func{{$m.Param}} {{$m.Result}}
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Val stores the value name and addressable status of symbols.
|
||||
type Val struct {
|
||||
Name string // "package.name"
|
||||
Addr bool // true if symbol is a Var
|
||||
}
|
||||
|
||||
// Method stores information for generating interface wrapper method.
|
||||
type Method struct {
|
||||
Name, Param, Result, Arg, Ret string
|
||||
}
|
||||
|
||||
// Wrap stores information for generating interface wrapper.
|
||||
type Wrap struct {
|
||||
Name string
|
||||
Method []Method
|
||||
}
|
||||
|
||||
// restricted map defines symbols for which a special implementation is provided.
|
||||
var restricted = map[string]bool{
|
||||
"osExit": true,
|
||||
"osFindProcess": true,
|
||||
"logFatal": true,
|
||||
"logFatalf": true,
|
||||
"logFatalln": true,
|
||||
"logLogger": true,
|
||||
"logNew": true,
|
||||
}
|
||||
|
||||
func genContent(dest, importPath, license string, p *types.Package, skip map[string]bool) ([]byte, error) {
|
||||
prefix := "_" + importPath + "_"
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
|
||||
|
||||
typ := map[string]string{}
|
||||
val := map[string]Val{}
|
||||
wrap := map[string]Wrap{}
|
||||
imports := map[string]bool{}
|
||||
sc := p.Scope()
|
||||
|
||||
for _, pkg := range p.Imports() {
|
||||
imports[pkg.Path()] = false
|
||||
}
|
||||
qualify := func(pkg *types.Package) string {
|
||||
if pkg.Path() != importPath {
|
||||
imports[pkg.Path()] = true
|
||||
}
|
||||
return pkg.Name()
|
||||
}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
o := sc.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
pname := path.Base(importPath) + "." + name
|
||||
if skip[pname] {
|
||||
continue
|
||||
}
|
||||
|
||||
if rname := path.Base(importPath) + name; restricted[rname] {
|
||||
// Restricted symbol, locally provided by stdlib wrapper.
|
||||
pname = rname
|
||||
}
|
||||
|
||||
switch o := o.(type) {
|
||||
case *types.Const:
|
||||
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
|
||||
// convert untyped constant to right type to avoid overflow
|
||||
val[name] = Val{fixConst(pname, o.Val(), imports), false}
|
||||
} else {
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
case *types.Func:
|
||||
val[name] = Val{pname, false}
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
case *types.TypeName:
|
||||
typ[name] = pname
|
||||
if t, ok := o.Type().Underlying().(*types.Interface); ok {
|
||||
var methods []Method
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
f := t.Method(i)
|
||||
if !f.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
sign := f.Type().(*types.Signature)
|
||||
args := make([]string, sign.Params().Len())
|
||||
params := make([]string, len(args))
|
||||
for j := range args {
|
||||
v := sign.Params().At(j)
|
||||
if args[j] = v.Name(); args[j] == "" {
|
||||
args[j] = fmt.Sprintf("a%d", j)
|
||||
}
|
||||
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
arg := "(" + strings.Join(args, ", ") + ")"
|
||||
param := "(" + strings.Join(params, ", ") + ")"
|
||||
|
||||
results := make([]string, sign.Results().Len())
|
||||
for j := range results {
|
||||
v := sign.Results().At(j)
|
||||
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
result := "(" + strings.Join(results, ", ") + ")"
|
||||
|
||||
ret := ""
|
||||
if sign.Results().Len() > 0 {
|
||||
ret = "return"
|
||||
}
|
||||
|
||||
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
||||
}
|
||||
wrap[name] = Wrap{prefix + name, methods}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTags, err := buildTags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := template.New("goexports")
|
||||
parse, err := base.Parse(model)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
|
||||
if importPath == "log/syslog" {
|
||||
buildTags += ",!windows,!nacl,!plan9"
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": dest,
|
||||
"Imports": imports,
|
||||
"PkgName": importPath,
|
||||
"Val": val,
|
||||
"Typ": typ,
|
||||
"Wrap": wrap,
|
||||
"BuildTags": buildTags,
|
||||
"License": license,
|
||||
}
|
||||
err = parse.Execute(b, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template error: %v", err)
|
||||
}
|
||||
|
||||
// gofmt
|
||||
source, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format source: %v: %s", err, b.Bytes())
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow.
|
||||
func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
var (
|
||||
tok string
|
||||
str string
|
||||
)
|
||||
switch val.Kind() {
|
||||
case constant.Int:
|
||||
tok = "INT"
|
||||
str = val.ExactString()
|
||||
case constant.Float:
|
||||
v := constant.Val(val) // v is *big.Rat or *big.Float
|
||||
f, ok := v.(*big.Float)
|
||||
if !ok {
|
||||
f = new(big.Float).SetRat(v.(*big.Rat))
|
||||
}
|
||||
|
||||
tok = "FLOAT"
|
||||
str = f.Text('g', int(f.Prec()))
|
||||
case constant.Complex:
|
||||
// TODO: not sure how to parse this case
|
||||
fallthrough
|
||||
default:
|
||||
return name
|
||||
}
|
||||
|
||||
imports["go/constant"] = true
|
||||
imports["go/token"] = true
|
||||
|
||||
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
|
||||
}
|
||||
|
||||
// importPath checks whether pkgIdent is an existing directory relative to
|
||||
// e.WorkingDir. If yes, it returns the actual import path of the Go package
|
||||
// located in the directory. If it is definitely a relative path, but it does not
|
||||
// exist, an error is returned. Otherwise, it is assumed to be an import path, and
|
||||
// pkgIdent is returned.
|
||||
func (e Extractor) importPath(pkgIdent, importPath string) (string, error) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dirPath := filepath.Join(wd, pkgIdent)
|
||||
_, err = os.Stat(dirPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err != nil {
|
||||
if len(pkgIdent) > 0 && pkgIdent[0] == '.' {
|
||||
// pkgIdent is definitely a relative path, not a package name, and it does not exist
|
||||
return "", err
|
||||
}
|
||||
// pkgIdent might be a valid stdlib package name. So we leave that responsibility to the caller now.
|
||||
return pkgIdent, nil
|
||||
}
|
||||
|
||||
// local import
|
||||
if importPath != "" {
|
||||
return importPath, nil
|
||||
}
|
||||
|
||||
modPath := filepath.Join(dirPath, "go.mod")
|
||||
_, err = os.Stat(modPath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", errors.New("no go.mod found, and no import path specified")
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f, err := os.Open(modPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
sc := bufio.NewScanner(f)
|
||||
var l string
|
||||
for sc.Scan() {
|
||||
l = sc.Text()
|
||||
break
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := strings.Fields(l)
|
||||
if len(parts) < 2 {
|
||||
return "", errors.New(`invalid first line syntax in go.mod`)
|
||||
}
|
||||
if parts[0] != "module" {
|
||||
return "", errors.New(`invalid first line in go.mod, no "module" found`)
|
||||
}
|
||||
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
// Extractor creates a package with all the symbols from a dependency package.
|
||||
type Extractor struct {
|
||||
Dest string // the name of the created package.
|
||||
License string // license text to be included in the created package, optional.
|
||||
Skip map[string]bool
|
||||
}
|
||||
|
||||
// Extract writes to rw a Go package with all the symbols found at pkgIdent.
|
||||
// pkgIdent can be an import path, or a local path, relative to e.WorkingDir. In
|
||||
// the latter case, Extract returns the actual import path of the package found at
|
||||
// pkgIdent, otherwise it just returns pkgIdent.
|
||||
// If pkgIdent is an import path, it is looked up in GOPATH. Vendoring is not
|
||||
// supported yet, and the behavior is only defined for GO111MODULE=off.
|
||||
func (e Extractor) Extract(pkgIdent, importPath string, rw io.Writer) (string, error) {
|
||||
ipp, err := e.importPath(pkgIdent, importPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pkg, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgIdent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
content, err := genContent(e.Dest, ipp, e.License, pkg, e.Skip)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := rw.Write(content); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ipp, nil
|
||||
}
|
||||
|
||||
// GetMinor returns the minor part of the version number.
|
||||
func GetMinor(part string) string {
|
||||
minor := part
|
||||
index := strings.Index(minor, "beta")
|
||||
if index < 0 {
|
||||
index = strings.Index(minor, "rc")
|
||||
}
|
||||
if index > 0 {
|
||||
minor = minor[:index]
|
||||
}
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
func buildTags() (string, error) {
|
||||
version := runtime.Version()
|
||||
if version == "devel" {
|
||||
return "", nil
|
||||
}
|
||||
parts := strings.Split(version, ".")
|
||||
|
||||
minorRaw := GetMinor(parts[1])
|
||||
|
||||
currentGoVersion := parts[0] + "." + minorRaw
|
||||
|
||||
minor, err := strconv.Atoi(minorRaw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse version: %v", err)
|
||||
}
|
||||
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
return currentGoVersion + ",!" + nextGoVersion, nil
|
||||
}
|
||||
140
extract/extract_test.go
Normal file
140
extract/extract_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package extract
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var expectedOutput = `// Code generated by 'github.com/containous/yaegi/extract guthib.com/baz'. DO NOT EDIT.
|
||||
|
||||
// +build BUILD_TAGS
|
||||
|
||||
package bar
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["guthib.com/baz"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Hello": reflect.ValueOf(baz.Hello),
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func init() {
|
||||
buildTags, err := buildTags()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expectedOutput = strings.Replace(expectedOutput, "BUILD_TAGS", buildTags, 1)
|
||||
}
|
||||
|
||||
func TestPackages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
moduleOn string
|
||||
wd string
|
||||
arg string
|
||||
importPath string
|
||||
expected string
|
||||
contains string
|
||||
dest string
|
||||
}{
|
||||
{
|
||||
desc: "stdlib math pkg, using go/importer",
|
||||
dest: "math",
|
||||
arg: "math",
|
||||
// We check this one because it shows both defects when we break it: the value
|
||||
// gets corrupted, and the type becomes token.INT
|
||||
// TODO(mpl): if the ident between key and value becomes annoying, be smarter about it.
|
||||
contains: `"MaxFloat64": reflect.ValueOf(constant.MakeFromLiteral("179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", token.FLOAT, 0)),`,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, using go.mod",
|
||||
wd: "./testdata/1/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, manual import path",
|
||||
wd: "./testdata/2/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, go.mod is ignored, because manual path",
|
||||
wd: "./testdata/3/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, dep in vendor, using go.mod",
|
||||
wd: "./testdata/4/src/guthib.com/bar",
|
||||
arg: "./vendor/guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, dep in vendor, manual import path",
|
||||
wd: "./testdata/5/src/guthib.com/bar",
|
||||
arg: "./vendor/guthib.com/baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wd := test.wd
|
||||
if wd == "" {
|
||||
wd = cwd
|
||||
} else {
|
||||
if err := os.Chdir(wd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Chdir(cwd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
dest := path.Base(wd)
|
||||
if test.dest != "" {
|
||||
dest = test.dest
|
||||
}
|
||||
ext := Extractor{
|
||||
Dest: dest,
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
if _, err := ext.Extract(test.arg, test.importPath, &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test.expected != "" {
|
||||
if out.String() != test.expected {
|
||||
t.Fatalf("\nGot:\n%q\nWant: \n%q", out.String(), test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
if test.contains != "" {
|
||||
if !strings.Contains(out.String(), test.contains) {
|
||||
t.Fatalf("Missing expected part: %s in %s", test.contains, out.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
9
extract/testdata/1/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/1/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/1/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/1/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
3
extract/testdata/1/src/guthib.com/baz/go.mod
vendored
Normal file
3
extract/testdata/1/src/guthib.com/baz/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module guthib.com/baz
|
||||
|
||||
go 1.14
|
||||
9
extract/testdata/2/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/2/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/2/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/2/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
9
extract/testdata/3/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/3/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/3/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/3/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
3
extract/testdata/3/src/guthib.com/baz/go.mod
vendored
Normal file
3
extract/testdata/3/src/guthib.com/baz/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module should.not/be/used
|
||||
|
||||
go 1.14
|
||||
9
extract/testdata/4/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/4/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
5
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user