Open tersec opened 2 years ago
Interestingly enough, this example works instead:
discard if true: static(0)
else: static(1)
Those don't, instead:
discard if true:
static(0)
else: static(1)
discard if true: static(0)
else:
static(1)
discard (if true:
static(0)
else:
static(1))
I also found out about a weird static
behavior:
echo static: static(0)
This code should output 0
, however its output is static0
(all examples were compiled using nim r test.nim
)
$ nim --version
Nim Compiler Version 1.6.6 [Linux: amd64]
Compiled at 2022-05-05
Copyright (c) 2006-2021 by Andreas Rumpf
git hash: 0565a70eab02122ce278b98181c7d1170870865c
active boot switches: -d:release
In general, anything which forces the static
into being parsed as part of an expression rather than part of a a statement will suffice (along the lines of how (;foo)
or similar works the other way around, creating an unambiguous statement-context).
So, e.g.,
discard if true:
(static(0))
else:
(static(0))
works fine, as does
discard if true:
(static(0))
else: (static(1))
as does
discard if true: (static(0))
else:
(static(1))
and
discard (if true:
(static(0))
else:
(static(1)))
work.
Wrapping the static
expression in parens suffices. It's just a kludgy workaround to a parser which doesn't properly parse the documented language grammar.
https://nim-lang.org/docs/manual.html#syntax-grammar shows the static
keyword in both
operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
| 'or' | 'xor' | 'and'
| 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
| 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
prefixOperator
and
staticStmt = 'static' colcom stmt
Nim just seems to prefer trying to parse the staticStmt
and doesn't seem to have a decent recovery mechanism to detect whether it should be parsing static
as a prefixOperator
.
One can scan for other keywords which appear in a prefix position in both statements and expressions, and, e.g., try
seems to have the same kind of problem:
echo(try: 0 finally: discard)
(i.e. using parens to force Nim to parse a tryExpr
rather than a tryStmt
) is fine, because
tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
(optInd 'except' exprList colcom stmt)*
(optInd 'finally' colcom stmt)?
allows optInd
rather than
tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
(IND{=}? 'except' exprList colcom stmt)*
(IND{=}? 'finally' colcom stmt)?
which require the IND{=}?
.
But if one does echo try: 0 finally: discard
, then Nim tries parsing it as a tryStmt
, and can't recover.
As a final edit to this comment, https://github.com/nim-lang/Nim/issues/18714 is basically this issue for proc
/func
(or, vice versa) and has the same workarounds.
There were 2 mistakes in the grammar: static
is not an operator, and ifExpr
actually parses stmt
s.
With this in mind, staticStmt
is parsed first (and yes this changes with indentation). Using a conservative definition of ordered choice (/
in the grammar) that only checks the first token, this would error, but we would need to mention this definition as it's not standard (and use it consistently). Same for #18714.
The problem is that it's hard to implement the required backtracking in the current parser, which sucks because it's only 1 token ahead. Maybe we could get rid of staticStmt
and treat it like a regular call but I'm not sure if the compiler could handle it and it would be an AST change
static
expressions inif
/block
/when
/etc don't parse.Example
Current Output
Expected Output
Successful compilation.
Some variations which do compile successfully:
Additional Information
Tested with