From f3552b86c15ea487910c71f75f31d0a25d0e5890 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 9 Sep 2020 07:36:31 +0200 Subject: [PATCH] Better semiStmtList parsing (#15123) * Better semiStmtList parsing * Add examples from forums and wiki * Move parseIfExpr near parseIfOrWhen * Update grammar --- compiler/parser.nim | 122 ++++++++++---------------- tests/parser/tstmtlists.nim | 165 ++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 78 deletions(-) create mode 100644 tests/parser/tstmtlists.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index 0fa860c99..f256cee66 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -523,15 +523,21 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode = proc complexOrSimpleStmt(p: var Parser): PNode proc simpleExpr(p: var Parser, mode = pmNormal): PNode +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList - result.add(complexOrSimpleStmt(p)) - # progress guaranteed - while p.tok.tokType == tkSemiColon: - getTok(p) - optInd(p, result) - result.add(complexOrSimpleStmt(p)) + withInd(p): + # Be lenient with the first stmt/expr + result.add if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) + while true: + if p.tok.tokType == tkSemiColon: + getTok(p) + if p.tok.tokType == tkParRi: + break + elif not (sameInd(p) or realInd(p)): + parMessage(p, errInvalidIndentation) + result.add complexOrSimpleStmt(p) dec p.inSemiStmtList result.transitionSonsKind(nkStmtListExpr) @@ -540,10 +546,10 @@ proc parsePar(p: var Parser): PNode = #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' #| par = '(' optInd - #| ( &parKeyw complexOrSimpleStmt ^+ ';' - #| | ';' complexOrSimpleStmt ^+ ';' + #| ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + #| | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + #| | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) #| optPar ')' # @@ -847,64 +853,6 @@ proc simpleExpr(p: var Parser, mode = pmNormal): PNode = when defined(nimpretty): dec p.em.doIndentMore -proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = - #| condExpr = expr colcom expr optInd - #| ('elif' expr colcom expr optInd)* - #| 'else' colcom expr - #| ifExpr = 'if' condExpr - #| whenExpr = 'when' condExpr - when true: - result = newNodeP(kind, p) - while true: - getTok(p) # skip `if`, `when`, `elif` - var branch = newNodeP(nkElifExpr, p) - optInd(p, branch) - branch.add(parseExpr(p)) - colcom(p, branch) - branch.add(parseStmt(p)) - skipComment(p, branch) - result.add(branch) - if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break - if p.tok.tokType == tkElse: # and sameOrNoInd(p): - var branch = newNodeP(nkElseExpr, p) - eat(p, tkElse) - colcom(p, branch) - branch.add(parseStmt(p)) - result.add(branch) - else: - var - b: PNode - wasIndented = false - result = newNodeP(kind, p) - - getTok(p) - let branch = newNodeP(nkElifExpr, p) - branch.add(parseExpr(p)) - colcom(p, branch) - let oldInd = p.currInd - if realInd(p): - p.currInd = p.tok.indent - wasIndented = true - branch.add(parseExpr(p)) - result.add branch - while sameInd(p) or not wasIndented: - case p.tok.tokType - of tkElif: - b = newNodeP(nkElifExpr, p) - getTok(p) - optInd(p, b) - b.add(parseExpr(p)) - of tkElse: - b = newNodeP(nkElseExpr, p) - getTok(p) - else: break - colcom(p, b) - b.add(parseStmt(p)) - result.add(b) - if b.kind == nkElseExpr: break - if wasIndented: - p.currInd = oldInd - proc parsePragma(p: var Parser): PNode = #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) @@ -1574,6 +1522,30 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = branch.add(parseStmt(p)) result.add(branch) +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = + #| condExpr = expr colcom expr optInd + #| ('elif' expr colcom expr optInd)* + #| 'else' colcom expr + #| ifExpr = 'if' condExpr + #| whenExpr = 'when' condExpr + result = newNodeP(kind, p) + while true: + getTok(p) # skip `if`, `when`, `elif` + var branch = newNodeP(nkElifExpr, p) + optInd(p, branch) + branch.add(parseExpr(p)) + colcom(p, branch) + branch.add(parseStmt(p)) + skipComment(p, branch) + result.add(branch) + if p.tok.tokType != tkElif: break + if p.tok.tokType == tkElse: + var branch = newNodeP(nkElseExpr, p) + eat(p, tkElse) + colcom(p, branch) + branch.add(parseStmt(p)) + result.add(branch) + proc parseWhile(p: var Parser): PNode = #| whileStmt = 'while' expr colcom stmt result = newNodeP(nkWhileStmt, p) @@ -2269,17 +2241,11 @@ proc parseStmt(p: var Parser): PNode = # deprecate this syntax later break p.hasProgress = false - var a = complexOrSimpleStmt(p) - if a.kind != nkEmpty: - result.add(a) - else: - # This is done to make the new 'if' expressions work better. - # XXX Eventually we need to be more strict here. - if p.tok.tokType notin {tkElse, tkElif}: - parMessage(p, errExprExpected, p.tok) - getTok(p) - else: - break + if p.tok.tokType in {tkElse, tkElif}: + parMessage(p, errInvalidIndentation) + getTok(p) + + result.add complexOrSimpleStmt(p) if not p.hasProgress and p.tok.tokType == tkEof: break else: # the case statement is only needed for better error messages: diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim new file mode 100644 index 000000000..b6c7e4a94 --- /dev/null +++ b/tests/parser/tstmtlists.nim @@ -0,0 +1,165 @@ +discard """ + output: ''' +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +hello +1 +hello +2 +hello +3 +hello +4 +hello +5 +hello +6 +hello +7 +hello +8 +hello +9 +hello +10 +hello +1 +hello +2 +hello +3 +hello +4 +hello +5 +hello +6 +hello +7 +hello +8 +hello +9 +hello +10 +''' +""" + +block: ( + discard; + echo 1 + 1; + ) + +block: ( + discard; #Haha + #haha + echo 1 + 1; +) + +block: ( + discard; + #Hmm + echo 1 + + 1; +) + +block: ( + discard + echo "2" +) + +block: ( + discard; + echo 1 + + 1 +) + +block: ( + discard + echo 1 + + 1 +) + +block: ( + discard; + discard +) + +block: ( + discard + echo 1 + 1; + ) + +block: ( + discard + echo 1 + 1; +) + +block: ( + discard + echo 1 + + 1; +) + +block: ( + discard; + ) + +block: ( discard; echo 1 + #heh + 1; +) + +for i in 1..10: + echo "hello" + echo i + +for i in 1..10: ( + echo "hello"; + echo i; +) + +proc square(inSeq: seq[float]): seq[float] = ( + result = newSeq[float](len(inSeq)); + for i, v in inSeq: ( + result[i] = v * v; + ) +) + +proc square2(inSeq: seq[float]): seq[float] = + result = newSeq[float](len(inSeq)); + for i, v in inSeq: ( + result[i] = v * v; + ) + +proc cstringCheck(tracked: int; n: int) = + if true == false and (let a = high(int); let b = high(int); + a.int8 == 8 and a.int8 notin {3..9}): + echo(tracked, n) + +template dim: int = + (if int.high == 0: + int.high + else: + int.high) + +template dim2: int = + (if int.high == 0: + int.high + else: + int.high) + +template dim: int = + ( + if int.high == 0: + int.high + else: + int.high) +