Repository: goplus/gop
Pull request #615 (2020-10-07 12:01:43)

new feature: support channel
**Done**

- [x] bytecode  builder
- [x] golang builder
- [x] test unit

fix: https://github.com/goplus/gop/issues/614
feature: https://github.com/goplus/gop/issues/278

example: 
```
c := make(chan int, 10)
c <- 3
d := <-c
println(d)  // 3
```



Patch:
diff --git a/cl/ctx_check.go b/cl/ctx_check.go
index 6e8c9a24..b5ee6a37 100644
--- a/cl/ctx_check.go
+++ b/cl/ctx_check.go
@@ -84,6 +84,8 @@ func isNoExecCtxStmt(ctx *blockCtx, stmt ast.Stmt) bool {
 		return isNoExecCtxCallExpr(ctx, v.Call)
 	case *ast.DeclStmt:
 		return isNoExecCtxDeclStmt(ctx, v)
+	case *ast.SendStmt:
+		return true
 	default:
 		log.Panicln("isNoExecCtxStmt failed: unknown -", reflect.TypeOf(v))
 	}
diff --git a/cl/expr.go b/cl/expr.go
index b82c260a..f86d26a8 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -599,6 +599,17 @@ func compileUnaryExpr(ctx *blockCtx, v *ast.UnaryExpr) func() {
 				ctx.takeAddr = false
 			}
 		}
+		if v.Op == token.ARROW { // <- x
+			vx := x.(iValue)
+			if vx.Type().Kind() == reflect.Chan {
+				ret := &goValue{t: vx.Type().Elem()}
+				ctx.infer.Ret(1, ret)
+				return func() {
+					exprX()
+					ctx.out.Recv()
+				}
+			}
+		}
 	}
 	xcons, xok := x.(*constVal)
 	if xok { // op <const>
diff --git a/cl/stmt.go b/cl/stmt.go
index 4c55d7c7..7c14feb3 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -83,6 +83,8 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
 		compileGoStmt(ctx, v)
 	case *ast.DeclStmt:
 		compileDeclStmt(ctx, v)
+	case *ast.SendStmt:
+		compileSendStmt(ctx, v)
 	case *ast.EmptyStmt:
 		// do nothing
 	default:
@@ -597,6 +599,14 @@ func compileDeclStmt(ctx *blockCtx, expr *ast.DeclStmt) {
 	}
 }
 
