Repository: goplus/gop
Pull request #1315 (2022-07-03 01:14:38)

parser: cmd style check tuple to list
```
println (1,2)
```


Patch:
diff --git a/parser/parser.go b/parser/parser.go
index aeb33760..3ba83574 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -688,10 +688,10 @@ func (p *parser) parseExprList(lhs, allowCmd bool) (list []ast.Expr) {
 		defer un(trace(p, "ExpressionList"))
 	}
 
-	list = append(list, p.checkExpr(p.parseExpr(lhs, allowCmd, false)))
+	list = append(list, p.checkExpr(p.parseExpr(lhs, false, allowCmd, false)))
 	for p.tok == token.COMMA {
 		p.next()
-		list = append(list, p.checkExpr(p.parseExpr(lhs, false, false)))
+		list = append(list, p.checkExpr(p.parseExpr(lhs, false, false, false)))
 	}
 	return
 }
@@ -1406,21 +1406,21 @@ func (p *parser) parseOperand(lhs, allowTuple, allowCmd bool) ast.Expr {
 		p.next()
 		if allowTuple && p.tok == token.RPAREN { // () => expr
 			p.next()
-			return &tupleExpr{}
+			return &tupleExpr{Opening: lparen, Closing: p.pos}
 		}
 		p.exprLev++
-		x := p.parseRHSOrType() // types may be parenthesized: (some type)
+		x := p.parseRHSOrType(false) // types may be parenthesized: (some type)
 		if allowTuple && p.tok == token.COMMA {
 			// (x, y, ...) => expr
-			items := make([]*ast.Ident, 1, 2)
-			items[0] = p.toIdent(x)
+			items := make([]ast.Expr, 1, 2)
+			items[0] = x
 			for p.tok == token.COMMA {
 				p.next()
-				items = append(items, p.parseIdent())
+				items = append(items, p.parseRHSOrType(false))
 			}
 			p.exprLev--
 			p.expect(token.RPAREN)
-			return &tupleExpr{items: items}
+			return &tupleExpr{Opening: lparen, Items: items, Closing: p.pos}
 		}
 		p.exprLev--
 		rparen := p.expect(token.RPAREN)
@@ -1568,7 +1568,6 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	if p.trace {
 		defer un(trace(p, "CallOrConversion"))
 	}
-
 	var lparen, rparen token.Pos
 	var endTok token.Token
 	if isCmd {
@@ -1580,7 +1579,14 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	var list []ast.Expr
 	var ellipsis token.Pos
 	for p.tok != endTok && p.tok != token.EOF && !ellipsis.IsValid() {
-		list = append(list, p.parseRHSOrType()) // builtins may expect a type: make(some type, ...)
+		expr := p.parseRHSOrType(isCmd && len(list) == 0)
+		if tuple, ok := expr.(*tupleExpr); ok {
+			list = tuple.Items
+			rparen = tuple.Closing
+			isCmd = false
+			break
+		}
+		list = append(list, expr) // builtins may expect a type: make(some type, ...)
 		if p.tok == token.ELLIPSIS {
 			ellipsis = p.pos
 			p.next()
@@ -1600,7 +1606,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	var noParenEnd token.Pos
 	if isCmd {
 		noParenEnd = p.pos
-	} else {
+	} else if rparen == token.NoPos {
 		rparen = p.expectClosing(token.RPAREN, "argument list")
 	}
 	if debugParseOutput {
@@ -1635,7 +1641,7 @@ func (p *parser) parseValue(keyOk bool) ast.Expr {
 	// undeclared; or b) it is a struct field. In the former case, the type
 	// checker can do a top-level lookup, and in the latter case it will do
 	// a separate field lookup.
-	x := p.checkExpr(p.parseExpr(keyOk, false, false))
+	x := p.checkExpr(p.parseExpr(keyOk, false, false, false))
 	if keyOk {
 		if p.tok == token.COLON {
 			// Try to resolve the key but don't collect it
@@ -1859,6 +1865,9 @@ func (p *parser) parsePrimaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 	}
 
 	x := p.parseOperand(lhs, allowTuple, allowCmd)
+	if _, ok := x.(*tupleExpr); ok {
+		return x
+	}
 L:
 	for {
 		switch p.tok {
@@ -1898,6 +1907,9 @@ L:
 			}
 			isCmd := allowCmd && x.End() != p.pos // println ()
 			x = p.parseCallOrConversion(p.checkExprOrType(x), isCmd)
+			if isCmd && !x.(*ast.CallExpr).IsCommand() {
+				allowCmd = false
+			}
 		case token.LBRACE: // {
 			if allowCmd && x.End() != p.pos { // println {}
 				x = p.parseCallOrConversion(p.checkExprOrType(x), true)
@@ -2024,6 +2036,9 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int, allowTuple, allowCmd bool)
 	}
 
 	x := p.parseUnaryExpr(lhs, allowTuple, allowCmd)
+	if _, ok := x.(*tupleExpr); ok {
+		return x
+	}
 	for {
 		op, oprec := p.tokPrec()
 		if oprec < prec1 {
@@ -2065,10 +2080,12 @@ func (p *parser) parseRangeExpr(allowCmd bool) ast.Expr {
 
 type tupleExpr struct {
 	ast.Expr
-	items []*ast.Ident
+	Opening token.Pos
+	Items   []ast.Expr
+	Closing token.Pos
 }
 
-func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
+func (p *parser) parseLambdaExpr(allowTuple bool, allowCmd, allowRangeExpr bool) ast.Expr {
 	var x ast.Expr
 	var first = p.pos
 	if p.tok != token.RARROW {
@@ -2089,7 +2106,7 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 			rhsHasParen = true
 			p.next()
 			for {
-				item := p.parseExpr(false, false, false)
+				item := p.parseExpr(false, false, false, false)
 				rhs = append(rhs, item)
 				if p.tok != token.COMMA {
 					break
@@ -2100,7 +2117,7 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 		case token.LBRACE: // {
 			body = p.parseBlockStmt()
 		default:
-			rhs = []ast.Expr{p.parseExpr(false, false, false)}
+			rhs = []ast.Expr{p.parseExpr(false, false, false, false)}
 		}
 		var lhs []*ast.Ident
 		if x != nil {
@@ -2108,7 +2125,11 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 		retry:
 			switch v := e.(type) {
 			case *tupleExpr:
-				lhs, lhsHasParen = v.items, true
+				items := make([]*ast.Ident, len(v.Items), len(v.Items))
+				for i, item := range v.Items {
+					items[i] = p.toIdent(item)
+				}
+				lhs, lhsHasParen = items, true
 			case *ast.ParenExpr:
 				e, lhsHasParen = v.X, true
 				goto retry
@@ -2138,8 +2159,10 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 			RhsHasParen: rhsHasParen,
 		}
 	}
-	if _, ok := x.(*tupleExpr); ok {
-		panic("TODO: tupleExpr")
+	if !allowTuple {
+		if tuple, ok := x.(*tupleExpr); ok {
+			p.error(tuple.Opening, "tuple is not supported")
+		}
 	}
 	return x
 }
@@ -2148,14 +2171,14 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 // The result may be a type or even a raw type ([...]int). Callers must
 // check the result (using checkExpr or checkExprOrType), depending on
 // context.
-func (p *parser) parseExpr(lhs, allowCmd, allowRangeExpr bool) ast.Expr {
+func (p *parser) parseExpr(lhs, allowTuple, allowCmd, allowRangeExpr bool) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "Expression"))
 	}
 	if lhs {
 		return p.parseBinaryExpr(true, token.LowestPrec+1, false, allowCmd)
 	}
-	return p.parseLambdaExpr(allowCmd, allowRangeExpr)
+	return p.parseLambdaExpr(allowTuple, allowCmd, allowRangeExpr)
 }
 
 func (p *parser) parseRHS() ast.Expr {
@@ -2165,15 +2188,15 @@ func (p *parser) parseRHS() ast.Expr {
 func (p *parser) parseRHSEx(allowRangeExpr bool) ast.Expr {
 	old := p.inRHS
 	p.inRHS = true
-	x := p.checkExpr(p.parseExpr(false, false, allowRangeExpr))
+	x := p.checkExpr(p.parseExpr(false, false, false, allowRangeExpr))
 	p.inRHS = old
 	return x
 }
 
-func (p *parser) parseRHSOrType() ast.Expr {
+func (p *parser) parseRHSOrType(allowTuple bool) ast.Expr {
 	old := p.inRHS
 	p.inRHS = true
-	x := p.checkExprOrType(p.parseExpr(false, false, false))
+	x := p.checkExprOrType(p.parseExpr(false, allowTuple, false, false))
 	p.inRHS = old
 	return x
 }
@@ -2275,7 +2298,7 @@ func (p *parser) parseSimpleStmt(mode int, allowCmd bool) (ast.Stmt, bool) {
 }
 
 func (p *parser) parseCallExpr(callType string) *ast.CallExpr {
-	x := p.parseRHSOrType() // could be a conversion: (some type)(x)
+	x := p.parseRHSOrType(false) // could be a conversion: (some type)(x)
 	if call, isCall := x.(*ast.CallExpr); isCall {
 		return call
 	}
@@ -2740,13 +2763,13 @@ func (p *parser) parseForPhrases() (phrases []*ast.ForPhrase) {
 
 func (p *parser) parseForPhraseStmtPart(lhs []ast.Expr) *ast.ForPhraseStmt {
 	tokPos := p.expect(token.ARROW) // <-
-	x := p.parseExpr(false, false, true)
+	x := p.parseExpr(false, false, false, true)
 	var cond ast.Expr
 	var ifPos token.Pos
 	if p.tok == token.IF || p.tok == token.COMMA {
 		ifPos = p.pos
 		p.next()
-		cond = p.parseExpr(false, false, false)
+		cond = p.parseExpr(false, false, false, false)
 	}
 
 	stmt := &ast.ForPhraseStmt{ForPhrase: &ast.ForPhrase{TokPos: tokPos, X: x, IfPos: ifPos, Cond: cond}}
@@ -2790,7 +2813,7 @@ func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container if c
 	}
 
 	tokPos := p.expect(token.ARROW) // <- container
-	x := p.parseExpr(false, false, true)
+	x := p.parseExpr(false, false, false, true)
 	var init ast.Stmt
 	var cond ast.Expr
 	var ifPos token.Pos


Test patch:
diff --git a/parser/_testdata/cmdlinestyle6/cmd.gop b/parser/_testdata/cmdlinestyle6/cmd.gop
new file mode 100644
index 00000000..126b37c5
--- /dev/null
+++ b/parser/_testdata/cmdlinestyle6/cmd.gop
@@ -0,0 +1,1 @@
+println (1+2i,2)
diff --git a/parser/_testdata/cmdlinestyle6/parser.expect b/parser/_testdata/cmdlinestyle6/parser.expect
new file mode 100644
index 00000000..dcdefb1d
--- /dev/null
+++ b/parser/_testdata/cmdlinestyle6/parser.expect
@@ -0,0 +1,35 @@
+package main
+
+file cmd.gop
+noEntrypoint
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.BinaryExpr:
+                  X:
+                    ast.BasicLit:
+                      Kind: INT
+                      Value: 1
+                  Op: +
+                  Y:
+                    ast.BasicLit:
+                      Kind: IMAG
+                      Value: 2i
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 2