Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
6486909921 | ||
|
|
d252821df3 | ||
|
|
2bef03e253 | ||
|
|
1fe91be882 | ||
|
|
5cbbf9339c |
@@ -38,6 +38,9 @@
|
||||
"gocognit",
|
||||
"stylecheck",
|
||||
"gomnd",
|
||||
"testpackage",
|
||||
"goerr113",
|
||||
"nestif",
|
||||
]
|
||||
|
||||
[issues]
|
||||
|
||||
@@ -26,7 +26,6 @@ matrix:
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
- CI=1
|
||||
|
||||
go_import_path: github.com/containous/yaegi
|
||||
|
||||
|
||||
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}
|
||||
16
_test/const13.go
Normal file
16
_test/const13.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const tooBig = 1267650600228229401496703205376
|
||||
const huge = 1 << 100
|
||||
const large = huge >> 38
|
||||
|
||||
fmt.Println(large)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4611686018427387904
|
||||
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:
|
||||
// {}
|
||||
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]
|
||||
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!
|
||||
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
|
||||
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
|
||||
16
_test/range8.go
Normal file
16
_test/range8.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _ = range time.Tick(time.Millisecond) {
|
||||
fmt.Println("success")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// success
|
||||
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
|
||||
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
|
||||
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
|
||||
38
_test/type23.go
Normal file
38
_test/type23.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var v1 interface{} = 1
|
||||
var v2 interface{}
|
||||
var v3 http.ResponseWriter = httptest.NewRecorder()
|
||||
|
||||
if r1, ok := v1.(string); ok {
|
||||
_ = r1
|
||||
println("unexpected")
|
||||
}
|
||||
if _, ok := v1.(string); ok {
|
||||
println("unexpected")
|
||||
}
|
||||
if r2, ok := v2.(string); ok {
|
||||
_ = r2
|
||||
println("unexpected")
|
||||
}
|
||||
if _, ok := v2.(string); ok {
|
||||
println("unexpected")
|
||||
}
|
||||
if r3, ok := v3.(http.Pusher); ok {
|
||||
_ = r3
|
||||
println("unexpected")
|
||||
}
|
||||
if _, ok := v3.(http.Pusher); ok {
|
||||
println("unexpected")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
48
_test/type24.go
Normal file
48
_test/type24.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
assertInt()
|
||||
assertNil()
|
||||
assertValue()
|
||||
}
|
||||
|
||||
func assertInt() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println(r)
|
||||
}()
|
||||
|
||||
var v interface{} = 1
|
||||
println(v.(string))
|
||||
}
|
||||
|
||||
func assertNil() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println(r)
|
||||
}()
|
||||
|
||||
var v interface{}
|
||||
println(v.(string))
|
||||
}
|
||||
|
||||
func assertValue() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println(r)
|
||||
}()
|
||||
|
||||
var v http.ResponseWriter = httptest.NewRecorder()
|
||||
println(v.(http.Pusher))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// interface conversion: interface {} is int, not string
|
||||
// interface conversion: interface {} is nil, not string
|
||||
// interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push
|
||||
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"
|
||||
)
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
@@ -97,24 +98,35 @@ func init() {
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Val store the value name and addressable status of symbols
|
||||
// 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
|
||||
// Method store information for generating interface wrapper method.
|
||||
type Method struct {
|
||||
Name, Param, Result, Arg, Ret string
|
||||
}
|
||||
|
||||
// Wrap store information for generating interface wrapper
|
||||
// Wrap store information for generating interface wrapper.
|
||||
type Wrap struct {
|
||||
Name string
|
||||
Method []Method
|
||||
}
|
||||
|
||||
func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
// 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, pkgName, license string, skip map[string]bool) ([]byte, error) {
|
||||
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -146,11 +158,19 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
}
|
||||
|
||||
pname := path.Base(pkgName) + "." + name
|
||||
if skip[pname] {
|
||||
continue
|
||||
}
|
||||
if rname := path.Base(pkgName) + 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()), false}
|
||||
val[name] = Val{fixConst(pname, o.Val(), imports), false}
|
||||
} else {
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
@@ -252,36 +272,36 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow
|
||||
func fixConst(name string, val constant.Value) string {
|
||||
// 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.Float:
|
||||
str := val.ExactString()
|
||||
if _, err := strconv.ParseFloat(str, 32); err == nil {
|
||||
return "float32(" + name + ")"
|
||||
}
|
||||
return name
|
||||
case constant.Int:
|
||||
str := val.ExactString()
|
||||
i, err := strconv.ParseInt(str, 0, 64)
|
||||
if err == nil {
|
||||
switch {
|
||||
case i == int64(int32(i)):
|
||||
return name
|
||||
case i == int64(uint32(i)):
|
||||
return "uint32(" + name + ")"
|
||||
default:
|
||||
return "int64(" + name + ")"
|
||||
}
|
||||
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))
|
||||
}
|
||||
_, err = strconv.ParseUint(str, 0, 64)
|
||||
if err == nil {
|
||||
return "uint64(" + name + ")"
|
||||
}
|
||||
return name
|
||||
|
||||
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
|
||||
@@ -335,20 +355,25 @@ func main() {
|
||||
dest := path.Base(dir)
|
||||
|
||||
for _, pkg := range flag.Args() {
|
||||
content, err := genContent(dest, pkg, license)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var oFile string
|
||||
skip := map[string]bool{}
|
||||
if pkg == "syscall" {
|
||||
goos, arch := os.Getenv("GOOS"), os.Getenv("GOARCH")
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + "_" + goos + "_" + arch + ".go"
|
||||
if goos == "solaris" {
|
||||
skip["syscall.RawSyscall6"] = true
|
||||
skip["syscall.Syscall6"] = true
|
||||
}
|
||||
} else {
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + ".go"
|
||||
}
|
||||
|
||||
content, err := genContent(dest, pkg, license, skip)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := runtime.Version()
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
@@ -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
|
||||
@@ -91,14 +95,23 @@ import (
|
||||
|
||||
"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 main() {
|
||||
var interactive bool
|
||||
var useSyscall bool
|
||||
var useUnrestricted bool
|
||||
var useUnsafe bool
|
||||
var tags string
|
||||
var cmd string
|
||||
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
flag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
||||
flag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
|
||||
flag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
flag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
||||
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]")
|
||||
@@ -112,6 +125,16 @@ func main() {
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
@@ -12,7 +13,11 @@ const model = `package interp
|
||||
|
||||
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Arithmetic operators
|
||||
{{range $name, $op := .Arithmetic}}
|
||||
@@ -176,9 +181,22 @@ func {{$name}}(n *node) {
|
||||
|
||||
func {{$name}}Const(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
{{- if $op.Shift}}
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.{{tokenFromName $name}}, uint(s))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
{{- else}}
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
{{- end}}
|
||||
{{- if $op.Str}}
|
||||
case isString(t):
|
||||
n.rval.SetString(v0.String() {{$op.Name}} v1.String())
|
||||
@@ -354,23 +372,35 @@ func {{$name}}(n *node) {
|
||||
{{end}}
|
||||
{{range $name, $op := .Unary}}
|
||||
func {{$name}}Const(n *node) {
|
||||
v0 := n.child[0].rval
|
||||
isConst := v0.IsValid() && isConstantValue(v0.Type())
|
||||
t := n.typ.rtype
|
||||
v := n.child[0].rval
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
|
||||
{{- if $op.Bool}}
|
||||
n.rval.SetBool({{$op.Name}} v.Bool())
|
||||
if isConst {
|
||||
v := constant.UnaryOp(token.{{tokenFromName $name}}, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
} else {
|
||||
n.rval.SetBool({{$op.Name}} v0.Bool())
|
||||
}
|
||||
{{- else}}
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n.rval.SetInt({{$op.Name}} v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n.rval.SetUint({{$op.Name}} v.Uint())
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.UnaryOp(token.{{tokenFromName $name}}, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isInt(t):
|
||||
n.rval.SetInt({{$op.Name}} v0.Int())
|
||||
case isUint(t):
|
||||
n.rval.SetUint({{$op.Name}} v0.Uint())
|
||||
{{- if $op.Float}}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
n.rval.SetFloat({{$op.Name}} v.Float())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
n.rval.SetComplex({{$op.Name}} v.Complex())
|
||||
case isFloat(t):
|
||||
n.rval.SetFloat({{$op.Name}} v0.Float())
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex({{$op.Name}} v0.Complex())
|
||||
{{- end}}
|
||||
}
|
||||
{{- end}}
|
||||
@@ -382,6 +412,83 @@ func {{$name}}(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -810,7 +917,7 @@ func {{$name}}(n *node) {
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Op define operator name and properties
|
||||
// Op define operator name and properties.
|
||||
type Op struct {
|
||||
Name string // +, -, ...
|
||||
Str bool // true if operator applies to string
|
||||
@@ -822,6 +929,22 @@ type Op struct {
|
||||
|
||||
func main() {
|
||||
base := template.New("goexports")
|
||||
base.Funcs(template.FuncMap{
|
||||
"tokenFromName": func(name string) string {
|
||||
switch name {
|
||||
case "andNot":
|
||||
return "AND_NOT"
|
||||
case "neg":
|
||||
return "SUB"
|
||||
case "pos":
|
||||
return "ADD"
|
||||
case "bitNot":
|
||||
return "XOR"
|
||||
default:
|
||||
return strings.ToUpper(name)
|
||||
}
|
||||
},
|
||||
})
|
||||
parse, err := base.Parse(model)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
@@ -11,10 +12,10 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// nkind defines the kind of AST, i.e. the grammar category
|
||||
// nkind defines the kind of AST, i.e. the grammar category.
|
||||
type nkind uint
|
||||
|
||||
// Node kinds for the go language
|
||||
// Node kinds for the go language.
|
||||
const (
|
||||
undefNode nkind = iota
|
||||
addressExpr
|
||||
@@ -30,6 +31,8 @@ const (
|
||||
caseBody
|
||||
caseClause
|
||||
chanType
|
||||
chanTypeSend
|
||||
chanTypeRecv
|
||||
commClause
|
||||
commClauseDefault
|
||||
compositeLitExpr
|
||||
@@ -108,6 +111,8 @@ var kinds = [...]string{
|
||||
caseBody: "caseBody",
|
||||
caseClause: "caseClause",
|
||||
chanType: "chanType",
|
||||
chanTypeSend: "chanTypeSend",
|
||||
chanTypeRecv: "chanTypeRecv",
|
||||
commClause: "commClause",
|
||||
commClauseDefault: "commClauseDefault",
|
||||
compositeLitExpr: "compositeLitExpr",
|
||||
@@ -178,13 +183,13 @@ func (k nkind) String() string {
|
||||
return "nKind(" + strconv.Itoa(int(k)) + ")"
|
||||
}
|
||||
|
||||
// astError represents an error during AST build stage
|
||||
// astError represents an error during AST build stage.
|
||||
type astError error
|
||||
|
||||
// action defines the node action to perform at execution
|
||||
// action defines the node action to perform at execution.
|
||||
type action uint
|
||||
|
||||
// Node actions for the go language
|
||||
// Node actions for the go language.
|
||||
const (
|
||||
aNop action = iota
|
||||
aAddr
|
||||
@@ -245,6 +250,7 @@ const (
|
||||
aTypeAssert
|
||||
aXor
|
||||
aXorAssign
|
||||
aMax
|
||||
)
|
||||
|
||||
var actions = [...]string{
|
||||
@@ -474,14 +480,14 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
v, _, _, _ := strconv.UnquoteChar(a.Value[1:len(a.Value)-1], '\'')
|
||||
n.rval = reflect.ValueOf(v)
|
||||
case token.FLOAT:
|
||||
v, _ := strconv.ParseFloat(a.Value, 64)
|
||||
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
||||
n.rval = reflect.ValueOf(v)
|
||||
case token.IMAG:
|
||||
v, _ := strconv.ParseFloat(a.Value[:len(a.Value)-1], 64)
|
||||
n.rval = reflect.ValueOf(complex(0, v))
|
||||
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
|
||||
n.rval = reflect.ValueOf(v)
|
||||
case token.INT:
|
||||
v, _ := strconv.ParseInt(a.Value, 0, 0)
|
||||
n.rval = reflect.ValueOf(int(v))
|
||||
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)
|
||||
@@ -564,7 +570,14 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
st.push(addChild(&root, anc, pos, caseClause, aCase), nod)
|
||||
|
||||
case *ast.ChanType:
|
||||
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
|
||||
switch a.Dir {
|
||||
case ast.SEND | ast.RECV:
|
||||
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
|
||||
case ast.SEND:
|
||||
st.push(addChild(&root, anc, pos, chanTypeSend, aNop), nod)
|
||||
case ast.RECV:
|
||||
st.push(addChild(&root, anc, pos, chanTypeRecv, aNop), nod)
|
||||
}
|
||||
|
||||
case *ast.CommClause:
|
||||
kind := commClause
|
||||
@@ -869,7 +882,7 @@ func (s *nodestack) top() astNode {
|
||||
return astNode{}
|
||||
}
|
||||
|
||||
// dup returns a duplicated node subtree
|
||||
// dup returns a duplicated node subtree.
|
||||
func (interp *Interpreter) dup(nod, anc *node) *node {
|
||||
nindex := atomic.AddInt64(&interp.nindex, 1)
|
||||
n := *nod
|
||||
|
||||
@@ -31,7 +31,7 @@ func (interp *Interpreter) buildOk(ctx *build.Context, name, src string) (bool,
|
||||
}
|
||||
|
||||
// buildLineOk returns true if line is not a build constraint or
|
||||
// if build constraint is satisfied
|
||||
// if build constraint is satisfied.
|
||||
func buildLineOk(ctx *build.Context, line string) (ok bool) {
|
||||
if len(line) < 7 || line[:7] != "+build " {
|
||||
return true
|
||||
@@ -46,7 +46,7 @@ func buildLineOk(ctx *build.Context, line string) (ok bool) {
|
||||
return ok
|
||||
}
|
||||
|
||||
// buildOptionOk return true if all comma separated tags match, false otherwise
|
||||
// buildOptionOk return true if all comma separated tags match, false otherwise.
|
||||
func buildOptionOk(ctx *build.Context, tag string) bool {
|
||||
// in option, evaluate the AND of individual tags
|
||||
for _, t := range strings.Split(tag, ",") {
|
||||
@@ -58,7 +58,7 @@ func buildOptionOk(ctx *build.Context, tag string) bool {
|
||||
}
|
||||
|
||||
// buildTagOk returns true if a build tag matches, false otherwise
|
||||
// if first character is !, result is negated
|
||||
// if first character is !, result is negated.
|
||||
func buildTagOk(ctx *build.Context, s string) (r bool) {
|
||||
not := s[0] == '!'
|
||||
if not {
|
||||
@@ -112,7 +112,7 @@ func contains(tags []string, tag string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// goMinorVersion returns the go minor version number
|
||||
// goMinorVersion returns the go minor version number.
|
||||
func goMinorVersion(ctx *build.Context) int {
|
||||
current := ctx.ReleaseTags[len(ctx.ReleaseTags)-1]
|
||||
|
||||
@@ -128,7 +128,7 @@ func goMinorVersion(ctx *build.Context) int {
|
||||
return m
|
||||
}
|
||||
|
||||
// skipFile returns true if file should be skipped
|
||||
// skipFile returns true if file should be skipped.
|
||||
func skipFile(ctx *build.Context, p string) bool {
|
||||
if !strings.HasSuffix(p, ".go") {
|
||||
return true
|
||||
|
||||
468
interp/cfg.go
468
interp/cfg.go
@@ -2,14 +2,17 @@ package interp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// A cfgError represents an error during CFG build stage
|
||||
// A cfgError represents an error during CFG build stage.
|
||||
type cfgError struct {
|
||||
*node
|
||||
error
|
||||
@@ -46,12 +49,14 @@ var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run
|
||||
// Following this pass, the CFG is ready to run.
|
||||
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
var initNodes []*node
|
||||
var err error
|
||||
|
||||
baseName := filepath.Base(interp.fset.Position(root.pos).Filename)
|
||||
|
||||
root.Walk(func(n *node) bool {
|
||||
// Pre-order processing
|
||||
if err != nil {
|
||||
@@ -65,6 +70,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// type of array like value is not yet known. This could be fixed in ast structure
|
||||
// by setting array/map node as 1st child of ForRangeStmt instead of 3rd child of
|
||||
// RangeStmt. The following workaround is less elegant but ok.
|
||||
c := n.anc.child[1]
|
||||
if c != nil && c.typ != nil && isSendChan(c.typ) {
|
||||
err = c.cfgErrorf("invalid operation: range %s receive from send-only channel", c.ident)
|
||||
return false
|
||||
}
|
||||
|
||||
if t := sc.rangeChanType(n.anc); t != nil {
|
||||
// range over channel
|
||||
e := n.anc.child[0]
|
||||
@@ -398,7 +409,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
|
||||
case arrayType, basicLit, chanType, funcType, interfaceType, mapType, structType:
|
||||
case arrayType, basicLit, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
|
||||
n.typ, err = nodeType(interp, sc, n)
|
||||
return false
|
||||
}
|
||||
@@ -459,9 +470,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if src.typ.isBinMethod {
|
||||
dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()}
|
||||
} else {
|
||||
dest.typ = src.typ
|
||||
// In a new definition, propagate the source type to the destination
|
||||
// type. If the source is an untyped constant, make sure that the
|
||||
// type matches a default type.
|
||||
dest.typ = sc.fixType(src.typ)
|
||||
}
|
||||
}
|
||||
if dest.typ.incomplete {
|
||||
return
|
||||
}
|
||||
if dest.typ.sizedef {
|
||||
dest.typ.size = arrayTypeLen(src)
|
||||
dest.typ.rtype = nil
|
||||
@@ -504,13 +521,30 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = src.cfgErrorf("invalid float truncate")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Rudimentary type check at this point,
|
||||
// improvements need to be made to make it better.
|
||||
switch {
|
||||
case dest.typ.untyped || src.typ.untyped:
|
||||
// Both side of the assignment must be typed.
|
||||
case isRecursiveType(dest.typ, dest.typ.rtype) || isRecursiveType(src.typ, src.typ.rtype):
|
||||
// Recursive types cannot be type checked.
|
||||
case t0.Kind() == reflect.Interface || t0.Kind() == reflect.Func:
|
||||
// We have no way of checking interfaces and functions.
|
||||
case t1.AssignableTo(t0):
|
||||
// All is well when they are assignable.
|
||||
default:
|
||||
err = src.cfgErrorf("cannot use type %s as type %s in assignment", src.typ.id(), dest.typ.id())
|
||||
return
|
||||
}
|
||||
}
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
switch {
|
||||
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
|
||||
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isMapEntry(dest) && !isRecursiveField(dest):
|
||||
// Call action may perform the assignment directly.
|
||||
n.gen = nop
|
||||
src.level = level
|
||||
@@ -543,8 +577,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case !src.rval.IsValid():
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
case n.anc.kind == constDecl:
|
||||
// Possible conversion from const to actual type will be handled later
|
||||
default:
|
||||
// Convert literal value to destination type.
|
||||
convertConstantValue(src)
|
||||
src.rval = src.rval.Convert(dest.typ.TypeOf())
|
||||
src.typ = dest.typ
|
||||
}
|
||||
@@ -559,6 +596,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest.gen = nop // skip getIndexMap
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
if !dest.typ.untyped {
|
||||
// If the dest is untyped, any constant rval needs to be converted
|
||||
convertConstantValue(src)
|
||||
}
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
if childPos(n) == len(n.anc.child)-1 {
|
||||
sc.iota = 0
|
||||
@@ -620,40 +663,91 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
nilSym := interp.universe.sym["nil"]
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
// Shift operator type is inherited from first parameter only.
|
||||
// All other binary operators require both parameter types to be the same.
|
||||
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equals(c1.typ) {
|
||||
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
|
||||
isConstVal := func(n *node) bool {
|
||||
return n.rval.IsValid() && isConstantValue(n.rval.Type())
|
||||
}
|
||||
|
||||
// Type check the binary expression. Mimics Gos logic as closely and possible.
|
||||
c := c0
|
||||
if isConstVal(c) {
|
||||
c = c1
|
||||
}
|
||||
|
||||
if isShiftNode(n) {
|
||||
if !c1.isNatural() {
|
||||
err = n.cfgErrorf("invalid operation: shift count type %v, must be integer", strings.TrimLeft(c1.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
|
||||
if !c0.isInteger() {
|
||||
err = n.cfgErrorf("invalid operation: shift of type %v", strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isShiftNode(n) && isComparisonNode(n) && !isConstVal(c) && !c0.typ.equals(c1.typ) {
|
||||
if isInterface(c1.typ) && !isInterface(c0.typ) && !c0.typ.comparable() {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
|
||||
if isInterface(c0.typ) && !isInterface(c1.typ) && !c1.typ.comparable() {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c1.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isShiftNode(n) && !isConstVal(c) && !c0.typ.equals(c1.typ) && t0 != nil && t1 != nil {
|
||||
switch {
|
||||
case isConstVal(c0) && isNumber(t1) || isConstVal(c1) && isNumber(t0): // const <-> numberic case
|
||||
case isNumber(t0) && isNumber(t1) && (c0.typ.untyped || c1.typ.untyped):
|
||||
case t0.Kind() == reflect.Uint8 && t1.Kind() == reflect.Int32 || t1.Kind() == reflect.Uint8 && t0.Kind() == reflect.Int32: // byte <-> rune case
|
||||
case isInterface(c0.typ) && isInterface(c1.typ): // interface <-> interface case
|
||||
default:
|
||||
err = n.cfgErrorf("invalid operation: mismatched types %s and %s", strings.TrimLeft(c0.typ.id(), "."), strings.TrimLeft(c1.typ.id(), "."))
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cat := c.typ.cat
|
||||
switch {
|
||||
case isConstVal(c):
|
||||
cat = catOfConst(c.rval)
|
||||
case c.typ.cat == valueT:
|
||||
cat = catOf(c.typ.rtype)
|
||||
case c.typ.cat == aliasT:
|
||||
cat = c.typ.val.cat
|
||||
}
|
||||
if !isShiftNode(n) && !okFor[n.action][cat] {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
if !isShiftNode(n) && isConstVal(c0) && isConstVal(c1) {
|
||||
// If both are constants, check the left type as well.
|
||||
if !okFor[n.action][catOfConst(c0.rval)] {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch n.action {
|
||||
case aAdd:
|
||||
if !(isNumber(t0) && isNumber(t1) || isString(t0) && isString(t1)) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aSub, aMul, aQuo:
|
||||
if !(isNumber(t0) && isNumber(t1)) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aAnd, aOr, aXor, aAndNot:
|
||||
if !(isInt(t0) && isInt(t1)) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aRem:
|
||||
if !(c0.isInteger() && c1.isInteger()) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
n.typ = c0.typ
|
||||
case aShl, aShr:
|
||||
if !(c0.isInteger() && c1.isNatural()) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aRem, aShl, aShr:
|
||||
n.typ = c0.typ
|
||||
case aEqual, aNotEqual:
|
||||
if isNumber(t0) && !isNumber(t1) || isString(t0) && !isString(t1) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
n.typ = sc.getType("bool")
|
||||
if isConstVal(c0) && !isConstVal(c1) || !isConstVal(c0) && isConstVal(c1) {
|
||||
// if either node is a constant value, but the other is not, the constant
|
||||
// must be converted into the non-constants type.
|
||||
switch {
|
||||
case isConstVal(c0):
|
||||
convertConstantValue(c0)
|
||||
c0.rval = c0.rval.Convert(c1.typ.TypeOf())
|
||||
default:
|
||||
convertConstantValue(c1)
|
||||
c1.rval = c1.rval.Convert(c0.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
if n.child[0].sym == nilSym || n.child[1].sym == nilSym {
|
||||
if n.action == aEqual {
|
||||
n.gen = isNil
|
||||
@@ -662,9 +756,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
case aGreater, aGreaterEqual, aLower, aLowerEqual:
|
||||
if isNumber(t0) && !isNumber(t1) || isString(t0) && !isString(t1) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
n.typ = sc.getType("bool")
|
||||
}
|
||||
if err != nil {
|
||||
@@ -691,6 +782,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
case n.anc.kind == returnStmt:
|
||||
// To avoid a copy in frame, if the result is to be returned, store it directly
|
||||
// at the frame location reserved for output arguments.
|
||||
@@ -746,6 +838,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if len(n.child) > 0 {
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
n.level = l.level
|
||||
n.val = l.val
|
||||
n.sym = l.sym
|
||||
n.typ = l.typ
|
||||
@@ -753,13 +846,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
sc = sc.pop()
|
||||
|
||||
case constDecl, varDecl:
|
||||
case constDecl:
|
||||
wireChild(n)
|
||||
|
||||
case varDecl:
|
||||
// Global varDecl do not need to be wired as this
|
||||
// will be handled after cfg.
|
||||
if n.anc.kind == fileStmt {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
wireChild(n)
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
n.level = l.level
|
||||
n.val = l.val
|
||||
n.sym = l.sym
|
||||
n.typ = l.typ
|
||||
@@ -826,6 +928,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.gen = nop
|
||||
n.typ = n.child[1].typ
|
||||
n.findex = n.child[1].findex
|
||||
n.level = n.child[1].level
|
||||
n.val = n.child[1].val
|
||||
n.rval = n.child[1].rval
|
||||
} else {
|
||||
@@ -939,7 +1042,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
|
||||
case fileStmt:
|
||||
wireChild(n)
|
||||
wireChild(n, varDecl)
|
||||
sc = sc.pop()
|
||||
n.findex = -1
|
||||
|
||||
@@ -1051,11 +1154,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.types = sc.types
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if !isMethod(n) {
|
||||
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
|
||||
interp.scopes[pkgID].sym[funcName].typ = n.typ
|
||||
interp.scopes[pkgID].sym[funcName].kind = funcSym
|
||||
interp.scopes[pkgID].sym[funcName].node = n
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
|
||||
sym.index = -1 // to force value to n.val
|
||||
sym.typ = n.typ
|
||||
sym.kind = funcSym
|
||||
sym.node = n
|
||||
}
|
||||
|
||||
case funcLit:
|
||||
@@ -1070,36 +1173,48 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if isKey(n) || isNewDefine(n, sc) {
|
||||
break
|
||||
}
|
||||
if sym, level, ok := sc.lookup(n.ident); ok {
|
||||
// Found symbol, populate node info
|
||||
n.typ, n.findex, n.level = sym.typ, sym.index, level
|
||||
if n.findex < 0 {
|
||||
n.val = sym.node
|
||||
} else {
|
||||
n.sym = sym
|
||||
switch {
|
||||
case sym.kind == constSym && sym.rval.IsValid():
|
||||
n.rval = sym.rval
|
||||
n.kind = basicLit
|
||||
case n.ident == "iota":
|
||||
n.rval = reflect.ValueOf(sc.iota)
|
||||
n.kind = basicLit
|
||||
case n.ident == "nil":
|
||||
n.kind = basicLit
|
||||
case sym.kind == binSym:
|
||||
n.typ = sym.typ
|
||||
n.rval = sym.rval
|
||||
case sym.kind == bltnSym:
|
||||
if n.anc.kind != callExpr {
|
||||
err = n.cfgErrorf("use of builtin %s not in function call", n.ident)
|
||||
}
|
||||
if n.anc.kind == funcDecl && n.anc.child[1] == n {
|
||||
// Dont process a function name identExpr.
|
||||
break
|
||||
}
|
||||
|
||||
sym, level, found := sc.lookup(n.ident)
|
||||
if !found {
|
||||
// retry with the filename, in case ident is a package name.
|
||||
// TODO(mpl): maybe we improve lookup itself so it can deal with that.
|
||||
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
|
||||
if !found {
|
||||
err = n.cfgErrorf("undefined: %s", n.ident)
|
||||
break
|
||||
}
|
||||
}
|
||||
// Found symbol, populate node info
|
||||
n.typ, n.findex, n.level = sym.typ, sym.index, level
|
||||
if n.findex < 0 {
|
||||
n.val = sym.node
|
||||
} else {
|
||||
n.sym = sym
|
||||
switch {
|
||||
case sym.kind == constSym && sym.rval.IsValid():
|
||||
n.rval = sym.rval
|
||||
n.kind = basicLit
|
||||
case n.ident == "iota":
|
||||
n.rval = reflect.ValueOf(sc.iota)
|
||||
n.kind = basicLit
|
||||
n.typ.untyped = true
|
||||
case n.ident == "nil":
|
||||
n.kind = basicLit
|
||||
case sym.kind == binSym:
|
||||
n.typ = sym.typ
|
||||
n.rval = sym.rval
|
||||
case sym.kind == bltnSym:
|
||||
if n.anc.kind != callExpr {
|
||||
err = n.cfgErrorf("use of builtin %s not in function call", n.ident)
|
||||
}
|
||||
}
|
||||
if n.sym != nil {
|
||||
n.recv = n.sym.recv
|
||||
}
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined: %s", n.ident)
|
||||
}
|
||||
if n.sym != nil {
|
||||
n.recv = n.sym.recv
|
||||
}
|
||||
|
||||
case ifStmt0: // if cond {}
|
||||
@@ -1213,12 +1328,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
wireChild(n)
|
||||
c := n.lastChild()
|
||||
n.findex = c.findex
|
||||
n.level = c.level
|
||||
n.typ = c.typ
|
||||
n.rval = c.rval
|
||||
|
||||
case rangeStmt:
|
||||
if sc.rangeChanType(n) != nil {
|
||||
n.start = n.child[1] // Get chan
|
||||
n.start = n.child[1].start // Get chan
|
||||
n.child[1].tnext = n // then go to range function
|
||||
n.tnext = n.child[2].start // then go to range body
|
||||
n.child[2].tnext = n // then body go to range function (loop)
|
||||
@@ -1230,7 +1346,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
k, o, body = n.child[0], n.child[1], n.child[2]
|
||||
}
|
||||
n.start = o // Get array or map object
|
||||
n.start = o.start // Get array or map object
|
||||
o.tnext = k.start // then go to iterator init
|
||||
k.tnext = n // then go to range function
|
||||
n.tnext = body.start // then go to range body
|
||||
@@ -1615,7 +1731,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
if n.anc.action != aAssignX {
|
||||
if n.child[0].typ.cat == valueT {
|
||||
if n.child[0].typ.cat == valueT && !isStruct(n.child[1].typ) {
|
||||
// Avoid special wrapping of interfaces and func types.
|
||||
n.typ = &itype{cat: valueT, rtype: n.child[1].typ.TypeOf()}
|
||||
} else {
|
||||
@@ -1679,6 +1795,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
case n.anc.kind == returnStmt:
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
@@ -1791,13 +1908,13 @@ func compDefineX(sc *scope, n *node) error {
|
||||
}
|
||||
|
||||
// TODO used for allocation optimization, temporarily disabled
|
||||
//func isAncBranch(n *node) bool {
|
||||
// func isAncBranch(n *node) bool {
|
||||
// switch n.anc.kind {
|
||||
// case If0, If1, If2, If3:
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
//}
|
||||
// }
|
||||
|
||||
func childPos(n *node) int {
|
||||
for i, c := range n.anc.child {
|
||||
@@ -1837,6 +1954,90 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func genGlobalVars(roots []*node, sc *scope) (*node, error) {
|
||||
var vars []*node
|
||||
for _, n := range roots {
|
||||
vars = append(vars, getVars(n)...)
|
||||
}
|
||||
|
||||
if len(vars) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
varNode, err := genGlobalVarDecl(vars, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setExec(varNode.start)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVars(n *node) (vars []*node) {
|
||||
for _, child := range n.child {
|
||||
if child.kind == varDecl {
|
||||
vars = append(vars, child.child...)
|
||||
}
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
varNode := &node{kind: varDecl, action: aNop, gen: nop}
|
||||
|
||||
deps := map[*node][]*node{}
|
||||
for _, n := range nodes {
|
||||
deps[n] = getVarDependencies(n, sc)
|
||||
}
|
||||
|
||||
inited := map[*node]bool{}
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
canInit := true
|
||||
for _, d := range deps[n] {
|
||||
if !inited[d] {
|
||||
canInit = false
|
||||
}
|
||||
}
|
||||
if !canInit {
|
||||
revisit = append(revisit, n)
|
||||
continue
|
||||
}
|
||||
|
||||
varNode.child = append(varNode.child, n)
|
||||
inited[n] = true
|
||||
}
|
||||
|
||||
if len(revisit) == 0 || equalNodes(nodes, revisit) {
|
||||
break
|
||||
}
|
||||
|
||||
nodes = revisit
|
||||
revisit = []*node{}
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return nil, revisit[0].cfgErrorf("variable definition loop")
|
||||
}
|
||||
wireChild(varNode)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
|
||||
nod.Walk(func(n *node) bool {
|
||||
if n.kind == identExpr {
|
||||
if sym, _, ok := sc.lookup(n.ident); ok {
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
|
||||
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
|
||||
// and sets the action to branch.
|
||||
func setFNext(cond, next *node) {
|
||||
@@ -1869,10 +2070,10 @@ func getDefault(n *node) int {
|
||||
|
||||
func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect.Ptr && v.IsNil() }
|
||||
|
||||
// isType returns true if node refers to a type definition, false otherwise
|
||||
// isType returns true if node refers to a type definition, false otherwise.
|
||||
func (n *node) isType(sc *scope) bool {
|
||||
switch n.kind {
|
||||
case arrayType, chanType, funcType, interfaceType, mapType, structType:
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
|
||||
return true
|
||||
case parenExpr, starExpr:
|
||||
if len(n.child) == 1 {
|
||||
@@ -1895,48 +2096,69 @@ func (n *node) isType(sc *scope) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// wireChild wires AST nodes for CFG in subtree
|
||||
func wireChild(n *node) {
|
||||
// wireChild wires AST nodes for CFG in subtree.
|
||||
func wireChild(n *node, exclude ...nkind) {
|
||||
child := excludeNodeKind(n.child, exclude)
|
||||
|
||||
// Set start node, in subtree (propagated to ancestors by post-order processing)
|
||||
for _, child := range n.child {
|
||||
switch child.kind {
|
||||
case arrayType, chanType, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
|
||||
for _, c := range child {
|
||||
switch c.kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
default:
|
||||
n.start = child.start
|
||||
n.start = c.start
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Chain sequential operations inside a block (next is right sibling)
|
||||
for i := 1; i < len(n.child); i++ {
|
||||
switch n.child[i].kind {
|
||||
for i := 1; i < len(child); i++ {
|
||||
switch child[i].kind {
|
||||
case funcDecl:
|
||||
n.child[i-1].tnext = n.child[i]
|
||||
child[i-1].tnext = child[i]
|
||||
default:
|
||||
switch n.child[i-1].kind {
|
||||
switch child[i-1].kind {
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i-1].tnext = n.child[i].start
|
||||
child[i-1].tnext = child[i].start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chain subtree next to self
|
||||
for i := len(n.child) - 1; i >= 0; i-- {
|
||||
switch n.child[i].kind {
|
||||
case arrayType, chanType, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
|
||||
for i := len(child) - 1; i >= 0; i-- {
|
||||
switch child[i].kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i].tnext = n
|
||||
child[i].tnext = n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func excludeNodeKind(child []*node, kinds []nkind) []*node {
|
||||
if len(kinds) == 0 {
|
||||
return child
|
||||
}
|
||||
var res []*node
|
||||
for _, c := range child {
|
||||
exclude := false
|
||||
for _, k := range kinds {
|
||||
if c.kind == k {
|
||||
exclude = true
|
||||
}
|
||||
}
|
||||
if !exclude {
|
||||
res = append(res, c)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (n *node) name() (s string) {
|
||||
switch {
|
||||
case n.ident != "":
|
||||
@@ -1947,7 +2169,7 @@ func (n *node) name() (s string) {
|
||||
return s
|
||||
}
|
||||
|
||||
// isInteger returns true if node type is integer, false otherwise
|
||||
// isInteger returns true if node type is integer, false otherwise.
|
||||
func (n *node) isInteger() bool {
|
||||
if isInt(n.typ.TypeOf()) {
|
||||
return true
|
||||
@@ -1966,11 +2188,25 @@ func (n *node) isInteger() bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if isConstantValue(t) {
|
||||
c := n.rval.Interface().(constant.Value)
|
||||
switch c.Kind() {
|
||||
case constant.Int:
|
||||
return true
|
||||
case constant.Float:
|
||||
f, _ := constant.Float64Val(c)
|
||||
if f == math.Trunc(f) {
|
||||
n.rval = reflect.ValueOf(constant.ToInt(c))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNatural returns true if node type is natural, false otherwise
|
||||
// isNatural returns true if node type is natural, false otherwise.
|
||||
func (n *node) isNatural() bool {
|
||||
if isUint(n.typ.TypeOf()) {
|
||||
return true
|
||||
@@ -1993,14 +2229,31 @@ func (n *node) isNatural() bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if isConstantValue(t) {
|
||||
c := n.rval.Interface().(constant.Value)
|
||||
switch c.Kind() {
|
||||
case constant.Int:
|
||||
i, _ := constant.Int64Val(c)
|
||||
if i >= 0 {
|
||||
return true
|
||||
}
|
||||
case constant.Float:
|
||||
f, _ := constant.Float64Val(c)
|
||||
if f == math.Trunc(f) {
|
||||
n.rval = reflect.ValueOf(constant.ToInt(c))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNil returns true if node is a literal nil value, false otherwise
|
||||
// isNil returns true if node is a literal nil value, false otherwise.
|
||||
func (n *node) isNil() bool { return n.kind == basicLit && !n.rval.IsValid() }
|
||||
|
||||
// fieldType returns the nth parameter field node (type) of a fieldList node
|
||||
// fieldType returns the nth parameter field node (type) of a fieldList node.
|
||||
func (n *node) fieldType(m int) *node {
|
||||
k := 0
|
||||
l := len(n.child)
|
||||
@@ -2023,7 +2276,7 @@ func (n *node) fieldType(m int) *node {
|
||||
return nil
|
||||
}
|
||||
|
||||
// lastChild returns the last child of a node
|
||||
// lastChild returns the last child of a node.
|
||||
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
|
||||
|
||||
func isKey(n *node) bool {
|
||||
@@ -2039,10 +2292,20 @@ func isField(n *node) bool {
|
||||
}
|
||||
|
||||
func isRecursiveField(n *node) bool {
|
||||
return isField(n) && (n.typ.recursive || n.typ.cat == ptrT && n.typ.val.recursive)
|
||||
if !isField(n) {
|
||||
return false
|
||||
}
|
||||
t := n.typ
|
||||
for t != nil {
|
||||
if t.recursive {
|
||||
return true
|
||||
}
|
||||
t = t.val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition
|
||||
// isNewDefine returns true if node refers to a new definition.
|
||||
func isNewDefine(n *node, sc *scope) bool {
|
||||
if n.ident == "_" {
|
||||
return true
|
||||
@@ -2234,12 +2497,15 @@ func arrayTypeLen(n *node) int {
|
||||
return max + 1
|
||||
}
|
||||
|
||||
// isValueUntyped returns true if value is untyped
|
||||
// isValueUntyped returns true if value is untyped.
|
||||
func isValueUntyped(v reflect.Value) bool {
|
||||
// Consider only constant values.
|
||||
if v.CanSet() {
|
||||
return false
|
||||
}
|
||||
t := v.Type()
|
||||
if t.Implements(constVal) {
|
||||
return true
|
||||
}
|
||||
return t.String() == t.Kind().String()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// astDot displays an AST in graphviz dot(1) format using dotty(1) co-process
|
||||
// astDot displays an AST in graphviz dot(1) format using dotty(1) co-process.
|
||||
func (n *node) astDot(out io.Writer, name string) {
|
||||
fmt.Fprintf(out, "digraph ast {\n")
|
||||
fmt.Fprintf(out, "labelloc=\"t\"\n")
|
||||
@@ -36,7 +36,7 @@ func (n *node) astDot(out io.Writer, name string) {
|
||||
fmt.Fprintf(out, "}\n")
|
||||
}
|
||||
|
||||
// cfgDot displays a CFG in graphviz dot(1) format using dotty(1) co-process
|
||||
// cfgDot displays a CFG in graphviz dot(1) format using dotty(1) co-process.
|
||||
func (n *node) cfgDot(out io.Writer) {
|
||||
fmt.Fprintf(out, "digraph cfg {\n")
|
||||
n.Walk(nil, func(n *node) {
|
||||
@@ -66,7 +66,7 @@ type nopCloser struct {
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
// dotWriter returns an output stream to a dot(1) co-process where to write data in .dot format
|
||||
// dotWriter returns an output stream to a dot(1) co-process where to write data in .dot format.
|
||||
func dotWriter(dotCmd string) io.WriteCloser {
|
||||
if dotCmd == "" {
|
||||
return nopCloser{ioutil.Discard}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/containous/yaegi/interp"
|
||||
)
|
||||
|
||||
// Generic example
|
||||
// Generic example.
|
||||
func Example_eval() {
|
||||
// Create a new interpreter context
|
||||
i := interp.New(interp.Options{})
|
||||
|
||||
108
interp/gta.go
108
interp/gta.go
@@ -1,6 +1,9 @@
|
||||
package interp
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// gta performs a global types analysis on the AST, registering types,
|
||||
// variables and functions symbols at package level, prior to CFG.
|
||||
@@ -11,6 +14,8 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
var err error
|
||||
var revisit []*node
|
||||
|
||||
baseName := filepath.Base(interp.fset.Position(root.pos).Filename)
|
||||
|
||||
root.Walk(func(n *node) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -73,8 +78,8 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if typ.isBinMethod {
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
|
||||
}
|
||||
if sc.sym[dest.ident] == nil {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
if sc.sym[dest.ident] == nil || sc.sym[dest.ident].typ.incomplete {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
@@ -103,17 +108,43 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[:l] {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
|
||||
asImportName := filepath.Join(c.ident, baseName)
|
||||
sym1, exists1 := sc.sym[asImportName]
|
||||
sym2, exists2 := sc.sym[c.ident]
|
||||
if !exists1 && !exists2 {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
|
||||
continue
|
||||
}
|
||||
|
||||
var sym *symbol
|
||||
if exists1 {
|
||||
// prev declaration is an import statement
|
||||
sym = sym1
|
||||
} else {
|
||||
// prev declaration is whatever else (var, type, etc)
|
||||
sym = sym2
|
||||
}
|
||||
// redeclaration error
|
||||
if sym.typ.node != nil && sym.typ.node.anc != nil {
|
||||
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 false
|
||||
}
|
||||
err = n.cfgErrorf("%s redeclared in this block", c.ident)
|
||||
return false
|
||||
}
|
||||
|
||||
case funcDecl:
|
||||
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return false
|
||||
}
|
||||
if isMethod(n) {
|
||||
ident := n.child[1].ident
|
||||
switch {
|
||||
case isMethod(n):
|
||||
// TODO(mpl): redeclaration detection
|
||||
// Add a method symbol in the receiver type name space
|
||||
var rcvrtype *itype
|
||||
n.ident = n.child[1].ident
|
||||
n.ident = ident
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
@@ -138,8 +169,32 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
rcvrtype.method = append(rcvrtype.method, n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
} else {
|
||||
// Add a function symbol in the package name space
|
||||
case ident == "init":
|
||||
// TODO(mpl): use constant instead of hardcoded string?
|
||||
// init functions do not get declared as per the Go spec.
|
||||
default:
|
||||
asImportName := filepath.Join(ident, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[ident]
|
||||
if exists {
|
||||
// Make sure the symbol we found seems to be about another node, before calling
|
||||
// it a redeclaration.
|
||||
if sym.typ.isComplete() {
|
||||
// TODO(mpl): this check might be too permissive?
|
||||
if sym.kind != funcSym || sym.typ.cat != n.typ.cat || sym.node != n || sym.index != -1 {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a function symbol in the package name space except for init
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if !n.typ.isComplete() {
|
||||
@@ -172,8 +227,20 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if name == "" {
|
||||
name = identifier.FindString(ipath)
|
||||
}
|
||||
// imports of a same package are all mapped in the same scope, so we cannot just
|
||||
// map them by their names, otherwise we could have collisions from same-name
|
||||
// imports in different source files of the same package. Therefore, we suffix
|
||||
// the key with the basename of the source file.
|
||||
name = filepath.Join(name, baseName)
|
||||
if _, exists := sc.sym[name]; !exists {
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
|
||||
break
|
||||
}
|
||||
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
|
||||
// redeclaration error
|
||||
// TODO(mpl): find position information about previous declaration
|
||||
err = n.cfgErrorf("%s redeclared in this block", name)
|
||||
return false
|
||||
}
|
||||
} else if pkgName, err = interp.importSrc(rpath, ipath); err == nil {
|
||||
sc.types = interp.universe.types
|
||||
@@ -191,6 +258,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
|
||||
// TODO(mpl): redecleration detection
|
||||
}
|
||||
} else {
|
||||
err = n.cfgErrorf("import %q error: %v", ipath, err)
|
||||
@@ -202,6 +270,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
|
||||
copy(n.typ.method, typ.method)
|
||||
@@ -210,11 +279,26 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
n.typ.name = typeName
|
||||
n.typ.path = rpath
|
||||
}
|
||||
// Type may be already declared for a receiver in a method function
|
||||
if sc.sym[typeName] == nil {
|
||||
|
||||
asImportName := filepath.Join(typeName, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", typeName)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[typeName]
|
||||
if !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
} else {
|
||||
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
|
||||
if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
// Type has already been seen as a receiver in a method function
|
||||
n.typ.method = append(n.typ.method, sym.typ.method...)
|
||||
} else {
|
||||
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
|
||||
// Allow redeclarations for now.
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
if !n.typ.isComplete() {
|
||||
|
||||
30
interp/hooks.go
Normal file
30
interp/hooks.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package interp
|
||||
|
||||
import "reflect"
|
||||
|
||||
const hooksPath = "github.com/containous/yaegi"
|
||||
|
||||
// convertFn is the signature of a symbol converter.
|
||||
type convertFn func(from, to reflect.Type) func(src, dest reflect.Value)
|
||||
|
||||
// hooks are external symbol bindings.
|
||||
type hooks struct {
|
||||
convert []convertFn
|
||||
}
|
||||
|
||||
func (h *hooks) Parse(m map[string]reflect.Value) {
|
||||
if con, ok := getConvertFn(m["convert"]); ok {
|
||||
h.convert = append(h.convert, con)
|
||||
}
|
||||
}
|
||||
|
||||
func getConvertFn(v reflect.Value) (convertFn, bool) {
|
||||
if !v.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
fn, ok := v.Interface().(func(from, to reflect.Type) func(src, dest reflect.Value))
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return fn, true
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Interpreter node structure for AST and CFG
|
||||
// Interpreter node structure for AST and CFG.
|
||||
type node struct {
|
||||
child []*node // child subtrees (AST)
|
||||
anc *node // ancestor (AST)
|
||||
@@ -46,14 +46,14 @@ type node struct {
|
||||
ident string // set if node is a var or func
|
||||
}
|
||||
|
||||
// receiver stores method receiver object access path
|
||||
// receiver stores method receiver object access path.
|
||||
type receiver struct {
|
||||
node *node // receiver value for alias and struct types
|
||||
val reflect.Value // receiver value for interface type and value type
|
||||
index []int // path in receiver value for interface or value type
|
||||
}
|
||||
|
||||
// frame contains values for the current execution level (a function context)
|
||||
// frame contains values for the current execution level (a function context).
|
||||
type frame struct {
|
||||
// id is an atomic counter used for cancellation, only access
|
||||
// via newFrame/runid/setrunid/clone.
|
||||
@@ -96,13 +96,13 @@ func (f *frame) clone() *frame {
|
||||
}
|
||||
}
|
||||
|
||||
// Exports stores the map of binary packages per package path
|
||||
// Exports stores the map of binary packages per package path.
|
||||
type Exports map[string]map[string]reflect.Value
|
||||
|
||||
// imports stores the map of source packages per package path
|
||||
// imports stores the map of source packages per package path.
|
||||
type imports map[string]map[string]*symbol
|
||||
|
||||
// opt stores interpreter options
|
||||
// opt stores interpreter options.
|
||||
type opt struct {
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
@@ -114,7 +114,7 @@ type opt struct {
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
}
|
||||
|
||||
// Interpreter contains global resources and state
|
||||
// Interpreter contains global resources and state.
|
||||
type Interpreter struct {
|
||||
// id is an atomic counter counter used for run cancellation,
|
||||
// only accessed via runid/stop
|
||||
@@ -138,6 +138,8 @@ type Interpreter struct {
|
||||
srcPkg imports // source packages used in interpreter, indexed by path
|
||||
pkgNames map[string]string // package names, indexed by path
|
||||
done chan struct{} // for cancellation of channel operations
|
||||
|
||||
hooks *hooks // symbol hooks
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -145,7 +147,7 @@ const (
|
||||
selfPath = "github.com/containous/yaegi/interp"
|
||||
)
|
||||
|
||||
// Symbols exposes interpreter values
|
||||
// Symbols exposes interpreter values.
|
||||
var Symbols = Exports{
|
||||
selfPath: map[string]reflect.Value{
|
||||
"New": reflect.ValueOf(New),
|
||||
@@ -157,7 +159,7 @@ var Symbols = Exports{
|
||||
|
||||
func init() { Symbols[selfPath]["Symbols"] = reflect.ValueOf(Symbols) }
|
||||
|
||||
// _error is a wrapper of error interface type
|
||||
// _error is a wrapper of error interface type.
|
||||
type _error struct {
|
||||
WError func() string
|
||||
}
|
||||
@@ -204,7 +206,7 @@ type Options struct {
|
||||
BuildTags []string
|
||||
}
|
||||
|
||||
// New returns a new interpreter
|
||||
// New returns a new interpreter.
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{context: build.Default},
|
||||
@@ -216,6 +218,7 @@ func New(options Options) *Interpreter {
|
||||
srcPkg: imports{},
|
||||
pkgNames: map[string]string{},
|
||||
rdir: map[string]bool{},
|
||||
hooks: &hooks{},
|
||||
}
|
||||
|
||||
i.opt.context.GOPATH = options.GoPath
|
||||
@@ -295,7 +298,7 @@ func initUniverse() *scope {
|
||||
return sc
|
||||
}
|
||||
|
||||
// resizeFrame resizes the global frame of interpreter
|
||||
// resizeFrame resizes the global frame of interpreter.
|
||||
func (interp *Interpreter) resizeFrame() {
|
||||
l := len(interp.universe.types)
|
||||
b := len(interp.frame.data)
|
||||
@@ -320,7 +323,7 @@ func (interp *Interpreter) main() *node {
|
||||
}
|
||||
|
||||
// Eval evaluates Go code represented as a string. It returns a map on
|
||||
// current interpreted package exported symbols
|
||||
// current interpreted package exported symbols.
|
||||
func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
@@ -402,6 +405,13 @@ func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
// Execute node closures
|
||||
interp.run(root, nil)
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars([]*node{root}, interp.scopes[interp.Name])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
for _, n := range initNodes {
|
||||
interp.run(n, interp.frame)
|
||||
}
|
||||
@@ -454,7 +464,7 @@ func (interp *Interpreter) stop() {
|
||||
|
||||
func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found
|
||||
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found.
|
||||
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
if p, ok := interp.binPkg[t.PkgPath()]; ok {
|
||||
return p["_"+t.Name()].Type().Elem()
|
||||
@@ -463,10 +473,22 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
}
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) {
|
||||
for k, v := range values {
|
||||
interp.binPkg[k] = v
|
||||
if k == hooksPath {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
}
|
||||
|
||||
if interp.binPkg[k] == nil {
|
||||
interp.binPkg[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[k][s] = sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +545,7 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
|
||||
// Repl performs a Read-Eval-Print-Loop on input file descriptor.
|
||||
// Results are printed on output.
|
||||
// Deprecated: use REPL instead
|
||||
// Deprecated: use REPL instead.
|
||||
func (interp *Interpreter) Repl(in, out *os.File) {
|
||||
interp.REPL(in, out)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
func TestInterpConsistencyBuild(t *testing.T) {
|
||||
@@ -34,6 +35,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
if filepath.Ext(file.Name()) != ".go" ||
|
||||
file.Name() == "assign11.go" || // expect error
|
||||
file.Name() == "assign12.go" || // expect error
|
||||
file.Name() == "assign15.go" || // expect error
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
@@ -43,8 +45,11 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "fun22.go" || // expect error
|
||||
file.Name() == "if2.go" || // expect error
|
||||
file.Name() == "import6.go" || // expect error
|
||||
file.Name() == "init1.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "op7.go" || // expect error
|
||||
file.Name() == "op9.go" || // expect error
|
||||
file.Name() == "bltn0.go" || // expect error
|
||||
file.Name() == "method16.go" || // private struct field
|
||||
file.Name() == "switch8.go" || // expect error
|
||||
@@ -64,6 +69,17 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "redeclaration3.go" || // expect error
|
||||
file.Name() == "redeclaration4.go" || // expect error
|
||||
file.Name() == "redeclaration5.go" || // expect error
|
||||
file.Name() == "redeclaration-global0.go" || // expect error
|
||||
file.Name() == "redeclaration-global1.go" || // expect error
|
||||
file.Name() == "redeclaration-global2.go" || // expect error
|
||||
file.Name() == "redeclaration-global3.go" || // expect error
|
||||
file.Name() == "redeclaration-global4.go" || // expect error
|
||||
file.Name() == "redeclaration-global5.go" || // expect error
|
||||
file.Name() == "redeclaration-global6.go" || // expect error
|
||||
file.Name() == "restricted0.go" || // expect error
|
||||
file.Name() == "restricted1.go" || // expect error
|
||||
file.Name() == "restricted2.go" || // expect error
|
||||
file.Name() == "restricted3.go" || // expect error
|
||||
file.Name() == "server6.go" || // syntax parsing
|
||||
file.Name() == "server5.go" || // syntax parsing
|
||||
file.Name() == "server4.go" || // syntax parsing
|
||||
@@ -72,7 +88,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "server1a.go" || // syntax parsing
|
||||
file.Name() == "server1.go" || // syntax parsing
|
||||
file.Name() == "server0.go" || // syntax parsing
|
||||
file.Name() == "server.go" { // syntax parsing
|
||||
file.Name() == "server.go" || // syntax parsing
|
||||
file.Name() == "range9.go" { // expect error
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -97,6 +114,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
i.Name = filePath
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
i.Use(unsafe.Symbols)
|
||||
|
||||
_, err = i.Eval(string(src))
|
||||
if err != nil {
|
||||
|
||||
@@ -34,24 +34,24 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "add_FI", src: "2.3 + 3", res: "5.3"},
|
||||
{desc: "add_IF", src: "2 + 3.3", res: "5.3"},
|
||||
{desc: "add_SS", src: `"foo" + "bar"`, res: "foobar"},
|
||||
{desc: "add_SI", src: `"foo" + 1`, err: "1:28: illegal operand types for '+' operator"},
|
||||
{desc: "sub_SS", src: `"foo" - "bar"`, err: "1:28: illegal operand types for '-' operator"},
|
||||
{desc: "add_SI", src: `"foo" + 1`, err: "1:28: invalid operation: mismatched types string and int"},
|
||||
{desc: "sub_SS", src: `"foo" - "bar"`, err: "1:28: invalid operation: operator - not defined on string"},
|
||||
{desc: "sub_II", src: "7 - 3", res: "4"},
|
||||
{desc: "sub_FI", src: "7.2 - 3", res: "4.2"},
|
||||
{desc: "sub_IF", src: "7 - 3.2", res: "3.8"},
|
||||
{desc: "mul_II", src: "2 * 3", res: "6"},
|
||||
{desc: "mul_FI", src: "2.2 * 3", res: "6.6000000000000005"},
|
||||
{desc: "mul_IF", src: "3 * 2.2", res: "6.6000000000000005"},
|
||||
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: illegal operand types for '%' operator"},
|
||||
{desc: "mul_FI", src: "2.2 * 3", res: "6.6"},
|
||||
{desc: "mul_IF", src: "3 * 2.2", res: "6.6"},
|
||||
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: invalid operation: operator % not defined on float64"},
|
||||
{desc: "shl_II", src: "1 << 8", res: "256"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: invalid operation: shift count type int, must be integer"},
|
||||
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
|
||||
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
|
||||
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: invalid operation: shift count type float64, must be integer"},
|
||||
{desc: "shl_IF2", src: "1.0 << 1", res: "2"},
|
||||
{desc: "shr_II", src: "1 >> 8", res: "0"},
|
||||
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
|
||||
{desc: "shr_IN", src: "1 >> -1", err: "1:28: invalid operation: shift count type int, must be integer"},
|
||||
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
|
||||
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
|
||||
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: invalid operation: shift count type float64, must be integer"},
|
||||
{desc: "neg_I", src: "-2", res: "-2"},
|
||||
{desc: "pos_I", src: "+2", res: "2"},
|
||||
{desc: "bitnot_I", src: "^2", res: "-3"},
|
||||
@@ -300,7 +300,7 @@ func TestEvalComparison(t *testing.T) {
|
||||
var b = Bar("test")
|
||||
var c = a == b
|
||||
`,
|
||||
err: "7:13: mismatched types main.Foo and main.Bar",
|
||||
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -385,6 +385,25 @@ func TestEvalChan(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalFunctionCallWithFunctionParam(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
eval(t, i, `
|
||||
func Bar(s string, fn func(string)string) string { return fn(s) }
|
||||
`)
|
||||
|
||||
v := eval(t, i, "Bar")
|
||||
bar := v.Interface().(func(string, func(string) string) string)
|
||||
|
||||
got := bar("hello ", func(s string) string {
|
||||
return s + "world!"
|
||||
})
|
||||
|
||||
want := "hello world!"
|
||||
if got != want {
|
||||
t.Errorf("unexpected result of function eval: got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalMissingSymbol(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
@@ -483,7 +502,7 @@ func TestEvalWithContext(t *testing.T) {
|
||||
// Successful cancellation.
|
||||
|
||||
// Check we can still execute an expression.
|
||||
v, err := i.EvalWithContext(context.Background(), "1+1\n") //nolint:govet
|
||||
v, err := i.EvalWithContext(context.Background(), "1+1\n")
|
||||
if err != nil {
|
||||
t.Errorf("failed to evaluate expression after cancellation: %v", err)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
@@ -56,6 +57,7 @@ func runCheck(t *testing.T, p string) {
|
||||
i.Name = p
|
||||
i.Use(interp.Symbols)
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(unsafe.Symbols)
|
||||
|
||||
_, err = i.Eval(string(src))
|
||||
if errWanted {
|
||||
|
||||
319
interp/op.go
319
interp/op.go
@@ -2,7 +2,11 @@ package interp
|
||||
|
||||
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Arithmetic operators
|
||||
|
||||
@@ -150,9 +154,16 @@ func add(n *node) {
|
||||
|
||||
func addConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.ADD, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isString(t):
|
||||
n.rval.SetString(v0.String() + v1.String())
|
||||
case isComplex(t):
|
||||
@@ -234,9 +245,16 @@ func and(n *node) {
|
||||
|
||||
func andConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.AND, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) & vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -312,9 +330,16 @@ func andNot(n *node) {
|
||||
|
||||
func andNotConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.AND_NOT, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) &^ vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -442,9 +467,16 @@ func mul(n *node) {
|
||||
|
||||
func mulConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.MUL, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex(vComplex(v0) * vComplex(v1))
|
||||
case isFloat(t):
|
||||
@@ -524,9 +556,16 @@ func or(n *node) {
|
||||
|
||||
func orConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.OR, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) | vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -654,9 +693,16 @@ func quo(n *node) {
|
||||
|
||||
func quoConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.QUO, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex(vComplex(v0) / vComplex(v1))
|
||||
case isFloat(t):
|
||||
@@ -736,9 +782,16 @@ func rem(n *node) {
|
||||
|
||||
func remConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.REM, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) % vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -814,9 +867,17 @@ func shl(n *node) {
|
||||
|
||||
func shlConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHL, uint(s))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) << vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -892,9 +953,17 @@ func shr(n *node) {
|
||||
|
||||
func shrConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHR, uint(s))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) >> vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -1022,9 +1091,16 @@ func sub(n *node) {
|
||||
|
||||
func subConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.SUB, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex(vComplex(v0) - vComplex(v1))
|
||||
case isFloat(t):
|
||||
@@ -1104,9 +1180,16 @@ func xor(n *node) {
|
||||
|
||||
func xorConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.XOR, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) ^ vUint(v1))
|
||||
case isInt(t):
|
||||
@@ -1865,53 +1948,83 @@ func inc(n *node) {
|
||||
}
|
||||
|
||||
func bitNotConst(n *node) {
|
||||
v0 := n.child[0].rval
|
||||
isConst := v0.IsValid() && isConstantValue(v0.Type())
|
||||
t := n.typ.rtype
|
||||
v := n.child[0].rval
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n.rval.SetInt(^v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n.rval.SetUint(^v.Uint())
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.UnaryOp(token.XOR, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isInt(t):
|
||||
n.rval.SetInt(^v0.Int())
|
||||
case isUint(t):
|
||||
n.rval.SetUint(^v0.Uint())
|
||||
}
|
||||
}
|
||||
|
||||
func negConst(n *node) {
|
||||
v0 := n.child[0].rval
|
||||
isConst := v0.IsValid() && isConstantValue(v0.Type())
|
||||
t := n.typ.rtype
|
||||
v := n.child[0].rval
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n.rval.SetInt(-v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n.rval.SetUint(-v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
n.rval.SetFloat(-v.Float())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
n.rval.SetComplex(-v.Complex())
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.UnaryOp(token.SUB, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isInt(t):
|
||||
n.rval.SetInt(-v0.Int())
|
||||
case isUint(t):
|
||||
n.rval.SetUint(-v0.Uint())
|
||||
case isFloat(t):
|
||||
n.rval.SetFloat(-v0.Float())
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex(-v0.Complex())
|
||||
}
|
||||
}
|
||||
|
||||
func notConst(n *node) {
|
||||
v0 := n.child[0].rval
|
||||
isConst := v0.IsValid() && isConstantValue(v0.Type())
|
||||
t := n.typ.rtype
|
||||
v := n.child[0].rval
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
n.rval.SetBool(!v.Bool())
|
||||
if isConst {
|
||||
v := constant.UnaryOp(token.NOT, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
} else {
|
||||
n.rval.SetBool(!v0.Bool())
|
||||
}
|
||||
}
|
||||
|
||||
func posConst(n *node) {
|
||||
v0 := n.child[0].rval
|
||||
isConst := v0.IsValid() && isConstantValue(v0.Type())
|
||||
t := n.typ.rtype
|
||||
v := n.child[0].rval
|
||||
if isConst {
|
||||
t = constVal
|
||||
}
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n.rval.SetInt(+v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n.rval.SetUint(+v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
n.rval.SetFloat(+v.Float())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
n.rval.SetComplex(+v.Complex())
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.UnaryOp(token.ADD, vConstantValue(v0), 0)
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isInt(t):
|
||||
n.rval.SetInt(+v0.Int())
|
||||
case isUint(t):
|
||||
n.rval.SetUint(+v0.Uint())
|
||||
case isFloat(t):
|
||||
n.rval.SetFloat(+v0.Float())
|
||||
case isComplex(t):
|
||||
n.rval.SetComplex(+v0.Complex())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1920,6 +2033,80 @@ func equal(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -3505,6 +3692,80 @@ func notEqual(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
|
||||
620
interp/run.go
620
interp/run.go
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,10 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A sKind represents the kind of symbol
|
||||
// A sKind represents the kind of symbol.
|
||||
type sKind uint
|
||||
|
||||
// Symbol kinds for the Go interpreter
|
||||
// Symbol kinds for the Go interpreter.
|
||||
const (
|
||||
undefSym sKind = iota
|
||||
binSym // Binary from runtime
|
||||
@@ -86,7 +86,7 @@ type scope struct {
|
||||
iota int // iota value in this scope
|
||||
}
|
||||
|
||||
// push creates a new scope and chain it to the current one
|
||||
// push creates a new scope and chain it to the current one.
|
||||
func (s *scope) push(indirect bool) *scope {
|
||||
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
|
||||
if indirect {
|
||||
@@ -117,13 +117,16 @@ func (s *scope) pop() *scope {
|
||||
|
||||
// lookup searches for a symbol in the current scope, and upper ones if not found
|
||||
// it returns the symbol, the number of indirections level from the current scope
|
||||
// and status (false if no result)
|
||||
// and status (false if no result).
|
||||
func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
level := s.level
|
||||
for s != nil {
|
||||
for {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
return sym, level - s.level, true
|
||||
}
|
||||
if s.anc == nil {
|
||||
break
|
||||
}
|
||||
s = s.anc
|
||||
}
|
||||
return nil, 0, false
|
||||
@@ -131,16 +134,43 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
|
||||
func (s *scope) rangeChanType(n *node) *itype {
|
||||
if sym, _, found := s.lookup(n.child[1].ident); found {
|
||||
if t := sym.typ; len(n.child) == 3 && t != nil && t.cat == chanT {
|
||||
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
if c := n.child[1]; c.typ != nil && c.typ.cat == chanT {
|
||||
return c.typ
|
||||
|
||||
c := n.child[1]
|
||||
if c.typ == nil {
|
||||
return nil
|
||||
}
|
||||
switch {
|
||||
case c.typ.cat == chanT, c.typ.cat == chanRecvT:
|
||||
return c.typ
|
||||
case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
|
||||
return &itype{cat: chanT, val: &itype{cat: valueT, rtype: c.typ.rtype.Elem()}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixType returns the input type, or a valid default type for untyped constant.
|
||||
func (s *scope) fixType(t *itype) *itype {
|
||||
if !t.untyped || t.cat != valueT {
|
||||
return t
|
||||
}
|
||||
switch typ := t.TypeOf(); typ.Kind() {
|
||||
case reflect.Int64:
|
||||
return s.getType("int")
|
||||
case reflect.Uint64:
|
||||
return s.getType("uint")
|
||||
case reflect.Float64:
|
||||
return s.getType("float64")
|
||||
case reflect.Complex128:
|
||||
return s.getType("complex128")
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (s *scope) getType(ident string) *itype {
|
||||
var t *itype
|
||||
if sym, _, found := s.lookup(ident); found {
|
||||
@@ -151,7 +181,7 @@ func (s *scope) getType(ident string) *itype {
|
||||
return t
|
||||
}
|
||||
|
||||
// add adds a type to the scope types array, and returns its index
|
||||
// add adds a type to the scope types array, and returns its index.
|
||||
func (s *scope) add(typ *itype) (index int) {
|
||||
if typ == nil {
|
||||
log.Panic("nil type")
|
||||
|
||||
102
interp/src.go
102
interp/src.go
@@ -22,12 +22,18 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
// In all other cases, absolute import paths are resolved from the GOPATH
|
||||
// and the nested "vendor" directories.
|
||||
if isPathRelative(path) {
|
||||
if rPath == "main" {
|
||||
if rPath == mainID {
|
||||
rPath = "."
|
||||
}
|
||||
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
|
||||
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
root, err := interp.rootFromSourceLocation(rPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if dir, rPath, err = pkgDir(interp.context.GOPATH, root, path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if interp.rdir[path] {
|
||||
@@ -126,6 +132,13 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
interp.run(n, nil)
|
||||
}
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars(rootNodes, interp.scopes[path])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
// Add main to list of functions to run, after all inits
|
||||
if m := interp.main(); m != nil {
|
||||
initNodes = append(initNodes, m)
|
||||
@@ -138,6 +151,23 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
return pkgName, nil
|
||||
}
|
||||
|
||||
func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
|
||||
sourceFile := interp.Name
|
||||
if rPath != mainID || !strings.HasSuffix(sourceFile, ".go") {
|
||||
return rPath, nil
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pkgDir := filepath.Join(wd, filepath.Dir(sourceFile))
|
||||
root := strings.TrimPrefix(pkgDir, filepath.Join(interp.context.GOPATH, "src")+"/")
|
||||
if root == wd {
|
||||
return "", fmt.Errorf("package location %s not in GOPATH", pkgDir)
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
// pkgDir returns the absolute path in filesystem for a package given its name and
|
||||
// the root of the subtree dependencies.
|
||||
func pkgDir(goPath string, root, path string) (string, string, error) {
|
||||
@@ -158,13 +188,62 @@ func pkgDir(goPath string, root, path string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("unable to find source related to: %q", path)
|
||||
}
|
||||
|
||||
return pkgDir(goPath, previousRoot(root), path)
|
||||
rootPath := filepath.Join(goPath, "src", root)
|
||||
prevRoot, err := previousRoot(rootPath, root)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return pkgDir(goPath, prevRoot, path)
|
||||
}
|
||||
|
||||
// Find the previous source root. (vendor > vendor > ... > GOPATH)
|
||||
func previousRoot(root string) string {
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
const vendor = "vendor"
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(rootPath, root string) (string, error) {
|
||||
rootPath = filepath.Clean(rootPath)
|
||||
parent, final := filepath.Split(rootPath)
|
||||
parent = filepath.Clean(parent)
|
||||
|
||||
// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
|
||||
if root != mainID && final != vendor {
|
||||
root = strings.TrimSuffix(root, string(filepath.Separator))
|
||||
prefix := strings.TrimSuffix(rootPath, root)
|
||||
|
||||
// look for the closest vendor in one of our direct ancestors, as it takes priority.
|
||||
var vendored string
|
||||
for {
|
||||
fi, err := os.Lstat(filepath.Join(parent, vendor))
|
||||
if err == nil && fi.IsDir() {
|
||||
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
|
||||
break
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// stop when we reach GOPATH/src/blah
|
||||
parent = filepath.Dir(parent)
|
||||
if parent == prefix {
|
||||
break
|
||||
}
|
||||
|
||||
// just an additional failsafe, stop if we reach the filesystem root.
|
||||
// TODO(mpl): It should probably be a critical error actually,
|
||||
// as we shouldn't have gone that high up in the tree.
|
||||
if parent == string(filepath.Separator) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vendored != "" {
|
||||
return vendored, nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mpl): the algorithm below might be redundant with the one above,
|
||||
// but keeping it for now. Investigate/simplify/remove later.
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
var index int
|
||||
for i := len(splitRoot) - 1; i >= 0; i-- {
|
||||
if splitRoot[i] == "vendor" {
|
||||
@@ -174,10 +253,10 @@ func previousRoot(root string) string {
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return filepath.Join(splitRoot[:index]...)
|
||||
return filepath.Join(splitRoot[:index]...), nil
|
||||
}
|
||||
|
||||
func effectivePkg(root, path string) string {
|
||||
@@ -191,7 +270,8 @@ func effectivePkg(root, path string) string {
|
||||
for i := 0; i < len(splitPath); i++ {
|
||||
part := splitPath[len(splitPath)-1-i]
|
||||
|
||||
if part == splitRoot[len(splitRoot)-1-rootIndex] && i != 0 {
|
||||
index := len(splitRoot) - 1 - rootIndex
|
||||
if index > 0 && part == splitRoot[index] && i != 0 {
|
||||
prevRootIndex = rootIndex
|
||||
rootIndex++
|
||||
} else if prevRootIndex == rootIndex {
|
||||
@@ -207,7 +287,7 @@ func effectivePkg(root, path string) string {
|
||||
return filepath.Join(root, frag)
|
||||
}
|
||||
|
||||
// isPathRelative returns true if path starts with "./" or "../"
|
||||
// isPathRelative returns true if path starts with "./" or "../".
|
||||
func isPathRelative(s string) bool {
|
||||
p := "." + string(filepath.Separator)
|
||||
return strings.HasPrefix(s, p) || strings.HasPrefix(s, "."+p)
|
||||
|
||||
@@ -26,6 +26,12 @@ func Test_effectivePkg(t *testing.T) {
|
||||
path: "vendor/guthib.com/containous/vin",
|
||||
expected: "github.com/foo/plugin/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/vin",
|
||||
},
|
||||
{
|
||||
desc: "path is non-existent",
|
||||
root: "foo",
|
||||
path: "githib.com/foo/app",
|
||||
expected: "foo/githib.com/foo/app",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -190,9 +196,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
|
||||
func Test_previousRoot(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
root string
|
||||
expected string
|
||||
desc string
|
||||
root string
|
||||
rootPathSuffix string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "GOPATH",
|
||||
@@ -209,6 +216,18 @@ func Test_previousRoot(t *testing.T) {
|
||||
root: "github.com/foo/pkg/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/fuu",
|
||||
expected: "github.com/foo/pkg/vendor/guthib.com/containous/fromage",
|
||||
},
|
||||
{
|
||||
desc: "vendor is sibling",
|
||||
root: "github.com/foo/bar",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
{
|
||||
desc: "vendor is uncle",
|
||||
root: "github.com/foo/bar/baz",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar/baz",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -216,7 +235,20 @@ func Test_previousRoot(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := previousRoot(test.root)
|
||||
var rootPath string
|
||||
if test.rootPathSuffix != "" {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootPath = filepath.Join(wd, test.rootPathSuffix)
|
||||
} else {
|
||||
rootPath = vendor
|
||||
}
|
||||
p, err := previousRoot(rootPath, test.root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if p != test.expected {
|
||||
t.Errorf("got: %s, want: %s", p, test.expected)
|
||||
|
||||
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package baz
|
||||
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package whatever
|
||||
400
interp/type.go
400
interp/type.go
@@ -1,14 +1,17 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// tcat defines interpreter type categories
|
||||
// tcat defines interpreter type categories.
|
||||
type tcat uint
|
||||
|
||||
// Types for go language
|
||||
// Types for go language.
|
||||
const (
|
||||
nilT tcat = iota
|
||||
aliasT
|
||||
@@ -18,6 +21,8 @@ const (
|
||||
boolT
|
||||
builtinT
|
||||
chanT
|
||||
chanSendT
|
||||
chanRecvT
|
||||
complex64T
|
||||
complex128T
|
||||
errorT
|
||||
@@ -89,7 +94,7 @@ func (c tcat) String() string {
|
||||
return "Cat(" + strconv.Itoa(int(c)) + ")"
|
||||
}
|
||||
|
||||
// structField type defines a field in a struct
|
||||
// structField type defines a field in a struct.
|
||||
type structField struct {
|
||||
name string
|
||||
tag string
|
||||
@@ -97,12 +102,12 @@ type structField struct {
|
||||
typ *itype
|
||||
}
|
||||
|
||||
// itype defines the internal representation of types in the interpreter
|
||||
// itype defines the internal representation of types in the interpreter.
|
||||
type itype struct {
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
val *itype // Type of value element if chanT,chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
method []*node // Associated methods or nil
|
||||
@@ -119,7 +124,7 @@ type itype struct {
|
||||
scope *scope // type declaration scope (in case of re-parse incomplete type)
|
||||
}
|
||||
|
||||
// nodeType returns a type definition for the corresponding AST subtree
|
||||
// nodeType returns a type definition for the corresponding AST subtree.
|
||||
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if n.typ != nil && !n.typ.incomplete {
|
||||
if n.kind == sliceExpr {
|
||||
@@ -152,10 +157,16 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case arrayType:
|
||||
t.cat = arrayT
|
||||
if len(n.child) > 1 {
|
||||
v := n.child[0].rval
|
||||
switch {
|
||||
case n.child[0].rval.IsValid():
|
||||
case v.IsValid():
|
||||
// constant size
|
||||
t.size = int(n.child[0].rval.Int())
|
||||
if isConstantValue(v.Type()) {
|
||||
c := v.Interface().(constant.Value)
|
||||
t.size = constToInt(c)
|
||||
} else {
|
||||
t.size = int(v.Int())
|
||||
}
|
||||
case n.child[0].kind == ellipsisExpr:
|
||||
// [...]T expression
|
||||
t.size = arrayTypeLen(n.anc)
|
||||
@@ -165,6 +176,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if sym.typ != nil && sym.typ.cat == intT {
|
||||
if v, ok := sym.rval.Interface().(int); ok {
|
||||
t.size = v
|
||||
} else if c, ok := sym.rval.Interface().(constant.Value); ok {
|
||||
t.size = constToInt(c)
|
||||
} else {
|
||||
t.incomplete = true
|
||||
}
|
||||
@@ -231,6 +244,23 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t.cat = stringT
|
||||
t.name = "string"
|
||||
t.untyped = true
|
||||
case constant.Value:
|
||||
switch v.Kind() {
|
||||
case constant.Int:
|
||||
t.cat = intT
|
||||
t.name = "int"
|
||||
t.untyped = true
|
||||
case constant.Float:
|
||||
t.cat = float64T
|
||||
t.name = "float64"
|
||||
t.untyped = true
|
||||
case constant.Complex:
|
||||
t.cat = complex128T
|
||||
t.name = "complex128"
|
||||
t.untyped = true
|
||||
default:
|
||||
err = n.cfgErrorf("missing support for type %v", n.rval)
|
||||
}
|
||||
default:
|
||||
err = n.cfgErrorf("missing support for type %T: %v", v, n.rval)
|
||||
}
|
||||
@@ -355,6 +385,20 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
t.incomplete = t.val.incomplete
|
||||
|
||||
case chanTypeRecv:
|
||||
t.cat = chanRecvT
|
||||
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.incomplete = t.val.incomplete
|
||||
|
||||
case chanTypeSend:
|
||||
t.cat = chanSendT
|
||||
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.incomplete = t.val.incomplete
|
||||
|
||||
case ellipsisExpr:
|
||||
t.cat = variadicT
|
||||
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
@@ -399,22 +443,30 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
|
||||
case identExpr:
|
||||
if sym, _, found := sc.lookup(n.ident); found {
|
||||
t = sym.typ
|
||||
if t.incomplete && t.node != n {
|
||||
m := t.method
|
||||
if t, err = nodeType(interp, sc, t.node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.method = m
|
||||
sym.typ = t
|
||||
sym, _, found := sc.lookup(n.ident)
|
||||
if !found {
|
||||
// retry with the filename, in case ident is a package name.
|
||||
// TODO(mpl): try to move that into lookup instead?
|
||||
baseName := filepath.Base(interp.fset.Position(n.pos).Filename)
|
||||
ident := filepath.Join(n.ident, baseName)
|
||||
sym, _, found = sc.lookup(ident)
|
||||
if !found {
|
||||
t.incomplete = true
|
||||
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t}
|
||||
break
|
||||
}
|
||||
if t.node == nil {
|
||||
t.node = n
|
||||
}
|
||||
t = sym.typ
|
||||
if t.incomplete && t.node != n {
|
||||
m := t.method
|
||||
if t, err = nodeType(interp, sc, t.node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
t.incomplete = true
|
||||
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t}
|
||||
t.method = m
|
||||
sym.typ = t
|
||||
}
|
||||
if t.node == nil {
|
||||
t.node = n
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
@@ -477,9 +529,31 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case selectorExpr:
|
||||
// Resolve the left part of selector, then lookup the right part on it
|
||||
var lt *itype
|
||||
if lt, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
|
||||
// If we are in a list of func parameters, and we are a selector on a binPkgT, but
|
||||
// one of the other parameters has the same name as the pkg name, in the list of
|
||||
// symbols we would find the other parameter instead of the pkg because it comes
|
||||
// first when looking up in the stack of scopes. So in that case we force the
|
||||
// lookup directly in the root scope to shortcircuit that issue.
|
||||
var localScope *scope
|
||||
localScope = sc
|
||||
if n.anc != nil && len(n.anc.child) > 1 && n.anc.child[1] == n &&
|
||||
// This check is weaker than what we actually want to know, i.e. whether
|
||||
// n.anc.child[0] is a variable, but it seems at this point in the run we have no
|
||||
// way of knowing that yet (typ is nil, so there's no typ.cat yet).
|
||||
n.anc.child[0].kind == identExpr {
|
||||
for {
|
||||
if localScope.level == 0 {
|
||||
break
|
||||
}
|
||||
localScope = localScope.anc
|
||||
}
|
||||
}
|
||||
|
||||
if lt, err = nodeType(interp, localScope, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lt.incomplete {
|
||||
t.incomplete = true
|
||||
break
|
||||
@@ -594,7 +668,7 @@ func (interp *Interpreter) isBuiltinCall(n *node) bool {
|
||||
return s != nil && s.kind == bltnSym
|
||||
}
|
||||
|
||||
// struct name returns the name of a struct type
|
||||
// struct name returns the name of a struct type.
|
||||
func typeName(n *node) string {
|
||||
if n.anc.kind == typeSpec {
|
||||
return n.anc.child[0].ident
|
||||
@@ -602,7 +676,7 @@ func typeName(n *node) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// fieldName returns an implicit struct field name according to node kind
|
||||
// fieldName returns an implicit struct field name according to node kind.
|
||||
func fieldName(n *node) string {
|
||||
switch n.kind {
|
||||
case selectorExpr:
|
||||
@@ -617,6 +691,7 @@ func fieldName(n *node) string {
|
||||
}
|
||||
|
||||
var zeroValues [maxT]reflect.Value
|
||||
var okFor [aMax][maxT]bool
|
||||
|
||||
func init() {
|
||||
zeroValues[boolT] = reflect.ValueOf(false)
|
||||
@@ -637,6 +712,77 @@ func init() {
|
||||
zeroValues[uint32T] = reflect.ValueOf(uint32(0))
|
||||
zeroValues[uint64T] = reflect.ValueOf(uint64(0))
|
||||
zeroValues[uintptrT] = reflect.ValueOf(uintptr(0))
|
||||
|
||||
// Calculate the action -> type allowances
|
||||
var (
|
||||
okForEq [maxT]bool
|
||||
okForCmp [maxT]bool
|
||||
okForAdd [maxT]bool
|
||||
okForAnd [maxT]bool
|
||||
okForBool [maxT]bool
|
||||
okForArith [maxT]bool
|
||||
)
|
||||
for cat := tcat(0); cat < maxT; cat++ {
|
||||
if (cat >= intT && cat <= int64T) || (cat >= uintT && cat <= uintptrT) {
|
||||
okForEq[cat] = true
|
||||
okForCmp[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForAnd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
if cat == float32T || cat == float64T {
|
||||
okForEq[cat] = true
|
||||
okForCmp[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
if cat == complex64T || cat == complex128T {
|
||||
okForEq[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
}
|
||||
|
||||
okForAdd[stringT] = true
|
||||
|
||||
okForBool[boolT] = true
|
||||
|
||||
okForEq[nilT] = true
|
||||
okForEq[ptrT] = true
|
||||
okForEq[interfaceT] = true
|
||||
okForEq[errorT] = true
|
||||
okForEq[chanT] = true
|
||||
okForEq[stringT] = true
|
||||
okForEq[boolT] = true
|
||||
okForEq[mapT] = true // nil only
|
||||
okForEq[funcT] = true // nil only
|
||||
okForEq[arrayT] = true // array: only if element type is comparable slice: nil only
|
||||
okForEq[structT] = true // only if all struct fields are comparable
|
||||
|
||||
okForCmp[stringT] = true
|
||||
|
||||
okFor[aAdd] = okForAdd
|
||||
okFor[aAnd] = okForAnd
|
||||
okFor[aLand] = okForBool
|
||||
okFor[aAndNot] = okForAnd
|
||||
okFor[aQuo] = okForArith
|
||||
okFor[aEqual] = okForEq
|
||||
okFor[aGreaterEqual] = okForCmp
|
||||
okFor[aGreater] = okForCmp
|
||||
okFor[aLowerEqual] = okForCmp
|
||||
okFor[aLower] = okForCmp
|
||||
okFor[aRem] = okForAnd
|
||||
okFor[aMul] = okForArith
|
||||
okFor[aNotEqual] = okForEq
|
||||
okFor[aOr] = okForAnd
|
||||
okFor[aLor] = okForBool
|
||||
okFor[aSub] = okForArith
|
||||
okFor[aXor] = okForAnd
|
||||
okFor[aShl] = okForAnd
|
||||
okFor[aShr] = okForAnd
|
||||
okFor[aNeg] = okForArith
|
||||
okFor[aNot] = okForBool
|
||||
okFor[aPos] = okForArith
|
||||
}
|
||||
|
||||
// Finalize returns a type pointer and error. It reparses a type from the
|
||||
@@ -680,7 +826,7 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool {
|
||||
}
|
||||
seen[t] = true
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, chanT, ptrT:
|
||||
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
|
||||
return t.val.referTo(name, seen)
|
||||
case funcT:
|
||||
for _, a := range t.arg {
|
||||
@@ -756,7 +902,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
visited[name] = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, chanT, ptrT:
|
||||
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
|
||||
return isComplete(t.val, visited)
|
||||
case funcT:
|
||||
complete := true
|
||||
@@ -781,6 +927,12 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// comparable returns true if the type is comparable.
|
||||
func (t *itype) comparable() bool {
|
||||
typ := t.TypeOf()
|
||||
return t.cat == nilT || typ != nil && typ.Comparable()
|
||||
}
|
||||
|
||||
// Equals returns true if the given type is identical to the receiver one.
|
||||
func (t *itype) equals(o *itype) bool {
|
||||
switch ti, oi := isInterface(t), isInterface(o); {
|
||||
@@ -852,7 +1004,7 @@ func (t *itype) methods() methodSet {
|
||||
return res
|
||||
}
|
||||
|
||||
// id returns a unique type identificator string
|
||||
// id returns a unique type identificator string.
|
||||
func (t *itype) id() (res string) {
|
||||
if t.name != "" {
|
||||
return t.path + "." + t.name
|
||||
@@ -861,7 +1013,11 @@ func (t *itype) id() (res string) {
|
||||
case arrayT:
|
||||
res = "[" + strconv.Itoa(t.size) + "]" + t.val.id()
|
||||
case chanT:
|
||||
res = "<-" + t.val.id()
|
||||
res = "chan " + t.val.id()
|
||||
case chanSendT:
|
||||
res = "chan<- " + t.val.id()
|
||||
case chanRecvT:
|
||||
res = "<-chan " + t.val.id()
|
||||
case funcT:
|
||||
res = "func("
|
||||
for _, t := range t.arg {
|
||||
@@ -894,7 +1050,7 @@ func (t *itype) id() (res string) {
|
||||
return res
|
||||
}
|
||||
|
||||
// zero instantiates and return a zero value object for the given type during execution
|
||||
// zero instantiates and return a zero value object for the given type during execution.
|
||||
func (t *itype) zero() (v reflect.Value, err error) {
|
||||
if t, err = t.finalize(); err != nil {
|
||||
return v, err
|
||||
@@ -915,7 +1071,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
return v, err
|
||||
}
|
||||
|
||||
// fieldIndex returns the field index from name in a struct, or -1 if not found
|
||||
// fieldIndex returns the field index from name in a struct, or -1 if not found.
|
||||
func (t *itype) fieldIndex(name string) int {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
@@ -929,7 +1085,7 @@ func (t *itype) fieldIndex(name string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// fieldSeq returns the field type from the list of field indexes
|
||||
// fieldSeq returns the field type from the list of field indexes.
|
||||
func (t *itype) fieldSeq(seq []int) *itype {
|
||||
ft := t
|
||||
for _, i := range seq {
|
||||
@@ -941,7 +1097,7 @@ func (t *itype) fieldSeq(seq []int) *itype {
|
||||
return ft
|
||||
}
|
||||
|
||||
// lookupField returns a list of indices, i.e. a path to access a field in a struct object
|
||||
// lookupField returns a list of indices, i.e. a path to access a field in a struct object.
|
||||
func (t *itype) lookupField(name string) []int {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
@@ -963,7 +1119,7 @@ func (t *itype) lookupField(name string) []int {
|
||||
return nil
|
||||
}
|
||||
|
||||
// lookupBinField returns a structfield and a path to access an embedded binary field in a struct object
|
||||
// lookupBinField returns a structfield and a path to access an embedded binary field in a struct object.
|
||||
func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int, ok bool) {
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinField(name)
|
||||
@@ -1063,8 +1219,9 @@ func exportName(s string) string {
|
||||
}
|
||||
|
||||
var interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
var constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
|
||||
// RefType returns a reflect.Type representation from an interpereter type.
|
||||
// RefType returns a reflect.Type representation from an interpreter type.
|
||||
// In simple cases, reflect types are directly mapped from the interpreter
|
||||
// counterpart.
|
||||
// For recursive named struct or interfaces, as reflect does not permit to
|
||||
@@ -1088,6 +1245,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
// a node can still point to a previous copy.
|
||||
st.typ.recursive = st.typ.recursive || st.typ.isRecursive()
|
||||
recursive = st.typ.isRecursive()
|
||||
// It is possible that t.recursive is not inline with st.typ.recursive
|
||||
// which will break recursion detection. Set it here to make sure it
|
||||
// is correct.
|
||||
t.recursive = recursive
|
||||
}
|
||||
}
|
||||
if wrapRecursive && t.recursive {
|
||||
@@ -1099,9 +1260,11 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
if defined[name] != nil && defined[name].rtype != nil {
|
||||
return defined[name].rtype
|
||||
}
|
||||
if t.val != nil && defined[t.val.path+"/"+t.val.name] != nil && t.val.rtype == nil {
|
||||
if t.val != nil && t.val.cat == structT && t.val.rtype == nil && hasRecursiveStruct(t.val, copyDefined(defined)) {
|
||||
// Replace reference to self (direct or indirect) by an interface{} to handle
|
||||
// recursive types with reflect.
|
||||
typ := *t.val
|
||||
t.val = &typ
|
||||
t.val.rtype = interf
|
||||
recursive = true
|
||||
}
|
||||
@@ -1116,21 +1279,27 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
}
|
||||
case chanT:
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
|
||||
case chanRecvT:
|
||||
t.rtype = reflect.ChanOf(reflect.RecvDir, t.val.refType(defined, wrapRecursive))
|
||||
case chanSendT:
|
||||
t.rtype = reflect.ChanOf(reflect.SendDir, t.val.refType(defined, wrapRecursive))
|
||||
case errorT:
|
||||
t.rtype = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
if t.name != "" {
|
||||
defined[name] = t
|
||||
}
|
||||
variadic := false
|
||||
in := make([]reflect.Type, len(t.arg))
|
||||
out := make([]reflect.Type, len(t.ret))
|
||||
for i, v := range t.arg {
|
||||
in[i] = v.refType(defined, true)
|
||||
variadic = v.cat == variadicT
|
||||
}
|
||||
for i, v := range t.ret {
|
||||
out[i] = v.refType(defined, true)
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, false)
|
||||
t.rtype = reflect.FuncOf(in, out, variadic)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
case mapT:
|
||||
@@ -1198,6 +1367,134 @@ func (t *itype) implements(it *itype) bool {
|
||||
return t.methods().contains(it.methods())
|
||||
}
|
||||
|
||||
func copyDefined(m map[string]*itype) map[string]*itype {
|
||||
n := make(map[string]*itype, len(m))
|
||||
for k, v := range m {
|
||||
n[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hasRecursiveStruct determines if a struct is a recursion or a recursion
|
||||
// intermediate. A recursion intermediate is a struct that contains a recursive
|
||||
// struct.
|
||||
func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
|
||||
if len(defined) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := t
|
||||
for typ != nil {
|
||||
if typ.cat != structT {
|
||||
typ = typ.val
|
||||
continue
|
||||
}
|
||||
|
||||
if defined[typ.path+"/"+typ.name] != nil {
|
||||
return true
|
||||
}
|
||||
defined[typ.path+"/"+typ.name] = typ
|
||||
|
||||
for _, f := range typ.field {
|
||||
if hasRecursiveStruct(f.typ, defined) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
func catOf(t reflect.Type) tcat {
|
||||
if t == nil {
|
||||
return nilT
|
||||
}
|
||||
if t == errType {
|
||||
return errorT
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return boolT
|
||||
case reflect.Int:
|
||||
return intT
|
||||
case reflect.Int8:
|
||||
return int8T
|
||||
case reflect.Int16:
|
||||
return int16T
|
||||
case reflect.Int32:
|
||||
return int32T
|
||||
case reflect.Int64:
|
||||
return int64T
|
||||
case reflect.Uint:
|
||||
return uintT
|
||||
case reflect.Uint8:
|
||||
return uint8T
|
||||
case reflect.Uint16:
|
||||
return uint16T
|
||||
case reflect.Uint32:
|
||||
return uint32T
|
||||
case reflect.Uint64:
|
||||
return uint64T
|
||||
case reflect.Uintptr:
|
||||
return uintptrT
|
||||
case reflect.Float32:
|
||||
return float32T
|
||||
case reflect.Float64:
|
||||
return float64T
|
||||
case reflect.Complex64:
|
||||
return complex64T
|
||||
case reflect.Complex128:
|
||||
return complex128T
|
||||
case reflect.Array, reflect.Slice:
|
||||
return arrayT
|
||||
case reflect.Chan:
|
||||
return chanT
|
||||
case reflect.Func:
|
||||
return funcT
|
||||
case reflect.Interface:
|
||||
return interfaceT
|
||||
case reflect.Map:
|
||||
return mapT
|
||||
case reflect.Ptr:
|
||||
return ptrT
|
||||
case reflect.String:
|
||||
return stringT
|
||||
case reflect.Struct:
|
||||
return structT
|
||||
case reflect.UnsafePointer:
|
||||
return uintptrT
|
||||
}
|
||||
return nilT
|
||||
}
|
||||
|
||||
func catOfConst(v reflect.Value) tcat {
|
||||
c, ok := v.Interface().(constant.Value)
|
||||
if !ok {
|
||||
return nilT
|
||||
}
|
||||
|
||||
switch c.Kind() {
|
||||
case constant.Int:
|
||||
return intT
|
||||
case constant.Float:
|
||||
return float64T
|
||||
case constant.Complex:
|
||||
return complex128T
|
||||
default:
|
||||
return nilT
|
||||
}
|
||||
}
|
||||
|
||||
func constToInt(c constant.Value) int {
|
||||
if constant.BitLen(c) > 64 {
|
||||
panic(fmt.Sprintf("constant %s overflows int64", c.ExactString()))
|
||||
}
|
||||
i, _ := constant.Int64Val(c)
|
||||
return int(i)
|
||||
}
|
||||
|
||||
func defRecvType(n *node) *itype {
|
||||
if n.kind != funcDecl || len(n.child[0].child) == 0 {
|
||||
return nil
|
||||
@@ -1216,12 +1513,20 @@ func isShiftNode(n *node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isComparisonNode(n *node) bool {
|
||||
switch n.action {
|
||||
case aEqual, aNotEqual, aGreater, aGreaterEqual, aLower, aLowerEqual:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
return chanElement(t.val)
|
||||
case chanT:
|
||||
case chanT, chanSendT, chanRecvT:
|
||||
return t.val
|
||||
case valueT:
|
||||
return &itype{cat: valueT, rtype: t.rtype.Elem(), node: t.node, scope: t.scope}
|
||||
@@ -1231,14 +1536,22 @@ func chanElement(t *itype) *itype {
|
||||
|
||||
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
|
||||
func isChan(t *itype) bool { return t.TypeOf().Kind() == reflect.Chan }
|
||||
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
|
||||
func isSendChan(t *itype) bool {
|
||||
rt := t.TypeOf()
|
||||
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
|
||||
}
|
||||
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
|
||||
func isArray(t *itype) bool {
|
||||
k := t.TypeOf().Kind()
|
||||
return k == reflect.Array || k == reflect.Slice
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool {
|
||||
return isInterfaceSrc(t) || t.TypeOf().Kind() == reflect.Interface
|
||||
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isStruct(t *itype) bool {
|
||||
@@ -1311,5 +1624,8 @@ func isByteArray(t reflect.Type) bool {
|
||||
|
||||
func isFloat32(t reflect.Type) bool { return t != nil && t.Kind() == reflect.Float32 }
|
||||
func isFloat64(t reflect.Type) bool { return t != nil && t.Kind() == reflect.Float64 }
|
||||
func isNumber(t reflect.Type) bool { return isInt(t) || isFloat(t) || isComplex(t) }
|
||||
func isString(t reflect.Type) bool { return t != nil && t.Kind() == reflect.String }
|
||||
func isNumber(t reflect.Type) bool {
|
||||
return isInt(t) || isFloat(t) || isComplex(t) || isConstantValue(t)
|
||||
}
|
||||
func isString(t reflect.Type) bool { return t != nil && t.Kind() == reflect.String }
|
||||
func isConstantValue(t reflect.Type) bool { return t != nil && t.Implements(constVal) }
|
||||
|
||||
109
interp/value.go
109
interp/value.go
@@ -1,6 +1,7 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
@@ -96,6 +97,7 @@ func genValueAs(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
func genValue(n *node) func(*frame) reflect.Value {
|
||||
switch n.kind {
|
||||
case basicLit:
|
||||
convertConstantValue(n)
|
||||
v := n.rval
|
||||
if !v.IsValid() {
|
||||
v = reflect.New(interf).Elem()
|
||||
@@ -111,6 +113,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
default:
|
||||
if n.rval.IsValid() {
|
||||
convertConstantValue(n)
|
||||
v := n.rval
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
}
|
||||
@@ -179,16 +182,6 @@ func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := reflect.New(interf).Elem()
|
||||
v.Set(value(f))
|
||||
return v.Addr()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -208,18 +201,6 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueDerefInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
|
||||
v := reflect.New(interf).Elem()
|
||||
@@ -266,6 +247,63 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterface(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
vv := value(f)
|
||||
v := reflect.New(t).Elem()
|
||||
toRecursive(v, vv)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func toRecursive(dest, src reflect.Value) {
|
||||
if !src.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch dest.Kind() {
|
||||
case reflect.Map:
|
||||
v := reflect.MakeMapWithSize(dest.Type(), src.Len())
|
||||
for _, kv := range src.MapKeys() {
|
||||
vv := reflect.New(dest.Type().Elem()).Elem()
|
||||
toRecursive(vv, src.MapIndex(kv))
|
||||
vv.SetMapIndex(kv, vv)
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Slice:
|
||||
l := src.Len()
|
||||
v := reflect.MakeSlice(dest.Type(), l, l)
|
||||
for i := 0; i < l; i++ {
|
||||
toRecursive(v.Index(i), src.Index(i))
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(dest.Type().Elem()).Elem()
|
||||
s := src
|
||||
if s.Elem().Kind() != reflect.Struct { // In the case of *interface{}, we want *struct{}
|
||||
s = s.Elem()
|
||||
}
|
||||
toRecursive(v, s)
|
||||
dest.Set(v.Addr())
|
||||
default:
|
||||
dest.Set(src)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterfacePtrValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
@@ -277,6 +315,10 @@ func vInt(v reflect.Value) (i int64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = int64(real(v.Complex()))
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -291,6 +333,11 @@ func vUint(v reflect.Value) (i uint64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = uint64(real(v.Complex()))
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
iv, _ := constant.Int64Val(constant.ToInt(c))
|
||||
i = uint64(iv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -305,6 +352,13 @@ func vComplex(v reflect.Value) (c complex128) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
c = v.Complex()
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
con := v.Interface().(constant.Value)
|
||||
con = constant.ToComplex(con)
|
||||
rel, _ := constant.Float64Val(constant.Real(con))
|
||||
img, _ := constant.Float64Val(constant.Imag(con))
|
||||
c = complex(rel, img)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -319,6 +373,17 @@ func vFloat(v reflect.Value) (i float64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = real(v.Complex())
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
i, _ = constant.Float64Val(constant.ToFloat(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func vConstantValue(v reflect.Value) (c constant.Value) {
|
||||
if v.Type().Implements(constVal) {
|
||||
c = v.Interface().(constant.Value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user