Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser: cmd style check tuple to list #1315

Merged
merged 8 commits into from Jul 4, 2022
1 change: 1 addition & 0 deletions parser/_testdata/cmdlinestyle6/cmd.gop
@@ -0,0 +1 @@
println (1+2i,2)
35 changes: 35 additions & 0 deletions 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
90 changes: 62 additions & 28 deletions parser/parser.go
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -1579,8 +1578,16 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
p.exprLev++
var list []ast.Expr
var ellipsis token.Pos
var extractTuple bool
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing

println (1, 2)*2

should return an error.

So, we should check if this statement is ended or not.

list = tuple.Items
extractTuple = true
visualfc marked this conversation as resolved.
Show resolved Hide resolved
rparen = tuple.End()
break
}
list = append(list, expr) // builtins may expect a type: make(some type, ...)
if p.tok == token.ELLIPSIS {
ellipsis = p.pos
p.next()
Expand All @@ -1599,7 +1606,9 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
p.exprLev--
var noParenEnd token.Pos
if isCmd {
noParenEnd = p.pos
if !extractTuple {
noParenEnd = p.pos
}
} else {
visualfc marked this conversation as resolved.
Show resolved Hide resolved
rparen = p.expectClosing(token.RPAREN, "argument list")
}
Expand Down Expand Up @@ -1635,7 +1644,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
Expand Down Expand Up @@ -1859,6 +1868,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 {
Expand Down Expand Up @@ -1898,6 +1910,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)
Expand Down Expand Up @@ -2024,6 +2039,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 {
Expand Down Expand Up @@ -2065,10 +2083,20 @@ 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 *tupleExpr) Pos() token.Pos {
visualfc marked this conversation as resolved.
Show resolved Hide resolved
return p.Opening
}

func (p *tupleExpr) End() token.Pos {
return p.Closing
}

func (p *parser) parseLambdaExpr(allowTuple bool, allowCmd, allowRangeExpr bool) ast.Expr {
var x ast.Expr
var first = p.pos
if p.tok != token.RARROW {
Expand All @@ -2089,7 +2117,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
Expand All @@ -2100,15 +2128,19 @@ 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 {
e := x
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
Expand Down Expand Up @@ -2138,8 +2170,10 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
RhsHasParen: rhsHasParen,
}
}
if _, ok := x.(*tupleExpr); ok {
panic("TODO: tupleExpr")
if !allowTuple {
if _, ok := x.(*tupleExpr); ok {
p.error(x.Pos(), "not support tuple")
visualfc marked this conversation as resolved.
Show resolved Hide resolved
}
}
return x
}
Expand All @@ -2148,14 +2182,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 {
Expand All @@ -2165,15 +2199,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
}
Expand Down Expand Up @@ -2275,7 +2309,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
}
Expand Down Expand Up @@ -2740,13 +2774,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}}
Expand Down Expand Up @@ -2790,7 +2824,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
Expand Down