Compare commits

..

6 Commits

16 changed files with 295 additions and 39 deletions

View File

@@ -13,7 +13,12 @@ discussions.
Once the proposal is approved, a Pull Request can be opened. If you want
to provide early visibility to reviewers, create a [Draft Pull Request].
We will also require you to sign the [Containous Contributor License Agreement]
after you submit your first pull request to this project. The link to sign the
agreement will be presented to you in the web interface of the pull request.
[Issues]: https://github.com/containous/yaegi/issues
[Pull Requests]: https://github.com/containous/yaegi/issues
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi

13
_test/bin2.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Abs(-5))
}
// Output:
// 5

12
_test/nil1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var a error = nil
if a == nil || a.Error() == "nil" {
println("a is nil")
}
}
// Output:
// a is nil

9
_test/or0.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(c || !c)
}
// Output:
// true

9
_test/or1.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(!c || c)
}
// Output:
// true

View File

@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
}
}
{{end}}
{{range $name, $op := .Unary}}
func {{$name}}Const(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
{{- if $op.Bool}}
n.rval.SetBool({{$op.Name}} v.Bool())
{{- else}}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt({{$op.Name}} v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint({{$op.Name}} v.Uint())
{{- if $op.Float}}
case reflect.Float32, reflect.Float64:
n.rval.SetFloat({{$op.Name}} v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex({{$op.Name}} v.Complex())
{{- end}}
}
{{- end}}
}
{{end}}
{{range $name, $op := .Comparison}}
func {{$name}}(n *node) {
tnext := getExec(n.tnext)
@@ -793,6 +817,7 @@ type Op struct {
Float bool // true if operator applies to float
Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
}
func main() {
@@ -805,17 +830,17 @@ func main() {
b := &bytes.Buffer{}
data := map[string]interface{}{
"Arithmetic": map[string]Op{
"add": {"+", true, true, true, false},
"sub": {"-", false, true, true, false},
"mul": {"*", false, true, true, false},
"quo": {"/", false, true, true, false},
"rem": {"%", false, false, false, false},
"shl": {"<<", false, false, false, true},
"shr": {">>", false, false, false, true},
"and": {"&", false, false, false, false},
"or": {"|", false, false, false, false},
"xor": {"^", false, false, false, false},
"andNot": {"&^", false, false, false, false},
"add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false, false},
},
"IncDec": map[string]Op{
"inc": {Name: "+"},
@@ -829,6 +854,12 @@ func main() {
"lowerEqual": {Name: "<=", Complex: false},
"notEqual": {Name: "!=", Complex: true},
},
"Unary": map[string]Op{
"not": {Name: "!", Float: false, Bool: true},
"neg": {Name: "-", Float: true, Bool: false},
"pos": {Name: "+", Float: true, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false},
},
}
if err = parse.Execute(b, data); err != nil {
log.Fatal(err)

View File

@@ -199,6 +199,7 @@ const (
aAndAssign
aAndNot
aAndNotAssign
aBitNot
aCall
aCase
aCompositeLit
@@ -217,11 +218,12 @@ const (
aMethod
aMul
aMulAssign
aNegate
aNeg
aNot
aNotEqual
aOr
aOrAssign
aPos
aQuo
aQuoAssign
aRange
@@ -255,6 +257,7 @@ var actions = [...]string{
aAndAssign: "&=",
aAndNot: "&^",
aAndNotAssign: "&^=",
aBitNot: "^",
aCall: "call",
aCase: "case",
aCompositeLit: "compositeLit",
@@ -271,11 +274,12 @@ var actions = [...]string{
aMethod: "Method",
aMul: "*",
aMulAssign: "*=",
aNegate: "-",
aNeg: "-",
aNot: "!",
aNotEqual: "!=",
aOr: "|",
aOrAssign: "|=",
aPos: "+",
aQuo: "/",
aQuoAssign: "/=",
aRange: "range",
@@ -771,6 +775,8 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
var kind = unaryExpr
var act action
switch a.Op {
case token.ADD:
act = aPos
case token.AND:
kind = addressExpr
act = aAddr
@@ -779,7 +785,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
case token.NOT:
act = aNot
case token.SUB:
act = aNegate
act = aNeg
case token.XOR:
act = aBitNot
}
st.push(addChild(&root, anc, pos, kind, act), nod)

View File

@@ -24,6 +24,10 @@ var constOp = map[action]func(*node){
aShr: shrConst,
aAndNot: andNotConst,
aXor: xorConst,
aNot: notConst,
aBitNot: bitNotConst,
aNeg: negConst,
aPos: posConst,
}
var constBltn = map[string]func(*node){
@@ -1019,6 +1023,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr:
n.start = n.child[0].start
@@ -1027,6 +1034,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr:
wireChild(n)
@@ -1375,16 +1385,60 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case unaryExpr:
wireChild(n)
n.typ = n.child[0].typ
if n.action == aRecv {
switch n.action {
case aRecv:
// Channel receive operation: set type to the channel data type
if n.typ.cat == valueT {
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
} else {
n.typ = n.typ.val
}
case aBitNot:
if !isInt(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '^' operator")
return
}
case aNot:
if !isBool(n.typ) {
err = n.cfgErrorf("illegal operand type for '!' operator")
return
}
case aNeg, aPos:
if !isNumber(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '%v' operator", n.action)
return
}
}
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
n.findex = sc.add(n.typ)
if n.child[0].rval.IsValid() && constOp[n.action] != nil {
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.typ.TypeOf() // init reflect type
constOp[n.action](n)
}
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = -1
case n.anc.kind == assignStmt && n.anc.action == aAssign:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
default:
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.findex = sc.add(n.typ)
}
case valueSpec:
n.gen = reset

View File

@@ -4,6 +4,18 @@ Package interp provides a complete Go interpreter.
For the Go language itself, refer to the official Go specification
https://golang.org/ref/spec.
Importing packages
Packages can be imported in source or binary form, using the standard
Go import statement. In source form, packages are searched first in the
vendor directory, the preferred way to store source dependencies. If not
found in vendor, sources modules will be searched in GOPATH. Go modules
are not supported yet by yaegi.
Binary form packages are compiled and linked with the interpreter
executable, and exposed to scripts with the Use method. The goexports
command can be used to generate package wrappers.
Custom build tags
Custom build tags allow to control which files in imported source

View File

@@ -38,7 +38,7 @@ func (n *node) astDot(out io.Writer, name string) {
func (n *node) cfgDot(out io.Writer) {
fmt.Fprintf(out, "digraph cfg {\n")
n.Walk(nil, func(n *node) {
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil {
if n.kind == basicLit || n.tnext == nil {
return
}
var label string

View File

@@ -47,11 +47,17 @@ func TestEvalArithmetic(t *testing.T) {
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1.0 << 1", res: "2"},
{desc: "shl_IF2", src: "1.0 << 1", res: "2"},
{desc: "shr_II", src: "1 >> 8", res: "0"},
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "neg_I", src: "-2", res: "-2"},
{desc: "pos_I", src: "+2", res: "2"},
{desc: "bitnot_I", src: "^2", res: "-3"},
{desc: "bitnot_F", src: "^0.2", err: "1:28: illegal operand type for '^' operator"},
{desc: "not_B", src: "!false", res: "true"},
{desc: "not_I", src: "!0", err: "1:28: illegal operand type for '!' operator"},
})
}

View File

@@ -1864,6 +1864,57 @@ func inc(n *node) {
}
}
func bitNotConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(^v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(^v.Uint())
}
}
func negConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(-v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(-v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(-v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(-v.Complex())
}
}
func notConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
n.rval.SetBool(!v.Bool())
}
func posConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(+v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(+v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(+v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(+v.Complex())
}
}
func equal(n *node) {
tnext := getExec(n.tnext)
dest := genValue(n)

View File

@@ -24,6 +24,7 @@ var builtin = [...]bltnGenerator{
aAndAssign: andAssign,
aAndNot: andNot,
aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call,
aCase: _case,
aCompositeLit: arrayLit,
@@ -40,11 +41,12 @@ var builtin = [...]bltnGenerator{
aLowerEqual: lowerEqual,
aMul: mul,
aMulAssign: mulAssign,
aNegate: negate,
aNeg: neg,
aNot: not,
aNotEqual: notEqual,
aOr: or,
aOrAssign: orAssign,
aPos: pos,
aQuo: quo,
aQuoAssign: quoAssign,
aRange: _range,
@@ -340,21 +342,24 @@ func assign(n *node) {
}
func not(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
tnext := getExec(n.tnext)
i := n.findex
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if !value(f).Bool() {
f.data[i].SetBool(true)
return tnext
}
f.data[i].SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).Bool())
dest(f).SetBool(!value(f).Bool())
return tnext
}
}
@@ -658,7 +663,7 @@ func call(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
var argType reflect.Type
if variadic >= 0 && i >= variadic {
argType = n.child[0].typ.arg[variadic].val.TypeOf()
@@ -843,7 +848,7 @@ func callBin(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type
if variadic >= 0 && i >= variadic {
@@ -1246,7 +1251,7 @@ func getIndexSeqMethod(n *node) {
}
}
func negate(n *node) {
func neg(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
@@ -1270,6 +1275,37 @@ func negate(n *node) {
}
}
func pos(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
dest(f).Set(value(f))
return next
}
}
func bitNot(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
typ := n.typ.TypeOf()
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.exec = func(f *frame) bltn {
dest(f).SetInt(^value(f).Int())
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.exec = func(f *frame) bltn {
dest(f).SetUint(^value(f).Uint())
return next
}
}
}
func land(n *node) {
value0 := genValue(n.child[0])
value1 := genValue(n.child[1])
@@ -1946,7 +1982,7 @@ func _close(n *node) {
}
func _complex(n *node) {
i := n.findex
dest := genValue(n)
c1, c2 := n.child[1], n.child[2]
convertLiteralValue(c1, floatType)
convertLiteralValue(c2, floatType)
@@ -1956,38 +1992,38 @@ func _complex(n *node) {
if typ := n.typ.TypeOf(); isComplex(typ) {
n.exec = func(f *frame) bltn {
f.data[i].SetComplex(complex(value0(f).Float(), value1(f).Float()))
dest(f).SetComplex(complex(value0(f).Float(), value1(f).Float()))
return next
}
} else {
// Not a complex type: ignore imaginary part
n.exec = func(f *frame) bltn {
f.data[i].Set(value0(f).Convert(typ))
dest(f).Set(value0(f).Convert(typ))
return next
}
}
}
func _imag(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(imag(value(f).Complex()))
dest(f).SetFloat(imag(value(f).Complex()))
return next
}
}
func _real(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(real(value(f).Complex()))
dest(f).SetFloat(real(value(f).Complex()))
return next
}
}
@@ -2005,12 +2041,12 @@ func _delete(n *node) {
}
func _len(n *node) {
i := n.findex
dest := genValue(n)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetInt(int64(value(f).Len()))
dest(f).SetInt(int64(value(f).Len()))
return next
}
}
@@ -2222,13 +2258,16 @@ func recv2(n *node) {
}
func convertLiteralValue(n *node, t reflect.Type) {
if n.kind != basicLit || t == nil || t.Kind() == reflect.Interface {
// Skip non-constant values, undefined target type or interface target type.
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
return
}
if n.rval.IsValid() {
// Convert constant value to target type.
n.rval = n.rval.Convert(t)
} else {
n.rval = reflect.New(t).Elem() // convert to type nil value
// Create a zero value of target type.
n.rval = reflect.New(t).Elem()
}
}
@@ -2420,19 +2459,21 @@ func isNil(n *node) {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(value(f).IsNil())
dest(f).SetBool(value(f).IsNil())
return tnext
}
}
@@ -2446,19 +2487,21 @@ func isNotNil(n *node) {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(true)
return tnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).IsNil())
dest(f).SetBool(!value(f).IsNil())
return tnext
}
}

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package stdlib provides wrappers of standard library packages to be imported natively in Yaegi.
package stdlib
import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package syscall provide wrapper of standard library syscall package for native import in Yaegi.
package syscall
import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package unsafe provides wrapper of standard library unsafe package to be imported natively in Yaegi.
package unsafe
import "reflect"