Repository: goplus/gop
Pull request #2111 (2025-02-21 22:56:32)

#2107 extend ast.SendStmt


Patch:
diff --git a/README.md b/README.md
index 0ebc4653..09712e05 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,6 @@ Code style is just the first step. We have made many efforts to make the code mo
 | fmt.Println("Hi") | echo("Hi") | More builtin functions: It simplifies the expression of the most common tasks |
 | fmt.Println("Hi") | echo "Hi" | Command-line style code: It reduces the number of parentheses in the code as much as possible, making it closer to natural language |
 | a := []int{1, 2, 3} | a := [1, 2, 3] | List literals |
-| a = append(a, 4) | a <- 4 | Append values to a list |
 | a := map[string]int{<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | a := {<br>&nbsp;&nbsp;&nbsp;&nbsp;"Monday": 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;"Tuesday": 2,<br>} | Mapping literals |
 | OnStart(func() {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>}) | onStart => {<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>} | Lambda expressions |
 | type Rect struct {<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>}<br><br>func (this *Rect) Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return this.Width * this.Height<br>} | var (<br>&nbsp;&nbsp;&nbsp;&nbsp;Width&nbsp; float64<br>&nbsp;&nbsp;&nbsp;&nbsp;Height float64<br>)<br><br>func Area() float64 { <br>&nbsp;&nbsp;&nbsp;&nbsp;return Width * Height<br>} | [Go+ Classfiles](doc/classfile.md): We can express OOP with global variables and functions. |
diff --git a/ast/ast.go b/ast/ast.go
index 1a87ed9b..5496a164 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -604,13 +604,6 @@ type (
 		X Expr // expression
 	}
 
-	// A SendStmt node represents a send statement.
-	SendStmt struct {
-		Chan  Expr
-		Arrow token.Pos // position of "<-"
-		Value Expr
-	}
-
 	// An IncDecStmt node represents an increment or decrement statement.
 	IncDecStmt struct {
 		X      Expr
@@ -815,9 +808,6 @@ func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }
 // End returns position of first character immediately after the node.
 func (s *ExprStmt) End() token.Pos { return s.X.End() }
 
