Repository: goplus/gop
Pull request #617 (2020-10-12 01:11:49)

cl, bytecode: support complex goinstr
goinstr
```
complex(1,2) => 1+2i
real(1+2i) => 1
imag(1+2i) => 2
```


Patch:
diff --git a/cl/goinstr.go b/cl/goinstr.go
index 55db8bd5..a5b1e951 100644
--- a/cl/goinstr.go
+++ b/cl/goinstr.go
@@ -21,6 +21,7 @@ import (
 	"reflect"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/ast/spec"
 	"github.com/goplus/gop/exec.spec"
 )
 
@@ -280,7 +281,90 @@ func igoComplex(ctx *blockCtx, v *ast.CallExpr, ct callType) func() {
 	if ct != callExpr {
 		log.Panicf("%s discards result of %s\n", gCallTypes[ct], ctx.code(v))
 	}
-	panic("todo")
+	if n := len(v.Args); n != 2 {
+		if n < 2 {
+			log.Panicln("not enough arguments in call to", ctx.code(v))
+		} else {
+			log.Panicln("too many arguments in call to", ctx.code(v))
+		}
+	}
+	xExpr := compileExpr(ctx, v.Args[0])
+	yExpr := compileExpr(ctx, v.Args[1])
+	xin := ctx.infer.Get(-1).(iValue)
+	yin := ctx.infer.Get(-2).(iValue)
+	xkind := xin.Kind()
+	ykind := yin.Kind()
+	switch xkind {
+	case spec.ConstUnboundInt, spec.ConstUnboundFloat, reflect.Float32, reflect.Float64:
+	default:
+		log.Panicf("invalid operation: %v (arguments have type %v, expected floating-point)\n", ctx.code(v), xkind)
+	}
+	switch ykind {
+	case spec.ConstUnboundInt, spec.ConstUnboundFloat, reflect.Float32, reflect.Float64:
+	default:
+		log.Panicf("invalid operation: %v (arguments have type %v, expected floating-point)\n", ctx.code(v), ykind)
+	}
+	if (xkind == reflect.Float32 && ykind == reflect.Float64) ||
+		(xkind == reflect.Float64 && ykind == reflect.Float32) {
+		log.Panicf("invalid operation: %v) (mismatched types %v and %v)", ctx.code(v), ykind, xkind)
+	}
+	var elem reflect.Type
+	var typ reflect.Type
+	if xkind == reflect.Float32 || ykind == reflect.Float32 {
+		elem = exec.TyFloat32
+		typ = exec.TyComplex64
+	} else {
+		elem = exec.TyFloat64
+		typ = exec.TyComplex128
+	}
+	ctx.infer.Ret(2, &goValue{typ})
+	return func() {
+		xExpr()
+		yExpr()
+		if c, ok := xin.(*constVal); ok {
+			c.bound(elem, ctx.out)
+		}
+		if c, ok := yin.(*constVal); ok {
+			c.bound(elem, ctx.out)
+		}
+		ctx.out.GoBuiltin(typ, exec.GobComplex)
+	}
+}
+
+// func real/imag(ComplexType) func
+func igoRealOrImag(ctx *blockCtx, v *ast.CallExpr, op exec.GoBuiltin) func() {
+	if n := len(v.Args); n != 1 {
+		if n == 0 {
+			log.Panicf("missing argument to %v: %v", op, ctx.code(v))
+		} else {
+			log.Panicf("too many arguments to %v: %v", op, ctx.code(v))
+		}
+	}
+	expr := compileExpr(ctx, v.Args[0])
+	in := ctx.infer.Get(-1).(iValue)
+	kind := in.Kind()
+	switch kind {
+	case spec.ConstUnboundInt, spec.ConstUnboundFloat, spec.ConstUnboundComplex, reflect.Complex64, reflect.Complex128:
+	default:
+		log.Panicf("invalid argument %v (type %v) for %v\n", ctx.code(v.Args[0]), kind, op)
+	}
+	var ctyp reflect.Type
+	var typ reflect.Type
+	if kind == reflect.Complex64 {
+		ctyp = exec.TyComplex64
+		typ = exec.TyFloat32
+	} else {
+		ctyp = exec.TyComplex128
+		typ = exec.TyFloat64
+	}
+	ctx.infer.Ret(1, &goValue{typ})
+	return func() {
+		expr()
+		if c, ok := in.(*constVal); ok {
+			c.bound(ctyp, ctx.out)
+		}
+		ctx.out.GoBuiltin(typ, op)
+	}
 }
 
 // func real(c ComplexType) FloatType
