Skip to content

Commit

Permalink
Allow full commands and blocks in type sections (nim-lang#19181)
Browse files Browse the repository at this point in the history
* allow full commands and blocks in type sections
* update grammar
* fix changelog [skip ci]
* more tests
* even more tests
  • Loading branch information
metagn authored and PMunch committed Mar 28, 2022
1 parent 372bdf0 commit bae116d
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 11 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
```
- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental,
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
for the right-hand side of type definitions in type sections. Previously
they would error with "invalid indentation".

## Compiler changes

Expand Down
28 changes: 19 additions & 9 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ proc primarySuffix(p: var Parser, r: PNode,
#| | DOTLIKEOP optInd symbol generalizedLit?
#| | '[' optInd exprColonEqExprList optPar ']'
#| | '{' optInd exprColonEqExprList optPar '}'
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax
result = r

# progress guaranteed
Expand All @@ -821,14 +821,14 @@ proc primarySuffix(p: var Parser, r: PNode,
of tkParLe:
# progress guaranteed
if p.tok.strongSpaceA > 0:
# inside type sections, expressions such as `ref (int, bar)`
# are parsed as a nkCommand with a single tuple argument (nkPar)
result = commandExpr(p, result, mode)
# type sections allow full command syntax
if mode == pmTypeDef:
result = newNodeP(nkCommand, p)
result.add r
result.add primary(p, pmNormal)
else:
result = commandExpr(p, result, mode)
var isFirstParam = false
while p.tok.tokType == tkComma:
getTok(p)
optInd(p, result)
result.add(commandParam(p, isFirstParam, mode))
break
result = namedParams(p, result, nkCall, tkParRi)
if result.len > 1 and result[1].kind == nkExprColonExpr:
Expand Down Expand Up @@ -869,9 +869,18 @@ proc primarySuffix(p: var Parser, r: PNode,
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that
result = commandExpr(p, result, mode)
if mode == pmTypeDef:
var isFirstParam = false
while p.tok.tokType == tkComma:
getTok(p)
optInd(p, result)
result.add(commandParam(p, isFirstParam, mode))
break
else:
break
# type sections allow post-expr blocks
if mode == pmTypeDef:
result = postExprBlocks(p, result)

proc parseOperators(p: var Parser, headNode: PNode,
limit: int, mode: PrimaryMode): PNode =
Expand Down Expand Up @@ -1342,7 +1351,8 @@ proc parseTypeDesc(p: var Parser): PNode =
result = binaryNot(p, result)

proc parseTypeDefAux(p: var Parser): PNode =
#| typeDefAux = simpleExpr ('not' expr)?
#| typeDefAux = simpleExpr ('not' expr
#| | postExprBlocks)?
result = simpleExpr(p, pmTypeDef)
result = binaryNot(p, result)

Expand Down
5 changes: 3 additions & 2 deletions doc/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')'
| DOTLIKEOP optInd symbol generalizedLit?
| '[' optInd exprColonEqExprList optPar ']'
| '{' optInd exprColonEqExprList optPar '}'
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax
pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
identVis = symbol OPR? # postfix position
identVisDot = symbol '.' optInd symbol OPR?
Expand Down Expand Up @@ -93,7 +93,8 @@ primary = operatorB primary primarySuffix* |
('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary
/ prefixOperator* identOrLiteral primarySuffix*
typeDesc = simpleExpr ('not' expr)?
typeDefAux = simpleExpr ('not' expr)?
typeDefAux = simpleExpr ('not' expr
| postExprBlocks)?
postExprBlocks = ':' stmt? ( IND{=} doBlock
| IND{=} 'of' exprList ':' stmt
| IND{=} 'elif' expr ':' stmt
Expand Down
9 changes: 9 additions & 0 deletions tests/parser/ttypecommandcomma.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
discard """
errormsg: "invalid indentation"
line: 8
column: 19
"""

type
Foo = call(1, 2), 3:
4
9 changes: 9 additions & 0 deletions tests/parser/ttypecommandindent1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
discard """
errormsg: "invalid indentation"
line: 9
column: 3
"""

type
Foo = call x, y, z:
abc
11 changes: 11 additions & 0 deletions tests/parser/ttypecommandindent2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
discard """
errormsg: "invalid indentation"
line: 10
column: 5
"""

type
Foo = call x, y, z:
abc
do:
def
11 changes: 11 additions & 0 deletions tests/parser/ttypecommandindent3.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
discard """
errormsg: "expression expected, but found 'keyword do'"
line: 10
column: 1
"""

type
Foo = call x, y, z:
abc
do:
def
Loading

0 comments on commit bae116d

Please sign in to comment.