Closed qwertie closed 4 years ago
Perhaps the right way to modify the syntax is to allow ,
whereever a continuator keyword (like else
or finally
) would be allowed, and to treat it like a continuator except that just a single expression is expected after it. For example, right now you can write
.keyword expr1 finally { } else expr2 { } catch expr3 { }
// This means #keyword(expr1, #else(expr2, { }), #finally({ }))
So you could instead write
.keyword expr1, { } else expr2 { }, expr3, { }
// This means #keyword(expr1, { }, #else(expr2, { }), expr3, { })
But it wouldn't allow a braced block the way a continuator does:
.keyword expr1, { } else expr2 { }, expr3 { }
^syntax error
Rather than modify the continuator syntax, I decided it was a bit simpler to allow the initial expression after the .keyword
to be a list of expressions instead. Stripped down to the bare essentials, the new keyword-expression grammar is
KeywordExpression :
"." &{IsConjoinedToken($LI)} TT.Id
( Expr[Precedence.MinValue, compactMode: false] CommaContinuator* )?
( "\n"? BracedBlock )?
Continuator*;
CommaContinuator :
"," "\n"? TopExpr[compactMode: false];
Continuator :
"\n"? ContinuatorKeyword "\n"?
( BracedBlock
/ TopExpr[compactMode: false]
( "\n"? BracedBlock )?
);
ContinuatorKeyword : &{IsContinuator($LI)} TT.Id;
The parser needed more code to support this than I hoped (grammar file is 28 lines longer) but this is mainly needed for proper error detection and reporting; it's just 6 lines of new "grammar" code. Generated code is 54 lines longer.
In the long run, the added complexity is probably worth it. I'll revisit keyword expressions again soon, when I split LES3 into multiple languages...
Sometimes it might be really useful to write keyword-statements with commas in them:
Currently, this is not allowed because it would generally be ambiguous, as commas are usually already allowed to separate subexpressions:
foo(x, .keyword y, z)
- doesfoo
take two arguments or three?[x, .keyword y, z]
- is it a list of 2 items or 3?(x, .keyword y, z)
- is it a list of 2 items or 3?{...}
: the code currently allows;
or,
to separate items (the only requirement is to be consistent - you can't switch between;
and,
)I don't want to complicate the parser by requiring the keyword-expression parser to have to change behavior - to ignore
,
if it is in a context where,
is being used as a list separator. However, perhaps a reasonable behavior is to always allow,
in keyword-expressions, but to print an error if a keyword-expression contains a comma in a context where the comma is already known to be a list separator. Specifically:Foo(.keyword a, b)
would be an error, as function calls always allow comma-separation[.keyword a, b]
would be an error, as lists always allow commas(.keyword a, b)
would not be an error, as you can always write a tuple with semicolons instead.(a, .keyword b, c)
would be an error, since the user has indicated an intention to separate items with commas.{.keyword a, b}
would not be an error, because the "normal" way to separate expressions inside braces is with semicolons or newlines.{.keyword a, b; c}
is a block with two statements.{a, b, .keyword c, d}
would be an error, since the user has indicated an intention to separate items with commas.These rules sound a little complicated, but I suspect they would be pretty easy to implement in the current parser (which already checks for inconsistency so it can give an error for code like
{a;b,c}
). Note that these rules could be implemented with almost any parser generator (because the actual parsing behavior is context-free), but some limited PGs might not allow the user to detect the error in the error cases.P.S. I'm going to avoid "LESv3" from now on in favor of simply "LES3".