@@ -288,7 +372,7 @@ func igoReal(ctx *blockCtx, v *ast.CallExpr, ct callType) func() {
 	if ct != callExpr {
 		log.Panicf("%s discards result of %s\n", gCallTypes[ct], ctx.code(v))
 	}
-	panic("todo")
+	return igoRealOrImag(ctx, v, exec.GobReal)
 }
 
 // func imag(c ComplexType) FloatType
@@ -296,7 +380,7 @@ func igoImag(ctx *blockCtx, v *ast.CallExpr, ct callType) func() {
 	if ct != callExpr {
 		log.Panicf("%s discards result of %s\n", gCallTypes[ct], ctx.code(v))
 	}
-	panic("todo")
+	return igoRealOrImag(ctx, v, exec.GobImag)
 }
 
 // func close(c chan<- Type)
diff --git a/exec/bytecode/var.go b/exec/bytecode/var.go
index 300d505a..8fa2a576 100644
--- a/exec/bytecode/var.go
+++ b/exec/bytecode/var.go
@@ -120,7 +120,8 @@ func execAddrOp(i Instr, p *Context) {
 }
 
 func execGoBuiltin(i Instr, p *Context) {
-	op := i & bitsOperand
+	op := (i & bitsOperand) >> bitsKind
+	kind := Kind(i & ((1 << bitsKind) - 1))
 	n := len(p.data)
 	switch exec.GoBuiltin(op) {
 	case GobLen:
@@ -139,8 +140,26 @@ func execGoBuiltin(i Instr, p *Context) {
 		v := reflect.ValueOf(p.data[n-2])
 		v.SetMapIndex(key, reflect.Value{})
 		p.PopN(2)
+	case GobComplex:
+		if kind == reflect.Complex64 {
+			p.Ret(2, complex(p.data[n-2].(float32), p.data[n-1].(float32)))
+		} else {
+			p.Ret(2, complex(p.data[n-2].(float64), p.data[n-1].(float64)))
+		}
+	case GobImag:
+		if kind == reflect.Float32 {
+			p.data[n-1] = imag(p.data[n-1].(complex64))
+		} else {
+			p.data[n-1] = imag(p.data[n-1].(complex128))
+		}
+	case GobReal:
+		if kind == reflect.Float32 {
+			p.data[n-1] = real(p.data[n-1].(complex64))
+		} else {
+			p.data[n-1] = real(p.data[n-1].(complex128))
+		}
 	default:
-		log.Panicln("execGoBuiltin: todo -", i)
+		log.Panicln("execGoBuiltin: todo -", op)
 	}
 }
 
@@ -400,7 +419,8 @@ func (p *Builder) AddrOp(kind Kind, op AddrOperator) *Builder {
 
 // GoBuiltin instr
 func (p *Builder) GoBuiltin(typ reflect.Type, op GoBuiltin) *Builder {
-	p.code.data = append(p.code.data, (opGoBuiltin<<bitsOpShift)|uint32(op))
+	i := (int(op) << bitsKind) | int(typ.Kind())
+	p.code.data = append(p.code.data, (opGoBuiltin<<bitsOpShift)|uint32(i))
 	return p
 }
 


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 95334783..2b12a27a 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -808,6 +808,112 @@ func _TestIsNoExecCtx(t *testing.T) {
 	println("values:", fns[0](), fns[1](), fns[2]())`, "values: 3 15 777\n")
 }
 
+func TestComplex(t *testing.T) {
+	cltest.Expect(t, `
+	c := complex(1,2)
+	printf("%v %T\n",c,c)
+	`, "(1+2i) complex128\n")
+	cltest.Expect(t, `
+	c := complex(float64(1),2)
+	printf("%v %T\n",c,c)
+	`, "(1+2i) complex128\n")
+	cltest.Expect(t, `
+	c := complex(float32(1),2)
+	printf("%v %T\n",c,c)
+	`, "(1+2i) complex64\n")
+	cltest.Expect(t, `
+	func test() float64 { return 1 }
+	c := complex(test(),2)
+	printf("%v %T\n",c,c)
+	`, "(1+2i) complex128\n")
+	cltest.Expect(t, `
+	func test() float32 { return 1 }
+	c := complex(test(),2)
+	printf("%v %T\n",c,c)
+	`, "(1+2i) complex64\n")
+
+	cltest.Expect(t, `
+	c := real(1+2i)
+	printf("%v %T\n",c,c)
+	`, "1 float64\n")
+	cltest.Expect(t, `
+	c := real(complex128(1+2i))
+	printf("%v %T\n",c,c)
+	`, "1 float64\n")
+	cltest.Expect(t, `
+	c := real(complex64(1+2i))
+	printf("%v %T\n",c,c)
+	`, "1 float32\n")
+	cltest.Expect(t, `
+	c := real(complex(1,2))
+	printf("%v %T\n",c,c)
+	`, "1 float64\n")
+	cltest.Expect(t, `
+	c := real(complex(1,float32(2)))
+	printf("%v %T\n",c,c)
+	`, "1 float32\n")
+
+	cltest.Expect(t, `
+	c := imag(1+2i)
+	printf("%v %T\n",c,c)
+	`, "2 float64\n")
+	cltest.Expect(t, `
+	c := imag(complex128(1+2i))
+	printf("%v %T\n",c,c)
+	`, "2 float64\n")
+	cltest.Expect(t, `
+	c := imag(complex64(1+2i))
+	printf("%v %T\n",c,c)
+	`, "2 float32\n")
+	cltest.Expect(t, `
+	c := imag(complex(1,2))
+	printf("%v %T\n",c,c)
+	`, "2 float64\n")
+	cltest.Expect(t, `
+	c := imag(complex(float32(1),2))
+	printf("%v %T\n",c,c)
+	`, "2 float32\n")
+}
+
+func TestBadComplex(t *testing.T) {
+	cltest.Expect(t, `
+	complex(1)
+	`, "", nil)
+	cltest.Expect(t, `
+	complex(1,2,3)
+	`, "", nil)
+	cltest.Expect(t, `
+	complex(float32(1),float64(2))
+	`, "", nil)
+	cltest.Expect(t, `
+	func test() int { return 100 }
+	complex(test(),2)
+	`, "", nil)
+	cltest.Expect(t, `
+	complex(1,int(2))
+	`, "", nil)
+
+	cltest.Expect(t, `
+	real()
+	`, "", nil)
+	cltest.Expect(t, `
+	real(1,2)
+	`, "", nil)
+	cltest.Expect(t, `
+	real(int(1))
+	`, "", nil)
+
+	cltest.Expect(t, `
+	imag()
+	`, "", nil)
+	cltest.Expect(t, `
+	imag(1,2)
+	`, "", nil)
+	cltest.Expect(t, `
+	imag(int(1))
+	`, "", nil)
+}
+
 func TestResult(t *testing.T) {
 	cltest.Expect(t, `
 	import "fmt"
diff --git a/exec/bytecode/const_test.go b/exec/bytecode/const_test.go
index d20e7f05..45c55f4c 100644
--- a/exec/bytecode/const_test.go
+++ b/exec/bytecode/const_test.go
@@ -119,7 +119,6 @@ func TestReserve(t *testing.T) {
 	}
 	i := b.Interface()
 	_ = i.EndStmt(nil, nil).StartStmt(nil)
-	_ = i.GoBuiltin(nil, 0).Store(0)
 	g := i.Pop(0).(*iBuilder).GetPackage()
 	_ = g.GetGoFuncInfo(0)
 	_ = g.GetGoFuncvInfo(0)
diff --git a/exec/bytecode/goinstr_lstcompr_test.go b/exec/bytecode/goinstr_lstcompr_test.go
index 3d492e1f..8742cf84 100644
--- a/exec/bytecode/goinstr_lstcompr_test.go
+++ b/exec/bytecode/goinstr_lstcompr_test.go
@@ -736,7 +736,7 @@ func TestCopy(t *testing.T) {
 		StoreVar(b).
 		LoadVar(b).
 		LoadVar(a).
-		GoBuiltin(nil, GobCopy).
+		GoBuiltin(reflect.SliceOf(TyInt), GobCopy).
 		LoadVar(b).
 		Resolve()
 
@@ -759,7 +759,7 @@ func TestCopy2(t *testing.T) {
 		StoreVar(a).
 		LoadVar(a).
 		Push("hello").
-		GoBuiltin(nil, GobCopy).
+		GoBuiltin(reflect.SliceOf(TyByte), GobCopy).
 		LoadVar(a).
 		Resolve()
 
@@ -771,3 +771,77 @@ func TestCopy2(t *testing.T) {
 		t.Fatal("copy failed")
 	}
 }
+
+func TestComplex1(t *testing.T) {
+	code := newBuilder().
+		Push(float64(1)).
+		Push(float64(2)).
+		GoBuiltin(exec.TyComplex128, exec.GobComplex).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != complex128(1+2i) {
+		t.Fatal("complex(1,2)`, ret =", v)
+	}
+}
+
+func TestComplex2(t *testing.T) {
+	code := newBuilder().
+		Push(float32(1)).
+		Push(float32(2)).
+		GoBuiltin(exec.TyComplex64, exec.GobComplex).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != complex64(1+2i) {
+		t.Fatal("complex(1,2)`, ret =", v)
+	}
+}
+
+func TestReal1(t *testing.T) {
+	code := newBuilder().
+		Push(complex128(1+2i)).
+		GoBuiltin(exec.TyFloat64, exec.GobReal).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != real(complex128(1+2i)) || reflect.ValueOf(v).Kind() != reflect.Float64 {
+		t.Fatal("real(1+2i)`, ret =", v)
+	}
+}
+
+func TestReal2(t *testing.T) {
+	code := newBuilder().
+		Push(complex64(1+2i)).
+		GoBuiltin(exec.TyFloat32, exec.GobReal).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != real(complex64(1+2i)) || reflect.ValueOf(v).Kind() != reflect.Float32 {
+		t.Fatal("real(complex64(1+2i))`, ret =", v)
+	}
+}
+
+func TestImag1(t *testing.T) {
+	code := newBuilder().
+		Push(complex128(1+2i)).
+		GoBuiltin(exec.TyFloat64, exec.GobImag).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != imag(complex128(1+2i)) || reflect.ValueOf(v).Kind() != reflect.Float64 {
+		t.Fatal("imag(1+2i)`, ret =", v)
+	}
+}
+
+func TestImag2(t *testing.T) {
+	code := newBuilder().
+		Push(complex64(1+2i)).
+		GoBuiltin(exec.TyFloat32, exec.GobImag).
+		Resolve()
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != imag(complex64(1+2i)) || reflect.ValueOf(v).Kind() != reflect.Float32 {
+		t.Fatal("imag(complex64(1+2i))`, ret =", v)
+	}
+}