+func compileSendStmt(ctx *blockCtx, expr *ast.SendStmt) {
+	compileExpr(ctx, expr.Chan)()
+	compileExpr(ctx, expr.Value)()
+	checkType(ctx.infer.Get(-2).(iValue).Type().Elem(), ctx.infer.Get(-1), ctx.out)
+	ctx.out.Send()
+	ctx.infer.PopN(2)
+}
+
 func compileIncDecStmt(ctx *blockCtx, expr *ast.IncDecStmt) {
 	compileExpr(ctx, expr.X)()
 	compileExprLHS(ctx, expr.X, expr.Tok)
diff --git a/exec.spec/interface.go b/exec.spec/interface.go
index 09a1bd74..3c6068a7 100644
--- a/exec.spec/interface.go
+++ b/exec.spec/interface.go
@@ -385,6 +385,12 @@ type Builder interface {
 
 	// EndBlock instr
 	EndBlock() Builder
+
+	// Send instr
+	Send() Builder
+
+	// Recv instr
+	Recv() Builder
 }
 
 // Package represents a Go+ package.
diff --git a/exec/bytecode/chan.go b/exec/bytecode/chan.go
new file mode 100644
index 00000000..ad94cccd
--- /dev/null
+++ b/exec/bytecode/chan.go
@@ -0,0 +1,33 @@
+package bytecode
+
+import (
+	"reflect"
+)
+
+// Send instr
+func (p *Builder) Send() *Builder {
+	p.code.data = append(p.code.data, opSend<<bitsOpShift)
+	return p
+}
+
+// Recv instr
+func (p *Builder) Recv() *Builder {
+	p.code.data = append(p.code.data, opRecv<<bitsOpShift)
+	return p
+}
+
+func execSend(i Instr, ctx *Context) {
+	args := ctx.GetArgs(2)
+	v := reflect.ValueOf(args[0])
+	v.Send(reflect.ValueOf(args[1]))
+	ctx.PopN(2)
+}
+
+func execRecv(i Instr, p *Context) {
+	n := len(p.data)
+	v := reflect.ValueOf(p.data[n-1])
+	x, ok := v.Recv()
+	if ok {
+		p.Ret(1, x.Interface())
+	}
+}
diff --git a/exec/bytecode/code.go b/exec/bytecode/code.go
index 005f80a4..d1896551 100644
--- a/exec/bytecode/code.go
+++ b/exec/bytecode/code.go
@@ -129,6 +129,8 @@ const (
 	opStoreField    = 48 // op(26)
 	opAddrField     = 49 // op(26)
 	opStruct        = 50 // funvArity(10) type(16)
+	opSend          = 51 // reserved(26)
+	opRecv          = 52 // reserved(26)
 )
 
 const (
@@ -219,6 +221,8 @@ var instrInfos = []InstrInfo{
 	opStoreField:    {"storeField", "", "", 26},                           // addr(26)
 	opAddrField:     {"addrField", "", "", 26},                            // addr(26)
 	opStruct:        {"struct", "funvArity", "type", (10 << 8) | 16},      // funvArity(10) type(16)
+	opSend:          {"send", "", "", 0},                                  // reserved(26)
+	opRecv:          {"recv", "", "", 0},                                  // reserved(26)
 }
 
 // -----------------------------------------------------------------------------
diff --git a/exec/bytecode/context.go b/exec/bytecode/context.go
index 723379e1..14f23711 100644
--- a/exec/bytecode/context.go
+++ b/exec/bytecode/context.go
@@ -260,6 +260,8 @@ var _execTable = [...]func(i Instr, p *Context){
 	opStoreField:    execStoreField,
 	opAddrField:     execAddrField,
 	opStruct:        execStruct,
+	opSend:          execSend,
+	opRecv:          execRecv,
 }
 
 var execTable []func(i Instr, p *Context)
diff --git a/exec/bytecode/interface.go b/exec/bytecode/interface.go
index 46301b34..1e9e4fea 100644
--- a/exec/bytecode/interface.go
+++ b/exec/bytecode/interface.go
@@ -138,6 +138,18 @@ func (p *iBuilder) Go() exec.Builder {
 	return ((*Builder)(p)).Go()
 }
 
+// Send instr
+func (p *iBuilder) Send() exec.Builder {
+	((*Builder)(p)).Send()
+	return p
+}
+
+// Recv instr
+func (p *iBuilder) Recv() exec.Builder {
+	((*Builder)(p)).Recv()
+	return p
+}
+
 // WrapIfErr instr
 func (p *iBuilder) WrapIfErr(nret int, l exec.Label) exec.Builder {
 	((*Builder)(p)).WrapIfErr(nret, l.(*Label))
diff --git a/exec/golang/interface.go b/exec/golang/interface.go
index df4cb6a4..41516709 100644
--- a/exec/golang/interface.go
+++ b/exec/golang/interface.go
@@ -172,6 +172,18 @@ func (p *iBuilder) Defer() exec.Builder {
 	return p
 }
 
+// Send instr
+func (p *iBuilder) Send() exec.Builder {
+	((*Builder)(p)).Send()
+	return p
+}
+
+// Recv instr
+func (p *iBuilder) Recv() exec.Builder {
+	((*Builder)(p)).Recv()
+	return p
+}
+
 // Go instr
 func (p *iBuilder) Go() exec.Builder {
 	((*Builder)(p)).Go()
diff --git a/exec/golang/stmt.go b/exec/golang/stmt.go
index e0cce832..fb1b9012 100644
--- a/exec/golang/stmt.go
+++ b/exec/golang/stmt.go
@@ -404,6 +404,28 @@ func (p *Builder) Defer() *Builder {
 	return p
 }
 
+// Send instr
+func (p *Builder) Send() *Builder {
+	args := p.rhs.GetArgs(2)
+	p.rhs.PopN(2)
+	stmt := &ast.SendStmt{
+		Chan:  args[0].(ast.Expr),
+		Value: args[1].(ast.Expr),
+	}
+	p.emitStmt(stmt)
+	return p
+}
+
+// Recv instr
+func (p *Builder) Recv() *Builder {
+	expr := &ast.UnaryExpr{
+		Op: token.ARROW,
+		X:  p.rhs.Get(-1).(ast.Expr),
+	}
+	p.rhs.Ret(1, expr)
+	return p
+}
+
 // Go instr
 func (p *Builder) Go() *Builder {
 	p.inDeferOrGo = callByGo
diff --git a/exec/golang/type.go b/exec/golang/type.go
index 632537f4..688c99e4 100644
--- a/exec/golang/type.go
+++ b/exec/golang/type.go
@@ -44,6 +44,11 @@ func toStructField(p *Builder, f reflect.StructField) *ast.Field {
 	return field
 }
 
+// ChanType instr
+func ChanType(p *Builder, typ reflect.Type) *ast.ChanType {
+	return &ast.ChanType{Dir: ast.SEND | ast.RECV, Value: Ident(typ.Elem().String())}
+}
+
 // MapType instr
 func MapType(p *Builder, typ reflect.Type) *ast.MapType {
 	key := Type(p, typ.Key())
@@ -162,6 +167,7 @@ func Type(p *Builder, typ reflect.Type, actualTypes ...bool) ast.Expr {
 	case reflect.Interface:
 		return InterfaceType(p, typ)
 	case reflect.Chan:
+		return ChanType(p, typ)
 	case reflect.Struct:
 		return StructType(p, typ)
 	}
diff --git a/parser/parser.go b/parser/parser.go
index bf72486a..e45ff844 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -1843,7 +1843,9 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
 		}
 		return as, isRange
 	case token.ARROW:
-		return p.parseForPhraseStmtPart(x), true
+		if mode == rangeOk {
+			return p.parseForPhraseStmtPart(x), true
+		}
 	}
 
 	if len(x) > 1 {
diff --git a/tutorial/28-channel/chan.gop b/tutorial/28-channel/chan.gop
new file mode 100644
index 00000000..3e636dc2
--- /dev/null
+++ b/tutorial/28-channel/chan.gop
@@ -0,0 +1,4 @@
+c := make(chan int, 10)
+c <- 3
+d := <-c
+println(d)


Test patch:
diff --git a/cl/stmt_test.go b/cl/stmt_test.go
index ac9a710c..34db160e 100644
--- a/cl/stmt_test.go
+++ b/cl/stmt_test.go
@@ -1740,3 +1740,16 @@ cnt := 0
 func TestBranchGotoStmt(t *testing.T) {
 	testScripts(t, "TestBranchGotoStmt", testBranchGotoClauses)
 }
+
+var testChannelClauses = map[string]testData{
+	"channel_basic": {`
+	c := make(chan int, 10)
+	c <- 3
+	d := <-c
+	println(d)	
+					`, "3\n", false},
+}
+
+func TestSendStmt(t *testing.T) {
+	testScripts(t, "TestSendStmt", testChannelClauses)
+}
diff --git a/exec/bytecode/chan_test.go b/exec/bytecode/chan_test.go
new file mode 100644
index 00000000..86cf061a
--- /dev/null
+++ b/exec/bytecode/chan_test.go
@@ -0,0 +1,55 @@
+/*
+ Copyright 2020 The GoPlus Authors (goplus.org)
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+     http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+package bytecode
+
+import (
+	"os"
+	"reflect"
+	"testing"
+)
+
+// -----------------------------------------------------------------------------
+
+func TestChan(t *testing.T) {
+	println, ok := I.FindFuncv("Println")
+	if !ok {
+		t.Fatal("FindFuncv failed: Println")
+	}
+
+	b := newBuilder()
+
+	v := NewVar(reflect.ChanOf(3, reflect.TypeOf(0)), "p")
+	i := NewVar(reflect.TypeOf(0), "i")
+	expect(t,
+		func() {
+			b.DefineVar(v)
+			b.DefineVar(i)
+			b.Push(10)
+			b.Make(reflect.ChanOf(3, reflect.TypeOf(0)), 1)
+			b.StoreVar(v)
+			b.LoadVar(v)
+			b.Push(3)
+			b.Send()
+			b.LoadVar(v)
+			b.Recv()
+			b.StoreVar(i)
+			b.LoadVar(i)
+			b.CallGoFuncv(println, 1, 1)
+			code := b.Resolve()
+			code.(*Code).Dump(os.Stderr)
+			ctx := NewContext(code)
+			ctx.Exec(0, code.Len())
+		},
+		"3\n",
+	)
+}
diff --git a/exec/golang/testdata/32.channel/chan.gop b/exec/golang/testdata/32.channel/chan.gop
new file mode 100644
index 00000000..3e636dc2
--- /dev/null
+++ b/exec/golang/testdata/32.channel/chan.gop
@@ -0,0 +1,4 @@
+c := make(chan int, 10)
+c <- 3
+d := <-c
+println(d)