Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6486909921 | ||
|
|
d252821df3 | ||
|
|
2bef03e253 | ||
|
|
1fe91be882 | ||
|
|
5cbbf9339c | ||
|
|
8365f687e7 | ||
|
|
36836cd4f2 | ||
|
|
82b499a1c8 | ||
|
|
0ef7f8fb80 | ||
|
|
c1f5005b2a | ||
|
|
def57d57c2 | ||
|
|
74479d70e6 | ||
|
|
f1cff308e6 | ||
|
|
6f878052f8 | ||
|
|
eb25c84797 | ||
|
|
2de0c80d38 | ||
|
|
151a856bf2 | ||
|
|
d5217f7db4 | ||
|
|
01e4cdea70 | ||
|
|
3c88542180 | ||
|
|
56d88ef89d | ||
|
|
5d78c8ae27 | ||
|
|
184623d81f | ||
|
|
5d56bac8d0 | ||
|
|
4a068ea452 | ||
|
|
8605c238ef | ||
|
|
4f39eaf893 | ||
|
|
4f8e1de267 | ||
|
|
6f4643ff19 | ||
|
|
bb1be9e6e1 | ||
|
|
cdf4622421 | ||
|
|
b9720d15e1 | ||
|
|
7fab75fbe4 | ||
|
|
7eac6955b3 | ||
|
|
7070367d21 | ||
|
|
22dfc8b10a | ||
|
|
7fba3fe580 | ||
|
|
ff36ec58b1 | ||
|
|
f6c8b8b14f | ||
|
|
e12c8b72c4 | ||
|
|
e4acba031d | ||
|
|
7d56fb067e | ||
|
|
1feece61ce | ||
|
|
01e2e4600e | ||
|
|
92eebbade2 | ||
|
|
a6389aca5e | ||
|
|
de8cb7dc3b | ||
|
|
18b843646c | ||
|
|
71f730b3d7 | ||
|
|
7b2d91bcb5 | ||
|
|
3ed4ec3f6f | ||
|
|
5e142fdedd | ||
|
|
94d44e7265 | ||
|
|
56925e6fea |
@@ -26,6 +26,7 @@ matrix:
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
- CI=1
|
||||
|
||||
go_import_path: github.com/containous/yaegi
|
||||
|
||||
@@ -39,7 +40,7 @@ install:
|
||||
- go mod download
|
||||
|
||||
before_script:
|
||||
- rm -f interp/op.go interp/interp_test.go
|
||||
- rm -f interp/op.go
|
||||
- make generate
|
||||
- git update-index -q --refresh
|
||||
- CHANGED=$(git diff-index --name-only HEAD --)
|
||||
|
||||
9
_test/add0.go
Normal file
9
_test/add0.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{} = 2 + 5
|
||||
println(a.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 7
|
||||
10
_test/add1.go
Normal file
10
_test/add1.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
b := 2
|
||||
var a interface{} = 5 + b
|
||||
println(a.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 7
|
||||
12
_test/add2.go
Normal file
12
_test/add2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type iface interface{}
|
||||
|
||||
func main() {
|
||||
b := 2
|
||||
var a iface = 5 + b
|
||||
println(a.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 7
|
||||
14
_test/append0.go
Normal file
14
_test/append0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a []int, b int) interface{} { return append(a, b) }
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
r := f(a, 3)
|
||||
fmt.Println(r.([]int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
10
_test/assign11.go
Normal file
10
_test/assign11.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
_, _, _ = fmt.Println("test")
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values
|
||||
11
_test/assign12.go
Normal file
11
_test/assign12.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a, b, c := fmt.Println("test")
|
||||
println(a, b, c)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values
|
||||
3
_test/baz-bat/baz-bat.go
Normal file
3
_test/baz-bat/baz-bat.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package baz
|
||||
|
||||
var Name = "baz-bat"
|
||||
18
_test/bin4.go
Normal file
18
_test/bin4.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Bar(s string) bool {
|
||||
a := strings.HasPrefix("fas", "f")
|
||||
b := strings.HasPrefix("aaaaa", "a")
|
||||
a_and_b := strings.HasPrefix("fas", "f") && strings.HasPrefix("aaaaa", "a")
|
||||
fmt.Println(a, b, a && b, a_and_b)
|
||||
return a && b
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(Bar("kung"))
|
||||
}
|
||||
15
_test/bin5.go
Normal file
15
_test/bin5.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr := net.TCPAddr{IP: net.IPv4(1, 1, 1, 1), Port: 80}
|
||||
var s fmt.Stringer = &addr
|
||||
fmt.Println(s.String())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1.1.1.1:80
|
||||
13
_test/cap0.go
Normal file
13
_test/cap0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f(a []int) interface{} {
|
||||
return cap(a)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
println(f(a).(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
9
_test/comp1.go
Normal file
9
_test/comp1.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{} = 1 < 2
|
||||
println(a.(bool))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
13
_test/complex4.go
Normal file
13
_test/complex4.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a, b float64) interface{} { return complex(a, b) }
|
||||
|
||||
func main() {
|
||||
a := f(3, 2)
|
||||
fmt.Println(a.(complex128))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// (3+2i)
|
||||
14
_test/composite10.go
Normal file
14
_test/composite10.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := []map[int]int{make(map[int]int)}
|
||||
|
||||
for _, b := range a {
|
||||
fmt.Println(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[]
|
||||
14
_test/composite9.go
Normal file
14
_test/composite9.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := [][]int{make([]int,0)}
|
||||
|
||||
for _, b := range a {
|
||||
fmt.Println(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// []
|
||||
13
_test/const11.go
Normal file
13
_test/const11.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
const (
|
||||
x = 2 * iota
|
||||
dim
|
||||
)
|
||||
var t [dim * 2]int
|
||||
println(t[0], len(t))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0 4
|
||||
17
_test/const12.go
Normal file
17
_test/const12.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type Kind int
|
||||
|
||||
const (
|
||||
None Kind = 0
|
||||
Left Kind = 1 << iota
|
||||
Right
|
||||
Both Kind = Left | Right
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(None, Left, Right, Both)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0 2 4 6
|
||||
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
|
||||
16
_test/copy2.go
Normal file
16
_test/copy2.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a, b []int) interface{} { return copy(a, b) }
|
||||
|
||||
func main() {
|
||||
a := []int{10, 20, 30}
|
||||
b := [4]int{}
|
||||
c := b[:]
|
||||
r := f(c, a)
|
||||
fmt.Println(r.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
12
_test/fun12.go
Normal file
12
_test/fun12.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func use(interface{}) {}
|
||||
|
||||
func main() {
|
||||
z := map[string]interface{}{"a": 5}
|
||||
use(z)
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
19
_test/fun13.go
Normal file
19
_test/fun13.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T struct{}
|
||||
|
||||
func newT() (T, error) { return T{}, nil }
|
||||
|
||||
func main() {
|
||||
var (
|
||||
i interface{}
|
||||
err error
|
||||
)
|
||||
i, err = newT()
|
||||
fmt.Println(i, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {} <nil>
|
||||
13
_test/fun14.go
Normal file
13
_test/fun14.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f() (bool, int) { return true, 2 }
|
||||
|
||||
func g() (bool, int) { return f() }
|
||||
|
||||
func main() {
|
||||
b, i := g()
|
||||
println(b, i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true 2
|
||||
11
_test/fun15.go
Normal file
11
_test/fun15.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func f1(a int) interface{} { return a + 1 }
|
||||
|
||||
func main() {
|
||||
c := f1(3)
|
||||
println(c.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
13
_test/fun16.go
Normal file
13
_test/fun16.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f1(a int) int { return a + 1 }
|
||||
|
||||
func f2(a int) interface{} { return f1(a) }
|
||||
|
||||
func main() {
|
||||
c := f2(3)
|
||||
println(c.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
13
_test/fun17.go
Normal file
13
_test/fun17.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f1(a int) interface{} { return a + 1 }
|
||||
|
||||
func f2(a int) interface{} { return f1(a) }
|
||||
|
||||
func main() {
|
||||
c := f2(3)
|
||||
println(c.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
12
_test/fun18.go
Normal file
12
_test/fun18.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var m = map[string]int{"foo": 1, "bar": 2}
|
||||
|
||||
func f(s string) interface{} { return m[s] }
|
||||
|
||||
func main() {
|
||||
println(f("foo").(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
17
_test/fun19.go
Normal file
17
_test/fun19.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func foo() ([]string, error) {
|
||||
return nil, fmt.Errorf("bar")
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, b := foo()
|
||||
fmt.Println(a, b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [] bar
|
||||
19
_test/fun20.go
Normal file
19
_test/fun20.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var myerr error = fmt.Errorf("bar")
|
||||
|
||||
func ferr() error { return myerr }
|
||||
|
||||
func foo() ([]string, error) {
|
||||
return nil, ferr()
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, b := foo()
|
||||
fmt.Println(a, b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [] bar
|
||||
12
_test/fun21.go
Normal file
12
_test/fun21.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func Bar() string {
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 4:2: not enough arguments to return
|
||||
10
_test/fun22.go
Normal file
10
_test/fun22.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
time.Date()
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 6:2: not enough arguments in call to time.Date
|
||||
14
_test/imag0.go
Normal file
14
_test/imag0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(c complex128) interface{} { return imag(c) }
|
||||
|
||||
func main() {
|
||||
c := complex(3, 2)
|
||||
a := f(c)
|
||||
fmt.Println(a.(float64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
10
_test/import9.go
Normal file
10
_test/import9.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/containous/yaegi/_test/baz-bat"
|
||||
|
||||
func main() {
|
||||
println(baz.Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// baz-bat
|
||||
37
_test/interface36.go
Normal file
37
_test/interface36.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
t *S
|
||||
_ I = t
|
||||
_ J = t
|
||||
)
|
||||
|
||||
type S struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *S) F() int { return len(s.Name) }
|
||||
func (s *S) G() int { return s.F() }
|
||||
func (s *S) Ri() I { return s }
|
||||
func (s *S) Rj() J { return s }
|
||||
|
||||
type J interface {
|
||||
I
|
||||
G() int
|
||||
Rj() J
|
||||
}
|
||||
|
||||
type I interface {
|
||||
F() int
|
||||
Ri() I
|
||||
}
|
||||
|
||||
func main() {
|
||||
var j J
|
||||
fmt.Println(j)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
23
_test/interface37.go
Normal file
23
_test/interface37.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
type I interface {
|
||||
A() string
|
||||
B() string
|
||||
}
|
||||
|
||||
type s struct{}
|
||||
|
||||
func NewS() (I, error) {
|
||||
return &s{}, nil
|
||||
}
|
||||
|
||||
func (c *s) A() string { return "a" }
|
||||
func (c *s) B() string { return "b" }
|
||||
|
||||
func main() {
|
||||
s, _ := NewS()
|
||||
println(s.A())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a
|
||||
19
_test/interface38.go
Normal file
19
_test/interface38.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type foo struct {
|
||||
bar string
|
||||
}
|
||||
|
||||
func (f foo) String() string {
|
||||
return "Hello from " + f.bar
|
||||
}
|
||||
|
||||
func main() {
|
||||
var f fmt.Stringer = foo{bar: "bar"}
|
||||
fmt.Println(f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from bar
|
||||
19
_test/interface39.go
Normal file
19
_test/interface39.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type foo struct {
|
||||
bar string
|
||||
}
|
||||
|
||||
func (f *foo) String() string {
|
||||
return "Hello from " + f.bar
|
||||
}
|
||||
|
||||
func main() {
|
||||
var f fmt.Stringer = &foo{bar: "bar"}
|
||||
fmt.Println(f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from bar
|
||||
23
_test/interface40.go
Normal file
23
_test/interface40.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type foo struct {
|
||||
bar string
|
||||
}
|
||||
|
||||
func (f foo) String() string {
|
||||
return "Hello from " + f.bar
|
||||
}
|
||||
|
||||
func Foo(s string) fmt.Stringer {
|
||||
return foo{s}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := Foo("bar")
|
||||
fmt.Println(f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from bar
|
||||
23
_test/interface41.go
Normal file
23
_test/interface41.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type foo struct {
|
||||
bar string
|
||||
}
|
||||
|
||||
func (f *foo) String() string {
|
||||
return "Hello from " + f.bar
|
||||
}
|
||||
|
||||
func Foo(s string) fmt.Stringer {
|
||||
return &foo{s}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := Foo("bar")
|
||||
fmt.Println(f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from bar
|
||||
12
_test/interface42.go
Normal file
12
_test/interface42.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
v := interface{}(0)
|
||||
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
12
_test/interface43.go
Normal file
12
_test/interface43.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
v := interface{}(nil)
|
||||
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
13
_test/len0.go
Normal file
13
_test/len0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f(a []int) interface{} {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
println(f(a).(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
13
_test/make0.go
Normal file
13
_test/make0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f() interface{} {
|
||||
return make([]int, 2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := f()
|
||||
println(len(a.([]int)))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
15
_test/make1.go
Normal file
15
_test/make1.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f() interface{} {
|
||||
return make(map[int]int)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, ok := f().(map[int]int)
|
||||
fmt.Println(a, ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[] true
|
||||
18
_test/map24.go
Normal file
18
_test/map24.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
jb := []byte(`{"property": "test"}`)
|
||||
params := map[string]interface{}{"foo": 1}
|
||||
if err := json.Unmarshal(jb, ¶ms); err != nil {
|
||||
panic("marshal failed.")
|
||||
}
|
||||
fmt.Println(params["foo"], params["property"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1 test
|
||||
26
_test/map25.go
Normal file
26
_test/map25.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
jb := []byte(`{"num": "2"}`)
|
||||
params := map[string]interface{}{"foo": "1"}
|
||||
if err := json.Unmarshal(jb, ¶ms); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sum := 0
|
||||
for _, v := range params {
|
||||
i, err := strconv.Atoi(v.(string))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sum += i
|
||||
}
|
||||
println(sum)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
11
_test/map26.go
Normal file
11
_test/map26.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
var m = map[string]int{"foo": 1, "bar": 2}
|
||||
|
||||
func main() {
|
||||
var a interface{} = m["foo"]
|
||||
println(a.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
24
_test/map27.go
Normal file
24
_test/map27.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type fm map[string]interface{}
|
||||
|
||||
type foo struct{}
|
||||
|
||||
func main() {
|
||||
a := make(fm)
|
||||
a["foo"] = &foo{}
|
||||
fmt.Println(a["foo"])
|
||||
|
||||
b := make(template.FuncMap) // type FuncMap map[string]interface{}
|
||||
b["foo"] = &foo{}
|
||||
fmt.Println(b["foo"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{}
|
||||
// &{}
|
||||
21
_test/map28.go
Normal file
21
_test/map28.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
value1 := url.Values{}
|
||||
|
||||
value1.Set("first", "v1")
|
||||
value1.Set("second", "v2")
|
||||
|
||||
l := 0
|
||||
for k, v := range value1 {
|
||||
l += len(k) + len(v)
|
||||
}
|
||||
println(l)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 13
|
||||
26
_test/map29.go
Normal file
26
_test/map29.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Item struct {
|
||||
Object interface{}
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
func main() {
|
||||
items := map[string]Item{}
|
||||
|
||||
items["test"] = Item{
|
||||
Object: "test",
|
||||
Expiry: time.Second,
|
||||
}
|
||||
|
||||
item := items["test"]
|
||||
fmt.Println(item)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {test 1s}
|
||||
18
_test/method32.go
Normal file
18
_test/method32.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var a = []func(string){bar}
|
||||
b := a[0]
|
||||
b("bar")
|
||||
}
|
||||
|
||||
func bar(a string) {
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bar
|
||||
58
_test/method33.go
Normal file
58
_test/method33.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type T1 struct{}
|
||||
|
||||
func (t1 T1) f() {
|
||||
fmt.Println("T1.f()")
|
||||
}
|
||||
|
||||
func (t1 T1) g() {
|
||||
fmt.Println("T1.g()")
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
T1
|
||||
}
|
||||
|
||||
func (t2 T2) f() {
|
||||
fmt.Println("T2.f()")
|
||||
}
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
func printType(i I) {
|
||||
if t1, ok := i.(T1); ok {
|
||||
println("T1 ok")
|
||||
t1.f()
|
||||
t1.g()
|
||||
}
|
||||
|
||||
if t2, ok := i.(T2); ok {
|
||||
println("T2 ok")
|
||||
t2.f()
|
||||
t2.g()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("T1")
|
||||
printType(T1{})
|
||||
println("T2")
|
||||
printType(T2{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// T1
|
||||
// T1 ok
|
||||
// T1.f()
|
||||
// T1.g()
|
||||
// T2
|
||||
// T2 ok
|
||||
// T2.f()
|
||||
// T1.g()
|
||||
23
_test/method34.go
Normal file
23
_test/method34.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
type Root struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type One struct {
|
||||
Root
|
||||
}
|
||||
|
||||
type Hi interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
func (r *Root) Hello() string { return "Hello " + r.Name }
|
||||
|
||||
func main() {
|
||||
var one interface{} = &One{Root{Name: "test2"}}
|
||||
println(one.(Hi).Hello())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello test2
|
||||
13
_test/new2.go
Normal file
13
_test/new2.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f() interface{} {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := f()
|
||||
println(*(a.(*int)))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
12
_test/nil2.go
Normal file
12
_test/nil2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func test() error { return nil }
|
||||
|
||||
func main() {
|
||||
if err := test(); nil == err {
|
||||
println("err is nil")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// err is nil
|
||||
16
_test/not0.go
Normal file
16
_test/not0.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
b := true
|
||||
c := false
|
||||
if b && c {
|
||||
a = 1
|
||||
} else {
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// -1
|
||||
15
_test/not1.go
Normal file
15
_test/not1.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
b := true
|
||||
if (!b) {
|
||||
a = 1
|
||||
} else {
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// -1
|
||||
9
_test/not2.go
Normal file
9
_test/not2.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var b interface{} = !(1 == 2)
|
||||
println(b.(bool))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
10
_test/num0.go
Normal file
10
_test/num0.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%g\n", 1.0)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
i := 100
|
||||
println(i % 1e2)
|
||||
i := 102
|
||||
println(i % -1e2)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 2
|
||||
|
||||
12
_test/op5.go
Normal file
12
_test/op5.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
i := 100
|
||||
j := i % 1e2
|
||||
fmt.Printf("%T %v\n", j, j)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// int 0
|
||||
21
_test/range7.go
Normal file
21
_test/range7.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func someChan() <-chan struct{} {
|
||||
c := make(chan struct{}, 1)
|
||||
c <- struct{}{}
|
||||
return c
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _ = range someChan() {
|
||||
fmt.Println("success")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// success
|
||||
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
|
||||
14
_test/real0.go
Normal file
14
_test/real0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(c complex128) interface{} { return real(c) }
|
||||
|
||||
func main() {
|
||||
c := complex(3, 2)
|
||||
a := f(c)
|
||||
fmt.Println(a.(float64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
15
_test/recover2.go
Normal file
15
_test/recover2.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
|
||||
var r interface{} = 1
|
||||
r = recover()
|
||||
if r == nil {
|
||||
println("world")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// world
|
||||
22
_test/recover3.go
Normal file
22
_test/recover3.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
|
||||
var r interface{} = 1
|
||||
r = recover()
|
||||
fmt.Printf("%v\n", r)
|
||||
if r == nil {
|
||||
println("world")
|
||||
}
|
||||
if r != nil {
|
||||
println("exception")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// <nil>
|
||||
// world
|
||||
25
_test/recover4.go
Normal file
25
_test/recover4.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func div(a, b int) (result int) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
|
||||
fmt.Printf("r = %#v\n", r)
|
||||
|
||||
if r != nil {
|
||||
result = 0
|
||||
}
|
||||
}()
|
||||
|
||||
return a / b
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(div(30, 2))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// r = <nil>
|
||||
// 15
|
||||
15
_test/redeclaration0.go
Normal file
15
_test/redeclaration0.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type foo struct {
|
||||
yolo string
|
||||
}
|
||||
|
||||
var foo int
|
||||
foo = 2
|
||||
println(foo)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration0.go:8:6: foo redeclared in this block
|
||||
// previous declaration at ../_test/redeclaration0.go:4:7
|
||||
12
_test/redeclaration1.go
Normal file
12
_test/redeclaration1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var foo string
|
||||
|
||||
var foo int
|
||||
foo = 2
|
||||
println(foo)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration1.go:6:6: foo redeclared in this block
|
||||
15
_test/redeclaration2.go
Normal file
15
_test/redeclaration2.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var foo struct {
|
||||
yolo string
|
||||
}
|
||||
|
||||
var foo int
|
||||
foo = 2
|
||||
println(foo)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration2.go:8:6: foo redeclared in this block
|
||||
// previous declaration at ../_test/redeclaration2.go:4:6
|
||||
13
_test/redeclaration3.go
Normal file
13
_test/redeclaration3.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var foo int
|
||||
foo = 2
|
||||
|
||||
type foo struct{}
|
||||
var bar foo
|
||||
println(bar)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration3.go:7:7: foo redeclared in this block
|
||||
14
_test/redeclaration4.go
Normal file
14
_test/redeclaration4.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var foo struct{
|
||||
yolo string
|
||||
}
|
||||
|
||||
type foo struct{}
|
||||
var bar foo
|
||||
println(bar)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration4.go:8:7: foo redeclared in this block
|
||||
14
_test/redeclaration5.go
Normal file
14
_test/redeclaration5.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type foo struct{
|
||||
yolo string
|
||||
}
|
||||
|
||||
type foo struct{}
|
||||
var bar foo
|
||||
println(bar)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// ../_test/redeclaration5.go:8:7: foo redeclared in this block
|
||||
14
_test/select10.go
Normal file
14
_test/select10.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := make(chan string)
|
||||
select {
|
||||
case <-c:
|
||||
println("unexpected")
|
||||
default:
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
16
_test/select11.go
Normal file
16
_test/select11.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := make(chan string)
|
||||
select {
|
||||
case <-c:
|
||||
println("unexpected")
|
||||
default:
|
||||
println("nothing received")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// nothing received
|
||||
// bye
|
||||
23
_test/select12.go
Normal file
23
_test/select12.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
q chan struct{}
|
||||
}
|
||||
|
||||
func (s *S) Send() {
|
||||
select {
|
||||
case s.q <- struct{}{}:
|
||||
println("sent")
|
||||
default:
|
||||
println("unexpected")
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
s := &S{q: make(chan struct{}, 1)}
|
||||
s.Send()
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// sent
|
||||
// bye
|
||||
16
_test/select13.go
Normal file
16
_test/select13.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var c interface{} = int64(1)
|
||||
q := make(chan struct{})
|
||||
select {
|
||||
case q <- struct{}{}:
|
||||
println("unexpected")
|
||||
default:
|
||||
_ = c.(int64)
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
@@ -15,3 +15,6 @@ func main() {
|
||||
_, err := c.Get("url")
|
||||
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
|
||||
@@ -14,4 +14,5 @@ func main() {
|
||||
println(t.b)
|
||||
}
|
||||
|
||||
// Output: true
|
||||
// Output:
|
||||
// true
|
||||
|
||||
21
_test/struct46.go
Normal file
21
_test/struct46.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
C D
|
||||
}
|
||||
|
||||
type D struct {
|
||||
E *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
a.C = D{E: a}
|
||||
fmt.Println(a.C.E.B)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// b
|
||||
26
_test/struct47.go
Normal file
26
_test/struct47.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
C D
|
||||
}
|
||||
|
||||
func (a *A) Test() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
type D struct {
|
||||
E *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
d := D{E: a}
|
||||
a.C = d
|
||||
fmt.Println(a.C.E.Test())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
37
_test/struct48.go
Normal file
37
_test/struct48.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
type List struct {
|
||||
Next *List
|
||||
Num int
|
||||
}
|
||||
|
||||
func add(l *List, n int) *List {
|
||||
if l == nil {
|
||||
return &List{Num: n}
|
||||
}
|
||||
l.Next = add(l.Next, n)
|
||||
return l
|
||||
}
|
||||
|
||||
func pr(l *List) {
|
||||
if l == nil {
|
||||
println("")
|
||||
return
|
||||
}
|
||||
print(l.Num)
|
||||
pr(l.Next)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := add(nil, 0)
|
||||
pr(a)
|
||||
a = add(a, 1)
|
||||
pr(a)
|
||||
a = add(a, 2)
|
||||
pr(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 01
|
||||
// 012
|
||||
13
_test/switch34.go
Normal file
13
_test/switch34.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
switch a.(type) {
|
||||
case []int:
|
||||
case []string:
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
17
_test/switch35.go
Normal file
17
_test/switch35.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 2
|
||||
switch {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
println(2)
|
||||
default:
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// bye
|
||||
14
_test/switch36.go
Normal file
14
_test/switch36.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 2
|
||||
switch {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
15
_test/switch37.go
Normal file
15
_test/switch37.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 2
|
||||
switch {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 3:
|
||||
default:
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
15
_test/time12.go
Normal file
15
_test/time12.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var twentyFourHours = time.Duration(24 * time.Hour)
|
||||
|
||||
func main() {
|
||||
fmt.Println(twentyFourHours.Hours())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 24
|
||||
16
_test/type21.go
Normal file
16
_test/type21.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
|
||||
v := reflect.ValueOf(t.String)
|
||||
f := v.Interface().(func() string)
|
||||
println(f())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2009-11-10 23:04:05 +0000 UTC
|
||||
18
_test/type22.go
Normal file
18
_test/type22.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
type T1 T
|
||||
|
||||
func foo() T1 {
|
||||
return T1(T{"foo"})
|
||||
}
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(foo().Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
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
|
||||
20
_test/variadic6.go
Normal file
20
_test/variadic6.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
}
|
||||
|
||||
func (a A) f(vals ...bool) {
|
||||
for _, v := range vals {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
bools := []bool{true}
|
||||
a := A{}
|
||||
a.f(bools...)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
20
_test/variadic7.go
Normal file
20
_test/variadic7.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a, b string
|
||||
|
||||
pattern := "%s %s"
|
||||
dest := []interface{}{&a, &b}
|
||||
|
||||
n, err := fmt.Sscanf("test1 test2", pattern, dest...)
|
||||
if err != nil || n != len(dest) {
|
||||
println("error")
|
||||
return
|
||||
}
|
||||
println(a, b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test1 test2
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
@@ -150,7 +151,7 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
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}
|
||||
}
|
||||
@@ -253,35 +254,35 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow
|
||||
func fixConst(name string, val constant.Value) string {
|
||||
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
|
||||
|
||||
@@ -1,22 +1,66 @@
|
||||
/*
|
||||
Yaegi interprets Go programs.
|
||||
|
||||
Yaegi reads Go language programs from its standard input or from a file
|
||||
and evaluates them.
|
||||
Yaegi reads Go language programs from standard input, string
|
||||
parameters or files and run them.
|
||||
|
||||
If invoked with no arguments, it processes the standard input
|
||||
in a Read-Eval-Print-Loop. A prompt is displayed if standard input
|
||||
is a terminal.
|
||||
If invoked with no arguments, it processes the standard input in
|
||||
a Read-Eval-Print-Loop. A prompt is displayed if standard input is
|
||||
a terminal.
|
||||
|
||||
Given a file, it operates on that file. If the first line starts with
|
||||
"#!/usr/bin/env yaegi", and the file has exec permission, then the file
|
||||
can be invoked directly from the shell.
|
||||
File Mode
|
||||
|
||||
In file mode, as in standard Go, files are read entirely, then parsed,
|
||||
then evaluated. In REPL mode, each line is parsed and evaluated separately,
|
||||
at global level in an implicit main package.
|
||||
In file mode, as in a standard Go compiler, source files are read entirely
|
||||
before being parsed, then evaluated. It allows to handle forward
|
||||
declarations and to have package code split in multiple source files.
|
||||
|
||||
Go specifications fully apply in this mode.
|
||||
|
||||
All files are interpreted in file mode except the initial file if it
|
||||
starts with "#!" characters (the shebang pattern to allow executable
|
||||
scripts), for example "#!/usr/bin/env yaegi". In that case, the initial
|
||||
file is interpreted in REPL mode.
|
||||
|
||||
REPL mode
|
||||
|
||||
In REPL mode, the interpreter parses the code incrementally. As soon
|
||||
as a statement is complete, it evaluates it. This makes the interpreter
|
||||
suitable for interactive command line and scripts.
|
||||
|
||||
Go specifications apply with the following differences:
|
||||
|
||||
All local and global declarations (const, var, type, func) are allowed,
|
||||
including in short form, except that all identifiers must be defined
|
||||
before use (as declarations inside a standard Go function).
|
||||
|
||||
The statements are evaluated in the global space, within an implicit
|
||||
"main" package.
|
||||
|
||||
It is not necessary to have a package statement, or a main function in
|
||||
REPL mode. Import statements for preloaded binary packages can also
|
||||
be avoided (i.e. all the standard library except the few packages
|
||||
where default names collide, as "math/rand" and "crypto/rand", for which
|
||||
an explicit import is still necessary).
|
||||
|
||||
Note that the source packages are always interpreted in file mode,
|
||||
even if imported from REPL.
|
||||
|
||||
The following extract is a valid executable script:
|
||||
|
||||
#!/usr/bin/env yaegi
|
||||
helloHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||
io.WriteString(w, "Hello, world!\n")
|
||||
}
|
||||
http.HandleFunc("/hello", helloHandler)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
|
||||
Example of a one liner:
|
||||
|
||||
$ yaegi -e 'println(reflect.TypeOf(fmt.Print))'
|
||||
|
||||
Options:
|
||||
-e string
|
||||
evaluate the string and return.
|
||||
-i
|
||||
start an interactive REPL after file execution.
|
||||
-tags tag,list
|
||||
@@ -28,6 +72,11 @@ Debugging support (may be removed at any time):
|
||||
Generate and display graphviz dot of AST with dotty(1)
|
||||
YAEGI_CFG_DOT=1
|
||||
Generate and display graphviz dot of CFG with dotty(1)
|
||||
YAEGI_DOT_CMD='dot -Tsvg -ofoo.svg'
|
||||
Defines how to process the dot code generated whenever YAEGI_AST_DOT and/or
|
||||
YAEGI_CFG_DOT is enabled. If any of YAEGI_AST_DOT or YAEGI_CFG_DOT is set,
|
||||
but YAEGI_DOT_CMD is not defined, the default is to write to a .dot file
|
||||
next to the Go source file.
|
||||
*/
|
||||
package main
|
||||
|
||||
@@ -84,17 +133,19 @@ func main() {
|
||||
log.Fatal("Could not read file: ", args[0])
|
||||
}
|
||||
|
||||
s := string(b)
|
||||
if s[:2] == "#!" {
|
||||
// Allow executable go scripts, but fix them prior to parse
|
||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
}
|
||||
|
||||
i.Name = args[0]
|
||||
if _, err := i.Eval(s); err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
i.REPL(strings.NewReader(s), os.Stdout)
|
||||
} else {
|
||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||
i.Name = args[0]
|
||||
_, err := i.Eval(s)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,34 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI
|
||||
CITimeoutMultiplier = 3
|
||||
)
|
||||
|
||||
// Sleep pauses the current goroutine for at least the duration d.
|
||||
func Sleep(d time.Duration) {
|
||||
d = applyCIMultiplier(d)
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
ci := os.Getenv("CI")
|
||||
if ci == "" {
|
||||
return timeout
|
||||
}
|
||||
b, err := strconv.ParseBool(ci)
|
||||
if err != nil || !b {
|
||||
return timeout
|
||||
}
|
||||
return time.Duration(float64(timeout) * CITimeoutMultiplier)
|
||||
}
|
||||
|
||||
func TestYaegiCmdCancel(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "yaegi-")
|
||||
if err != nil {
|
||||
@@ -56,7 +80,7 @@ func TestYaegiCmdCancel(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("failed pipe test source to yaegi command: %v", err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
Sleep(200 * time.Millisecond)
|
||||
err = cmd.Process.Signal(os.Interrupt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)
|
||||
@@ -84,7 +108,7 @@ func TestYaegiCmdCancel(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
if outBuf.String() != "context canceled\n2\n" {
|
||||
if outBuf.String() != "context canceled\n" {
|
||||
t.Errorf("unexpected output: %q", &outBuf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
@@ -12,14 +13,18 @@ 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}}
|
||||
func {{$name}}(n *node) {
|
||||
dest := genValue(n)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.typ.TypeOf()
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
dest := genValueOutput(n, typ)
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
switch typ.Kind() {
|
||||
@@ -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}}
|
||||
@@ -379,7 +409,7 @@ func {{$name}}Const(n *node) {
|
||||
{{range $name, $op := .Comparison}}
|
||||
func {{$name}}(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
@@ -822,6 +852,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)
|
||||
|
||||
191
interp/ast.go
191
interp/ast.go
@@ -3,10 +3,10 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
@@ -32,6 +32,7 @@ const (
|
||||
caseClause
|
||||
chanType
|
||||
commClause
|
||||
commClauseDefault
|
||||
compositeLitExpr
|
||||
constDecl
|
||||
continueStmt
|
||||
@@ -94,80 +95,81 @@ const (
|
||||
)
|
||||
|
||||
var kinds = [...]string{
|
||||
undefNode: "undefNode",
|
||||
addressExpr: "addressExpr",
|
||||
arrayType: "arrayType",
|
||||
assignStmt: "assignStmt",
|
||||
assignXStmt: "assignXStmt",
|
||||
basicLit: "basicLit",
|
||||
binaryExpr: "binaryExpr",
|
||||
blockStmt: "blockStmt",
|
||||
branchStmt: "branchStmt",
|
||||
breakStmt: "breakStmt",
|
||||
callExpr: "callExpr",
|
||||
caseBody: "caseBody",
|
||||
caseClause: "caseClause",
|
||||
chanType: "chanType",
|
||||
commClause: "commClause",
|
||||
compositeLitExpr: "compositeLitExpr",
|
||||
constDecl: "constDecl",
|
||||
continueStmt: "continueStmt",
|
||||
declStmt: "declStmt",
|
||||
deferStmt: "deferStmt",
|
||||
defineStmt: "defineStmt",
|
||||
defineXStmt: "defineXStmt",
|
||||
ellipsisExpr: "ellipsisExpr",
|
||||
exprStmt: "exprStmt",
|
||||
fallthroughtStmt: "fallthroughStmt",
|
||||
fieldExpr: "fieldExpr",
|
||||
fieldList: "fieldList",
|
||||
fileStmt: "fileStmt",
|
||||
forStmt0: "forStmt0",
|
||||
forStmt1: "forStmt1",
|
||||
forStmt2: "forStmt2",
|
||||
forStmt3: "forStmt3",
|
||||
forStmt3a: "forStmt3a",
|
||||
forStmt4: "forStmt4",
|
||||
forRangeStmt: "forRangeStmt",
|
||||
funcDecl: "funcDecl",
|
||||
funcType: "funcType",
|
||||
funcLit: "funcLit",
|
||||
goStmt: "goStmt",
|
||||
gotoStmt: "gotoStmt",
|
||||
identExpr: "identExpr",
|
||||
ifStmt0: "ifStmt0",
|
||||
ifStmt1: "ifStmt1",
|
||||
ifStmt2: "ifStmt2",
|
||||
ifStmt3: "ifStmt3",
|
||||
importDecl: "importDecl",
|
||||
importSpec: "importSpec",
|
||||
incDecStmt: "incDecStmt",
|
||||
indexExpr: "indexExpr",
|
||||
interfaceType: "interfaceType",
|
||||
keyValueExpr: "keyValueExpr",
|
||||
labeledStmt: "labeledStmt",
|
||||
landExpr: "landExpr",
|
||||
lorExpr: "lorExpr",
|
||||
mapType: "mapType",
|
||||
parenExpr: "parenExpr",
|
||||
rangeStmt: "rangeStmt",
|
||||
returnStmt: "returnStmt",
|
||||
selectStmt: "selectStmt",
|
||||
selectorExpr: "selectorExpr",
|
||||
selectorImport: "selectorImport",
|
||||
sendStmt: "sendStmt",
|
||||
sliceExpr: "sliceExpr",
|
||||
starExpr: "starExpr",
|
||||
structType: "structType",
|
||||
switchStmt: "switchStmt",
|
||||
switchIfStmt: "switchIfStmt",
|
||||
typeAssertExpr: "typeAssertExpr",
|
||||
typeDecl: "typeDecl",
|
||||
typeSpec: "typeSpec",
|
||||
typeSwitch: "typeSwitch",
|
||||
unaryExpr: "unaryExpr",
|
||||
valueSpec: "valueSpec",
|
||||
varDecl: "varDecl",
|
||||
undefNode: "undefNode",
|
||||
addressExpr: "addressExpr",
|
||||
arrayType: "arrayType",
|
||||
assignStmt: "assignStmt",
|
||||
assignXStmt: "assignXStmt",
|
||||
basicLit: "basicLit",
|
||||
binaryExpr: "binaryExpr",
|
||||
blockStmt: "blockStmt",
|
||||
branchStmt: "branchStmt",
|
||||
breakStmt: "breakStmt",
|
||||
callExpr: "callExpr",
|
||||
caseBody: "caseBody",
|
||||
caseClause: "caseClause",
|
||||
chanType: "chanType",
|
||||
commClause: "commClause",
|
||||
commClauseDefault: "commClauseDefault",
|
||||
compositeLitExpr: "compositeLitExpr",
|
||||
constDecl: "constDecl",
|
||||
continueStmt: "continueStmt",
|
||||
declStmt: "declStmt",
|
||||
deferStmt: "deferStmt",
|
||||
defineStmt: "defineStmt",
|
||||
defineXStmt: "defineXStmt",
|
||||
ellipsisExpr: "ellipsisExpr",
|
||||
exprStmt: "exprStmt",
|
||||
fallthroughtStmt: "fallthroughStmt",
|
||||
fieldExpr: "fieldExpr",
|
||||
fieldList: "fieldList",
|
||||
fileStmt: "fileStmt",
|
||||
forStmt0: "forStmt0",
|
||||
forStmt1: "forStmt1",
|
||||
forStmt2: "forStmt2",
|
||||
forStmt3: "forStmt3",
|
||||
forStmt3a: "forStmt3a",
|
||||
forStmt4: "forStmt4",
|
||||
forRangeStmt: "forRangeStmt",
|
||||
funcDecl: "funcDecl",
|
||||
funcType: "funcType",
|
||||
funcLit: "funcLit",
|
||||
goStmt: "goStmt",
|
||||
gotoStmt: "gotoStmt",
|
||||
identExpr: "identExpr",
|
||||
ifStmt0: "ifStmt0",
|
||||
ifStmt1: "ifStmt1",
|
||||
ifStmt2: "ifStmt2",
|
||||
ifStmt3: "ifStmt3",
|
||||
importDecl: "importDecl",
|
||||
importSpec: "importSpec",
|
||||
incDecStmt: "incDecStmt",
|
||||
indexExpr: "indexExpr",
|
||||
interfaceType: "interfaceType",
|
||||
keyValueExpr: "keyValueExpr",
|
||||
labeledStmt: "labeledStmt",
|
||||
landExpr: "landExpr",
|
||||
lorExpr: "lorExpr",
|
||||
mapType: "mapType",
|
||||
parenExpr: "parenExpr",
|
||||
rangeStmt: "rangeStmt",
|
||||
returnStmt: "returnStmt",
|
||||
selectStmt: "selectStmt",
|
||||
selectorExpr: "selectorExpr",
|
||||
selectorImport: "selectorImport",
|
||||
sendStmt: "sendStmt",
|
||||
sliceExpr: "sliceExpr",
|
||||
starExpr: "starExpr",
|
||||
structType: "structType",
|
||||
switchStmt: "switchStmt",
|
||||
switchIfStmt: "switchIfStmt",
|
||||
typeAssertExpr: "typeAssertExpr",
|
||||
typeDecl: "typeDecl",
|
||||
typeSpec: "typeSpec",
|
||||
typeSwitch: "typeSwitch",
|
||||
unaryExpr: "unaryExpr",
|
||||
valueSpec: "valueSpec",
|
||||
varDecl: "varDecl",
|
||||
}
|
||||
|
||||
func (k nkind) String() string {
|
||||
@@ -196,15 +198,20 @@ const (
|
||||
aAndNot
|
||||
aAndNotAssign
|
||||
aBitNot
|
||||
aBranch
|
||||
aCall
|
||||
aCallSlice
|
||||
aCase
|
||||
aCompositeLit
|
||||
aConvert
|
||||
aDec
|
||||
aEqual
|
||||
aGreater
|
||||
aGreaterEqual
|
||||
aGetFunc
|
||||
aGetIndex
|
||||
aGetMethod
|
||||
aGetSym
|
||||
aInc
|
||||
aLand
|
||||
aLor
|
||||
@@ -253,14 +260,19 @@ var actions = [...]string{
|
||||
aAndNot: "&^",
|
||||
aAndNotAssign: "&^=",
|
||||
aBitNot: "^",
|
||||
aBranch: "branch",
|
||||
aCall: "call",
|
||||
aCallSlice: "callSlice",
|
||||
aCase: "case",
|
||||
aCompositeLit: "compositeLit",
|
||||
aConvert: "convert",
|
||||
aDec: "--",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGetFunc: "getFunc",
|
||||
aGetIndex: "getIndex",
|
||||
aGetMethod: "getMethod",
|
||||
aGetSym: ".",
|
||||
aInc: "++",
|
||||
aLand: "&&",
|
||||
aLor: "||",
|
||||
@@ -463,18 +475,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)
|
||||
if math.Trunc(v) == v {
|
||||
n.rval = reflect.ValueOf(int(v))
|
||||
} else {
|
||||
n.rval = reflect.ValueOf(v)
|
||||
}
|
||||
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)
|
||||
@@ -546,7 +554,12 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
st.push(addChild(&root, anc, pos, kind, aNop), nod)
|
||||
|
||||
case *ast.CallExpr:
|
||||
st.push(addChild(&root, anc, pos, callExpr, aCall), nod)
|
||||
action := aCall
|
||||
if a.Ellipsis != token.NoPos {
|
||||
action = aCallSlice
|
||||
}
|
||||
|
||||
st.push(addChild(&root, anc, pos, callExpr, action), nod)
|
||||
|
||||
case *ast.CaseClause:
|
||||
st.push(addChild(&root, anc, pos, caseClause, aCase), nod)
|
||||
@@ -555,7 +568,11 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
|
||||
|
||||
case *ast.CommClause:
|
||||
st.push(addChild(&root, anc, pos, commClause, aNop), nod)
|
||||
kind := commClause
|
||||
if a.Comm == nil {
|
||||
kind = commClauseDefault
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, kind, aNop), nod)
|
||||
|
||||
case *ast.CommentGroup:
|
||||
return false
|
||||
|
||||
504
interp/cfg.go
504
interp/cfg.go
@@ -2,6 +2,7 @@ package interp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"math"
|
||||
"reflect"
|
||||
@@ -50,7 +51,6 @@ var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
var initNodes []*node
|
||||
var iotaValue int
|
||||
var err error
|
||||
|
||||
root.Walk(func(n *node) bool {
|
||||
@@ -90,6 +90,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
n.anc.gen = rangeMap
|
||||
ityp := &itype{cat: valueT, rtype: reflect.TypeOf((*reflect.MapIter)(nil))}
|
||||
sc.add(ityp)
|
||||
ktyp = &itype{cat: valueT, rtype: typ.Key()}
|
||||
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
|
||||
case reflect.String:
|
||||
@@ -202,9 +204,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
nod.typ = typ
|
||||
}
|
||||
|
||||
case commClauseDefault:
|
||||
sc = sc.pushBloc()
|
||||
|
||||
case commClause:
|
||||
sc = sc.pushBloc()
|
||||
if n.child[0].action == aAssign {
|
||||
if len(n.child) > 0 && n.child[0].action == aAssign {
|
||||
ch := n.child[0].child[1].child[0]
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, ch); err != nil {
|
||||
@@ -247,6 +252,10 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case binaryExpr, unaryExpr:
|
||||
// Do not attempt to propagate composite type to operator expressions,
|
||||
// it breaks constant folding.
|
||||
case callExpr:
|
||||
if c.typ, err = nodeType(interp, sc, c); err != nil {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
c.typ = n.typ
|
||||
}
|
||||
@@ -350,27 +359,47 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case typeSpec:
|
||||
// processing already done in GTA pass for global types, only parses inlined types
|
||||
if sc.def != nil {
|
||||
typeName := n.child[0].ident
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||
} else {
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
|
||||
if sc.def == nil {
|
||||
return false
|
||||
}
|
||||
typeName := n.child[0].ident
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
|
||||
if _, exists := sc.sym[typeName]; exists {
|
||||
// TODO(mpl): find the exact location of the previous declaration
|
||||
err = n.cfgErrorf("%s redeclared in this block", typeName)
|
||||
return false
|
||||
}
|
||||
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||
} else {
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
|
||||
return false
|
||||
|
||||
case arrayType, basicLit, chanType, funcType, mapType, structType:
|
||||
case constDecl:
|
||||
// Early parse of constDecl subtrees, to compute all constant
|
||||
// values which may be used in further declarations.
|
||||
if !sc.global {
|
||||
for _, c := range n.child {
|
||||
if _, err = interp.cfg(c, pkgID); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case arrayType, basicLit, chanType, funcType, interfaceType, mapType, structType:
|
||||
n.typ, err = nodeType(interp, sc, n)
|
||||
return false
|
||||
}
|
||||
@@ -482,7 +511,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
switch {
|
||||
case n.action == aAssign && src.action == aCall && dest.typ.cat != interfaceT:
|
||||
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
|
||||
// Call action may perform the assignment directly.
|
||||
n.gen = nop
|
||||
src.level = level
|
||||
@@ -495,7 +524,14 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.gen = nop
|
||||
src.findex = dest.findex // Set recv address to LHS
|
||||
dest.typ = src.typ
|
||||
case n.action == aAssign && src.action == aCompositeLit:
|
||||
case n.action == aAssign && src.action == aCompositeLit && !isMapEntry(dest):
|
||||
if dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface {
|
||||
// Skip optimisation for assigned binary interface or map entry
|
||||
// which require and additional operation to set the value
|
||||
break
|
||||
}
|
||||
// Skip the assign operation entirely, the source frame index is set
|
||||
// to destination index, avoiding extra memory alloc and duplication.
|
||||
n.gen = nop
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
@@ -508,8 +544,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
|
||||
}
|
||||
@@ -524,8 +563,18 @@ 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
|
||||
iotaValue++
|
||||
if childPos(n) == len(n.anc.child)-1 {
|
||||
sc.iota = 0
|
||||
} else {
|
||||
sc.iota++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,22 +591,28 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case assignXStmt:
|
||||
wireChild(n)
|
||||
l := len(n.child) - 1
|
||||
switch n.child[l].kind {
|
||||
switch lc := n.child[l]; lc.kind {
|
||||
case callExpr:
|
||||
if n.child[l-1].isType(sc) {
|
||||
l--
|
||||
}
|
||||
if r := lc.child[0].typ.numOut(); r != l {
|
||||
err = n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, lc.child[0].name(), r)
|
||||
}
|
||||
n.gen = nop
|
||||
case indexExpr:
|
||||
n.child[l].gen = getIndexMap2
|
||||
lc.gen = getIndexMap2
|
||||
n.gen = nop
|
||||
case typeAssertExpr:
|
||||
if n.child[0].ident == "_" {
|
||||
n.child[l].gen = typeAssertStatus
|
||||
lc.gen = typeAssertStatus
|
||||
} else {
|
||||
n.child[l].gen = typeAssert2
|
||||
lc.gen = typeAssert2
|
||||
}
|
||||
n.gen = nop
|
||||
case unaryExpr:
|
||||
if n.child[l].action == aRecv {
|
||||
n.child[l].gen = recv2
|
||||
if lc.action == aRecv {
|
||||
lc.gen = recv2
|
||||
n.gen = nop
|
||||
}
|
||||
}
|
||||
@@ -565,7 +620,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case defineXStmt:
|
||||
wireChild(n)
|
||||
if sc.def == nil {
|
||||
// in global scope, type definition already handled by GTA
|
||||
// In global scope, type definition already handled by GTA.
|
||||
break
|
||||
}
|
||||
err = compDefineX(sc, n)
|
||||
@@ -575,8 +630,8 @@ 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
|
||||
// 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())
|
||||
break
|
||||
@@ -590,10 +645,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !(isNumber(t0) && isNumber(t1)) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aRem, aAnd, aOr, aXor, aAndNot:
|
||||
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)
|
||||
@@ -620,33 +680,35 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if c0.rval.IsValid() && c1.rval.IsValid() && constOp[n.action] != nil {
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
break
|
||||
}
|
||||
n.typ.TypeOf() // init reflect type
|
||||
constOp[n.action](n)
|
||||
}
|
||||
if c0.rval.IsValid() && c1.rval.IsValid() && !isInterface(n.typ) && constOp[n.action] != nil {
|
||||
n.typ.TypeOf() // Force compute of reflection type.
|
||||
constOp[n.action](n) // Compute a constant result now rather than during exec.
|
||||
}
|
||||
switch {
|
||||
case n.rval.IsValid():
|
||||
// This operation involved constants, and the result is already computed
|
||||
// by constOp and available in n.rval. Nothing else to do at execution.
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
// To avoid a copy in frame, if the result is to be assigned, store it directly
|
||||
// at the frame location of destination.
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
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.
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
n.findex = pos
|
||||
default:
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Allocate a new location in frame, and store the result here.
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
|
||||
@@ -654,7 +716,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
switch t.cat {
|
||||
case ptrT:
|
||||
case aliasT, ptrT:
|
||||
n.typ = t.val
|
||||
if t.val.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
|
||||
@@ -673,7 +735,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.typ = t.val
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
n.recv = &receiver{node: n}
|
||||
typ := t.TypeOf()
|
||||
switch k := typ.Kind(); k {
|
||||
case reflect.Map:
|
||||
@@ -702,11 +763,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
sc = sc.pop()
|
||||
|
||||
case constDecl:
|
||||
iotaValue = 0
|
||||
wireChild(n)
|
||||
|
||||
case varDecl:
|
||||
case constDecl, varDecl:
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
@@ -749,14 +806,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
if n.typ.cat == builtinT {
|
||||
switch {
|
||||
case n.typ.cat == builtinT:
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
} else {
|
||||
case n.anc.kind == returnStmt:
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
default:
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
|
||||
op(n) // pre-compute non-assigned constant builtin calls
|
||||
op(n) // pre-compute non-assigned constant :
|
||||
}
|
||||
|
||||
case n.child[0].isType(sc):
|
||||
@@ -764,6 +825,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
|
||||
err = n.cfgErrorf("truncated to integer")
|
||||
}
|
||||
n.action = aConvert
|
||||
if isInterface(n.child[0].typ) && !n.child[1].isNil() {
|
||||
// Convert to interface: just check that all required methods are defined by concrete type.
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
@@ -783,7 +845,23 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
case isBinCall(n):
|
||||
n.gen = callBin
|
||||
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
|
||||
typ := n.child[0].typ.rtype
|
||||
numIn := len(n.child) - 1
|
||||
tni := typ.NumIn()
|
||||
if numIn == 1 && isCall(n.child[1]) {
|
||||
numIn = n.child[1].typ.numOut()
|
||||
}
|
||||
if n.child[0].action == aGetMethod {
|
||||
tni-- // The first argument is the method receiver.
|
||||
}
|
||||
if typ.IsVariadic() {
|
||||
tni-- // The last argument could be empty.
|
||||
}
|
||||
if numIn < tni {
|
||||
err = n.cfgErrorf("not enough arguments in call to %v", n.child[0].name())
|
||||
break
|
||||
}
|
||||
if typ.NumOut() > 0 {
|
||||
if funcType := n.child[0].typ.val; funcType != nil {
|
||||
// Use the original unwrapped function type, to allow future field and
|
||||
// methods resolutions, otherwise impossible on the opaque bin type.
|
||||
@@ -794,22 +872,30 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
if n.anc.kind == returnStmt {
|
||||
n.findex = childPos(n)
|
||||
} else {
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
if n.child[0].action == aGetFunc {
|
||||
// allocate frame entry for anonymous function
|
||||
// Allocate a frame entry to store the anonymous function definition.
|
||||
sc.add(n.child[0].typ)
|
||||
}
|
||||
if typ := n.child[0].typ; len(typ.ret) > 0 {
|
||||
n.typ = typ.ret[0]
|
||||
n.findex = sc.add(n.typ)
|
||||
for _, t := range typ.ret[1:] {
|
||||
sc.add(t)
|
||||
if n.anc.kind == returnStmt {
|
||||
n.findex = childPos(n)
|
||||
} else {
|
||||
n.findex = sc.add(n.typ)
|
||||
for _, t := range typ.ret[1:] {
|
||||
sc.add(t)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.findex = -1
|
||||
@@ -822,7 +908,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case typeSwichAssign(n) && len(n.child) > 1:
|
||||
n.start = n.child[1].start
|
||||
case len(n.child) == 0:
|
||||
// empty case body: jump to switch node (exit node)
|
||||
// Empty case body: jump to switch node (exit node).
|
||||
n.start = n.anc.anc.anc
|
||||
default:
|
||||
n.start = n.child[0].start
|
||||
@@ -831,21 +917,29 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case caseClause:
|
||||
sc = sc.pop()
|
||||
|
||||
case commClauseDefault:
|
||||
wireChild(n)
|
||||
sc = sc.pop()
|
||||
if len(n.child) == 0 {
|
||||
return
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
|
||||
|
||||
case commClause:
|
||||
wireChild(n)
|
||||
sc = sc.pop()
|
||||
if len(n.child) == 0 {
|
||||
return
|
||||
}
|
||||
if len(n.child) > 1 {
|
||||
n.start = n.child[1].start // Skip chan operation, performed by select
|
||||
} else {
|
||||
n.start = n.child[0].start // default clause
|
||||
}
|
||||
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
|
||||
sc = sc.pop()
|
||||
|
||||
case compositeLitExpr:
|
||||
wireChild(n)
|
||||
if n.anc.action != aAssign {
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
// TODO: Check that composite literal expr matches corresponding type
|
||||
n.gen = compositeGenerator(n, sc)
|
||||
|
||||
@@ -881,7 +975,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
cond.tnext = body.start
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt2: // for init; cond; {}
|
||||
@@ -903,7 +997,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3: // for ; cond; post {}
|
||||
@@ -922,7 +1016,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
body.tnext = post.start
|
||||
sc = sc.pop()
|
||||
|
||||
@@ -953,13 +1047,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
body.tnext = post.start
|
||||
sc = sc.pop()
|
||||
|
||||
case forRangeStmt:
|
||||
n.start = n.child[0].start
|
||||
n.child[0].fnext = n
|
||||
setFNext(n.child[0], n)
|
||||
sc = sc.pop()
|
||||
|
||||
case funcDecl:
|
||||
@@ -998,7 +1092,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.rval = sym.rval
|
||||
n.kind = basicLit
|
||||
case n.ident == "iota":
|
||||
n.rval = reflect.ValueOf(iotaValue)
|
||||
n.rval = reflect.ValueOf(constant.Make(int64(sc.iota)))
|
||||
n.kind = basicLit
|
||||
case n.ident == "nil":
|
||||
n.kind = basicLit
|
||||
@@ -1010,9 +1104,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = n.cfgErrorf("use of builtin %s not in function call", n.ident)
|
||||
}
|
||||
}
|
||||
if sym.kind == varSym && sym.typ != nil && sym.typ.TypeOf().Kind() == reflect.Bool {
|
||||
fixBranch(n)
|
||||
}
|
||||
}
|
||||
if n.sym != nil {
|
||||
n.recv = n.sym.recv
|
||||
@@ -1035,7 +1126,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
tbody.tnext = n
|
||||
sc = sc.pop()
|
||||
|
||||
@@ -1054,7 +1145,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
setFNext(cond, fbody.start)
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
@@ -1078,7 +1169,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
cond.fnext = n
|
||||
setFNext(cond, n)
|
||||
sc = sc.pop()
|
||||
|
||||
case ifStmt3: // if init; cond {} else {}
|
||||
@@ -1097,7 +1188,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
setFNext(cond, fbody.start)
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
@@ -1109,7 +1200,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case landExpr:
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n.child[1].start
|
||||
n.child[0].fnext = n
|
||||
setFNext(n.child[0], n)
|
||||
n.child[1].tnext = n
|
||||
n.typ = n.child[0].typ
|
||||
n.findex = sc.add(n.typ)
|
||||
@@ -1120,7 +1211,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case lorExpr:
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n
|
||||
n.child[0].fnext = n.child[1].start
|
||||
setFNext(n.child[0], n.child[1].start)
|
||||
n.child[1].tnext = n
|
||||
n.typ = n.child[0].typ
|
||||
n.findex = sc.add(n.typ)
|
||||
@@ -1158,6 +1249,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
|
||||
case returnStmt:
|
||||
if mustReturnValue(sc.def.child[2]) {
|
||||
nret := len(n.child)
|
||||
if nret == 1 && isCall(n.child[0]) {
|
||||
nret = n.child[0].child[0].typ.numOut()
|
||||
}
|
||||
if nret < sc.def.typ.numOut() {
|
||||
err = n.cfgErrorf("not enough arguments to return")
|
||||
break
|
||||
}
|
||||
}
|
||||
wireChild(n)
|
||||
n.tnext = nil
|
||||
n.val = sc.def
|
||||
@@ -1194,6 +1295,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case ok:
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
|
||||
case n.typ.rtype.Kind() == reflect.Ptr:
|
||||
@@ -1217,6 +1319,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.gen = getIndexBinPtrMethod
|
||||
n.typ = &itype{cat: valueT, rtype: m2.Type}
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
|
||||
}
|
||||
@@ -1231,11 +1334,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type}
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.val = field.Index
|
||||
@@ -1254,6 +1359,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
|
||||
n.rval = s
|
||||
}
|
||||
n.action = aGetSym
|
||||
n.gen = nop
|
||||
} else {
|
||||
err = n.cfgErrorf("package %s \"%s\" has no symbol %s", n.child[0].ident, pkg, name)
|
||||
@@ -1265,6 +1371,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.findex = sym.index
|
||||
n.val = sym.node
|
||||
n.gen = nop
|
||||
n.action = aGetSym
|
||||
n.typ = sym.typ
|
||||
n.sym = sym
|
||||
n.rval = sym.rval
|
||||
@@ -1272,6 +1379,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
|
||||
}
|
||||
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
|
||||
n.action = aGetMethod
|
||||
if n.child[0].isType(sc) {
|
||||
// Handle method as a function with receiver in 1st argument
|
||||
n.val = m
|
||||
@@ -1288,6 +1396,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
n.action = aGetMethod
|
||||
if isPtr && n.typ.fieldSeq(lind).cat != ptrT {
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
} else {
|
||||
@@ -1341,28 +1450,37 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// Chain channel init actions in commClauses prior to invoke select.
|
||||
var cur *node
|
||||
for _, c := range n.child[0].child {
|
||||
var an *node // channel init action node
|
||||
c0 := c.child[0]
|
||||
switch {
|
||||
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
|
||||
an = c0.child[0].child[0]
|
||||
case c0.action == aAssign:
|
||||
an = c0.lastChild().child[0]
|
||||
case c0.kind == sendStmt:
|
||||
an = c0.child[0]
|
||||
}
|
||||
if an != nil {
|
||||
if cur == nil {
|
||||
// First channel init action, the entry point for the select block.
|
||||
n.start = an.start
|
||||
} else {
|
||||
// Chain channel init action to the previous one.
|
||||
cur.tnext = an.start
|
||||
var an, pn *node // channel init action nodes
|
||||
if len(c.child) > 0 {
|
||||
switch c0 := c.child[0]; {
|
||||
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
|
||||
an = c0.child[0].child[0]
|
||||
pn = an
|
||||
case c0.action == aAssign:
|
||||
an = c0.lastChild().child[0]
|
||||
pn = an
|
||||
case c0.kind == sendStmt:
|
||||
an = c0.child[0]
|
||||
pn = c0.child[1]
|
||||
}
|
||||
}
|
||||
cur = an
|
||||
if an == nil {
|
||||
continue
|
||||
}
|
||||
if cur == nil {
|
||||
// First channel init action, the entry point for the select block.
|
||||
n.start = an.start
|
||||
} else {
|
||||
// Chain channel init action to the previous one.
|
||||
cur.tnext = an.start
|
||||
}
|
||||
if pn != nil {
|
||||
// Chain channect init action to send data init action.
|
||||
// (already done by wireChild, but let's be explicit).
|
||||
an.tnext = pn
|
||||
cur = pn
|
||||
}
|
||||
}
|
||||
// Invoke select action
|
||||
if cur == nil {
|
||||
// There is no channel init action, call select directly.
|
||||
n.start = n.child[0]
|
||||
@@ -1423,7 +1541,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
// Chain case clauses.
|
||||
for i, c := range clauses[:l-1] {
|
||||
c.fnext = clauses[i+1] // Chain to next clause.
|
||||
// Chain to next clause.
|
||||
setFNext(c, clauses[i+1])
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
@@ -1444,6 +1563,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
c := clauses[l-1] // Last clause.
|
||||
c.fnext = n
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
@@ -1467,24 +1587,29 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
c := clauses[i]
|
||||
c.gen = nop
|
||||
body := c.lastChild()
|
||||
if len(c.child) > 1 {
|
||||
cond := c.child[0]
|
||||
cond.tnext = body.start
|
||||
if i == l-1 {
|
||||
cond.fnext = n
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n
|
||||
c.fnext = n
|
||||
} else {
|
||||
body := c.lastChild()
|
||||
if len(c.child) > 1 {
|
||||
cond := c.child[0]
|
||||
cond.tnext = body.start
|
||||
if i == l-1 {
|
||||
setFNext(cond, n)
|
||||
} else {
|
||||
setFNext(cond, clauses[i+1].start)
|
||||
}
|
||||
c.start = cond.start
|
||||
} else {
|
||||
cond.fnext = clauses[i+1].start
|
||||
c.start = body.start
|
||||
}
|
||||
// If last case body statement is a fallthrough, then jump to next case body
|
||||
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
} else {
|
||||
body.tnext = n
|
||||
}
|
||||
c.start = cond.start
|
||||
} else {
|
||||
c.start = body.start
|
||||
}
|
||||
// If last case body statement is a fallthrough, then jump to next case body
|
||||
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
} else {
|
||||
body.tnext = n
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
@@ -1500,7 +1625,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
if n.anc.action != aAssignX {
|
||||
n.typ = n.child[1].typ
|
||||
if n.child[0].typ.cat == valueT {
|
||||
// Avoid special wrapping of interfaces and func types.
|
||||
n.typ = &itype{cat: valueT, rtype: n.child[1].typ.TypeOf()}
|
||||
} else {
|
||||
n.typ = n.child[1].typ
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
} else {
|
||||
@@ -1541,13 +1671,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
|
||||
if n.child[0].rval.IsValid() && constOp[n.action] != nil {
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
|
||||
if n.child[0].rval.IsValid() && !isInterface(n.typ) && constOp[n.action] != nil {
|
||||
n.typ.TypeOf() // init reflect type
|
||||
constOp[n.action](n)
|
||||
}
|
||||
@@ -1564,11 +1694,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
n.findex = pos
|
||||
default:
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
|
||||
@@ -1586,6 +1711,19 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// Global object allocation is already performed in GTA.
|
||||
index = sc.sym[c.ident].index
|
||||
} else {
|
||||
if sym, exists := sc.sym[c.ident]; exists {
|
||||
if sym.typ.node != nil &&
|
||||
sym.typ.node.anc != nil {
|
||||
// for non-predeclared identifiers (struct, map, etc)
|
||||
prevDecl := n.interp.fset.Position(sym.typ.node.anc.pos)
|
||||
err = n.cfgErrorf("%s redeclared in this block\n\tprevious declaration at %v", c.ident, prevDecl)
|
||||
return
|
||||
}
|
||||
// for predeclared identifiers (int, string, etc)
|
||||
// TODO(mpl): find the exact location of the previous declaration in all cases.
|
||||
err = n.cfgErrorf("%s redeclared in this block", c.ident)
|
||||
return
|
||||
}
|
||||
index = sc.add(n.typ)
|
||||
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
|
||||
}
|
||||
@@ -1619,6 +1757,12 @@ func compDefineX(sc *scope, n *node) error {
|
||||
} else {
|
||||
types = funtype.ret
|
||||
}
|
||||
if n.child[l-1].isType(sc) {
|
||||
l--
|
||||
}
|
||||
if len(types) != l {
|
||||
return n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, src.child[0].name(), len(types))
|
||||
}
|
||||
n.gen = nop
|
||||
|
||||
case indexExpr:
|
||||
@@ -1657,13 +1801,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 {
|
||||
@@ -1703,15 +1847,19 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// FixBranch sets the branch action to the identExpr node if it is a bool
|
||||
// used in a conditional expression.
|
||||
func fixBranch(n *node) {
|
||||
switch n.anc.kind {
|
||||
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
|
||||
n.gen = branch
|
||||
case parenExpr:
|
||||
fixBranch(n.anc)
|
||||
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
|
||||
// and sets the action to branch.
|
||||
func setFNext(cond, next *node) {
|
||||
if cond.action == aNop {
|
||||
cond.action = aBranch
|
||||
cond.gen = branch
|
||||
cond.fnext = next
|
||||
}
|
||||
if cond.kind == parenExpr {
|
||||
setFNext(cond.lastChild(), next)
|
||||
return
|
||||
}
|
||||
cond.fnext = next
|
||||
}
|
||||
|
||||
// GetDefault return the index of default case clause in a switch statement, or -1.
|
||||
@@ -1799,12 +1947,22 @@ func wireChild(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) name() (s string) {
|
||||
switch {
|
||||
case n.ident != "":
|
||||
s = n.ident
|
||||
case n.action == aGetSym:
|
||||
s = n.child[0].ident + "." + n.child[1].ident
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// isInteger returns true if node type is integer, false otherwise
|
||||
func (n *node) isInteger() bool {
|
||||
if isInt(n.typ.TypeOf()) {
|
||||
return true
|
||||
}
|
||||
if n.typ.untyped && n.rval.IsValid() {
|
||||
if n.rval.IsValid() {
|
||||
t := n.rval.Type()
|
||||
if isInt(t) {
|
||||
return true
|
||||
@@ -1818,6 +1976,20 @@ 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
|
||||
}
|
||||
@@ -1827,7 +1999,7 @@ func (n *node) isNatural() bool {
|
||||
if isUint(n.typ.TypeOf()) {
|
||||
return true
|
||||
}
|
||||
if n.typ.untyped && n.rval.IsValid() {
|
||||
if n.rval.IsValid() {
|
||||
t := n.rval.Type()
|
||||
if isUint(t) {
|
||||
return true
|
||||
@@ -1845,6 +2017,23 @@ 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
|
||||
}
|
||||
@@ -1890,6 +2079,10 @@ func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
func isRecursiveField(n *node) bool {
|
||||
return isField(n) && (n.typ.recursive || n.typ.cat == ptrT && n.typ.val.recursive)
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition
|
||||
func isNewDefine(n *node, sc *scope) bool {
|
||||
if n.ident == "_" {
|
||||
@@ -1915,13 +2108,29 @@ func isMethod(n *node) bool {
|
||||
}
|
||||
|
||||
func isMapEntry(n *node) bool {
|
||||
return n.action == aGetIndex && n.child[0].typ.cat == mapT
|
||||
return n.action == aGetIndex && isMap(n.child[0].typ)
|
||||
}
|
||||
|
||||
func isCall(n *node) bool {
|
||||
return n.action == aCall || n.action == aCallSlice
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func mustReturnValue(n *node) bool {
|
||||
if len(n.child) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, f := range n.child[1].child {
|
||||
if len(f.child) > 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isRegularCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].typ.cat == funcT
|
||||
}
|
||||
@@ -2073,5 +2282,8 @@ func isValueUntyped(v reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
t := v.Type()
|
||||
if t.Implements(constVal) {
|
||||
return true
|
||||
}
|
||||
return t.String() == t.Kind().String()
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -58,10 +60,19 @@ func (n *node) cfgDot(out io.Writer) {
|
||||
fmt.Fprintf(out, "}\n")
|
||||
}
|
||||
|
||||
// dotX returns an output stream to a dot(1) co-process where to write data in .dot format
|
||||
func dotX() io.WriteCloser {
|
||||
cmd := exec.Command("dotty", "-")
|
||||
//cmd := exec.Command("dot", "-T", "xlib")
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
// 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}
|
||||
}
|
||||
fields := strings.Fields(dotCmd)
|
||||
cmd := exec.Command(fields[0], fields[1:]...)
|
||||
dotin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -71,3 +82,14 @@ func dotX() io.WriteCloser {
|
||||
}
|
||||
return dotin
|
||||
}
|
||||
|
||||
func defaultDotCmd(filePath, prefix string) string {
|
||||
dir, fileName := filepath.Split(filePath)
|
||||
ext := filepath.Ext(fileName)
|
||||
if ext == "" {
|
||||
fileName += ".dot"
|
||||
} else {
|
||||
fileName = strings.Replace(fileName, ext, ".dot", 1)
|
||||
}
|
||||
return "dot -Tdot -o" + dir + prefix + fileName
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import "reflect"
|
||||
func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
var err error
|
||||
var iotaValue int
|
||||
var revisit []*node
|
||||
|
||||
root.Walk(func(n *node) bool {
|
||||
@@ -18,9 +17,8 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
switch n.kind {
|
||||
case constDecl:
|
||||
iotaValue = 0
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
// values which may be necessary in further declarations.
|
||||
// values which may be used in further declarations.
|
||||
if _, err = interp.cfg(n, pkgID); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
@@ -47,7 +45,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
val := reflect.ValueOf(iotaValue)
|
||||
val := reflect.ValueOf(sc.iota)
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, pkgID); err2 != nil {
|
||||
// Constant value can not be computed yet.
|
||||
@@ -80,7 +78,11 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
iotaValue++
|
||||
if childPos(n) == len(n.anc.child)-1 {
|
||||
sc.iota = 0
|
||||
} else {
|
||||
sc.iota++
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -135,6 +137,7 @@ 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
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
@@ -151,9 +154,9 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
name = n.child[0].ident
|
||||
} else {
|
||||
ipath = n.child[0].rval.String()
|
||||
name = identifier.FindString(ipath)
|
||||
}
|
||||
// Try to import a binary package first, or a source package
|
||||
var pkgName string
|
||||
if interp.binPkg[ipath] != nil {
|
||||
switch name {
|
||||
case "_": // no import of symbols
|
||||
@@ -166,9 +169,13 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ, scope: sc}, rval: v}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
if name == "" {
|
||||
name = identifier.FindString(ipath)
|
||||
}
|
||||
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
|
||||
}
|
||||
} else if err = interp.importSrc(rpath, ipath); err == nil {
|
||||
} else if pkgName, err = interp.importSrc(rpath, ipath); err == nil {
|
||||
sc.types = interp.universe.types
|
||||
switch name {
|
||||
case "_": // no import of symbols
|
||||
@@ -179,6 +186,10 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
if name == "" {
|
||||
name = pkgName
|
||||
}
|
||||
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
|
||||
}
|
||||
} else {
|
||||
@@ -192,7 +203,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
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}
|
||||
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)
|
||||
} else {
|
||||
n.typ = typ
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user