Better semiStmtList parsing (#15123)

* Better semiStmtList parsing
* Add examples from forums and wiki
* Move parseIfExpr near parseIfOrWhen
* Update grammar
This commit is contained in:
Clyybber
2020-09-09 07:36:31 +02:00
committed by GitHub
parent 10988d4840
commit f3552b86c1
2 changed files with 209 additions and 78 deletions

View File

@@ -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:

165
tests/parser/tstmtlists.nim Normal file
View File

@@ -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)