-// End returns position of first character immediately after the node.
-func (s *SendStmt) End() token.Pos { return s.Value.End() }
-
 // End returns position of first character immediately after the node.
 func (s *IncDecStmt) End() token.Pos {
 	return s.TokPos + 2 /* len("++") */
diff --git a/ast/ast_gop.go b/ast/ast_gop.go
index 87e86b6a..64fe4a85 100644
--- a/ast/ast_gop.go
+++ b/ast/ast_gop.go
@@ -282,6 +282,41 @@ func (*LambdaExpr2) exprNode() {}
 
 // -----------------------------------------------------------------------------
 
+// A RangeExpr node represents a range expression.
+type RangeExpr struct {
+	First  Expr      // start of composite elements; or nil
+	To     token.Pos // position of ":"
+	Last   Expr      // end of composite elements
+	Colon2 token.Pos // position of ":" or token.NoPos
+	Expr3  Expr      // step (or max) of composite elements; or nil
+}
+
+// Pos - position of first character belonging to the node.
+func (p *RangeExpr) Pos() token.Pos {
+	if p.First != nil {
+		return p.First.Pos()
+	}
+	return p.To
+}
+
+// End - position of first character immediately after the node.
+func (p *RangeExpr) End() token.Pos {
+	if p.Expr3 != nil {
+		return p.Expr3.End()
+	}
+	if p.Colon2 != token.NoPos {
+		return p.Colon2 + 1
+	}
+	if p.Last != nil {
+		return p.Last.End()
+	}
+	return p.To + 1
+}
+
+func (*RangeExpr) exprNode() {}
+
+// -----------------------------------------------------------------------------
+
 // ForPhrase represents `for k, v <- container if init; cond` phrase.
 type ForPhrase struct {
 	For        token.Pos // position of "for" keyword
@@ -354,37 +389,21 @@ func (*ForPhraseStmt) stmtNode() {}
 
 // -----------------------------------------------------------------------------
 
-// A RangeExpr node represents a range expression.
-type RangeExpr struct {
-	First  Expr      // start of composite elements; or nil
-	To     token.Pos // position of ":"
-	Last   Expr      // end of composite elements
-	Colon2 token.Pos // position of ":" or token.NoPos
-	Expr3  Expr      // step (or max) of composite elements; or nil
-}
-
-// Pos - position of first character belonging to the node.
-func (p *RangeExpr) Pos() token.Pos {
-	if p.First != nil {
-		return p.First.Pos()
-	}
-	return p.To
+// A SendStmt node represents a send statement.
+type SendStmt struct {
+	Chan     Expr
+	Arrow    token.Pos // position of "<-"
+	Values   []Expr    // len(Values) must > 0
+	Ellipsis token.Pos // position of "..."
 }
 
-// End - position of first character immediately after the node.
-func (p *RangeExpr) End() token.Pos {
-	if p.Expr3 != nil {
-		return p.Expr3.End()
-	}
-	if p.Colon2 != token.NoPos {
-		return p.Colon2 + 1
-	}
-	if p.Last != nil {
-		return p.Last.End()
+// End returns position of first character immediately after the node.
+func (s *SendStmt) End() token.Pos {
+	if s.Ellipsis != token.NoPos {
+		return s.Ellipsis + 3
 	}
-	return p.To + 1
+	vals := s.Values
+	return vals[len(vals)-1].End()
 }
 
-func (*RangeExpr) exprNode() {}
-
 // -----------------------------------------------------------------------------
diff --git a/ast/walk.go b/ast/walk.go
index 0cea28fe..88a21fd5 100644
--- a/ast/walk.go
+++ b/ast/walk.go
@@ -223,7 +223,9 @@ func Walk(v Visitor, node Node) {
 
 	case *SendStmt:
 		Walk(v, n.Chan)
-		Walk(v, n.Value)
+		for _, val := range n.Values {
+			Walk(v, val)
+		}
 
 	case *IncDecStmt:
 		Walk(v, n.X)
diff --git a/cl/stmt.go b/cl/stmt.go
index b2d4ff12..b2cc9817 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -260,19 +260,21 @@ func isAppendable(x ast.Expr) bool {
 
 func compileSendStmt(ctx *blockCtx, expr *ast.SendStmt) {
 	cb := ctx.cb
-	ch, v := expr.Chan, expr.Value
+	ch, vals := expr.Chan, expr.Values
 	stk := cb.InternalStack()
-	if isAppendable(ch) { // a <- v (issue #2107)
+	if isAppendable(ch) { // a <- v1, v2, v3 (issue #2107)
 		compileExpr(ctx, ch)
 		a := stk.Get(-1)
 		t := a.Type.Underlying()
-		if _, ok := t.(*types.Slice); ok { // a = append(a, x)
+		if _, ok := t.(*types.Slice); ok { // a = append(a, v1, v2, v3)
 			stk.Pop()
 			compileExprLHS(ctx, ch)
 			cb.Val(ctx.pkg.Builtin().Ref("append"))
 			stk.Push(a)
-			compileExpr(ctx, v)
-			cb.CallWith(2, 0, expr).AssignWith(1, 1, expr)
+			for _, v := range vals {
+				compileExpr(ctx, v)
+			}
+			cb.CallWith(len(vals)+1, 0, expr).AssignWith(1, 1, expr)
 			return
 		}
 		goto normal
@@ -280,7 +282,7 @@ func compileSendStmt(ctx *blockCtx, expr *ast.SendStmt) {
 	compileExpr(ctx, ch)
 
 normal:
-	compileExpr(ctx, v)
+	compileExpr(ctx, vals[0]) // TODO(xsw): issue #2107
 	ctx.cb.Send()
 }
 
diff --git a/parser/parser.go b/parser/parser.go
index a9850690..2178c0c4 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -2763,7 +2763,8 @@ func (p *parser) parseSimpleStmtEx(mode int, allowCmd, allowRangeExpr bool) (ast
 		arrow := p.pos
 		p.next()
 		y := p.parseRHS()
-		return &ast.SendStmt{Chan: x[0], Arrow: arrow, Value: y}, false
+		// TODO(xsw): https://github.com/goplus/gop/issues/2107
+		return &ast.SendStmt{Chan: x[0], Arrow: arrow, Values: []ast.Expr{y}}, false
 
 	case token.INC, token.DEC:
 		// increment or decrement
@@ -3183,7 +3184,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
 			arrow := p.pos
 			p.next()
 			rhs := p.parseRHS()
-			comm = &ast.SendStmt{Chan: lhs[0], Arrow: arrow, Value: rhs}
+			comm = &ast.SendStmt{Chan: lhs[0], Arrow: arrow, Values: []ast.Expr{rhs}}
 		} else {
 			// RecvStmt
 			if tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE {
diff --git a/printer/nodes.go b/printer/nodes.go
index 7e7c2a08..9899f5ca 100644
--- a/printer/nodes.go
+++ b/printer/nodes.go
@@ -1402,7 +1402,15 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
 		const depth = 1
 		p.expr0(s.Chan, depth)
 		p.print(blank, s.Arrow, token.ARROW, blank)
-		p.expr0(s.Value, depth)
+		for i, val := range s.Values {
+			if i > 0 {
+				p.print(token.COMMA, blank)
+			}
+			p.expr0(val, depth)
+		}
+		if s.Ellipsis.IsValid() {
+			p.print(s.Ellipsis, token.ELLIPSIS)
+		}
 
 	case *ast.IncDecStmt:
 		const depth = 1
diff --git a/x/format/stmt_expr_or_type.go b/x/format/stmt_expr_or_type.go
index b5e13c65..d8fc5d89 100644
--- a/x/format/stmt_expr_or_type.go
+++ b/x/format/stmt_expr_or_type.go
@@ -248,7 +248,9 @@ func formatStmt(ctx *formatCtx, stmt ast.Stmt) {
 		formatCallExpr(ctx, v.Call)
 	case *ast.SendStmt:
 		formatExpr(ctx, v.Chan, &v.Chan)
-		formatExpr(ctx, v.Value, &v.Value)
+		for i, val := range v.Values {
+			formatExpr(ctx, val, &v.Values[i])
+		}
 	case *ast.LabeledStmt:
 		formatStmt(ctx, v.Stmt)
 	case *ast.BranchStmt, *ast.EmptyStmt, nil, *ast.BadStmt:


Test patch:
diff --git a/parser/_testdata/functype/parser.expect b/parser/_testdata/functype/parser.expect
index 9d588e2b..182dd084 100644
--- a/parser/_testdata/functype/parser.expect
+++ b/parser/_testdata/functype/parser.expect
@@ -68,7 +68,7 @@ ast.FuncDecl:
           Chan:
             ast.Ident:
               Name: v
-          Value:
+          Values:
             ast.Ident:
               Name: true
         ast.ExprStmt:
diff --git a/parser/_testdata/mytest/parser.expect b/parser/_testdata/mytest/parser.expect
index 8cfb4a85..e4978817 100644
--- a/parser/_testdata/mytest/parser.expect
+++ b/parser/_testdata/mytest/parser.expect
@@ -554,7 +554,7 @@ ast.FuncDecl:
                       Chan:
                         ast.Ident:
                           Name: ch
-                      Value:
+                      Values:
                         ast.Ident:
                           Name: true
                   Body:
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 2277ec8d..086a27fc 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -276,7 +276,7 @@ ast.FuncDecl:
                       Chan:
                         ast.Ident:
                           Name: c
-                      Value:
+                      Values:
                         ast.Ident:
                           Name: true
                 ast.CommClause: