qwertie / ecsharp

Home of LoycCore, the LES language of Loyc trees, the Enhanced C# parser, the LeMP macro preprocessor, and the LLLPG parser generator.
http://ecsharp.net
Other
177 stars 25 forks source link

LES3: Allow commas in keyword-expressions? #85

Closed qwertie closed 4 years ago

qwertie commented 5 years ago

Sometimes it might be really useful to write keyword-statements with commas in them:

.lineto x, y
.print x, y, z

Currently, this is not allowed because it would generally be ambiguous, as commas are usually already allowed to separate subexpressions:

  1. Function calls: foo(x, .keyword y, z) - does foo take two arguments or three?
  2. Lists: [x, .keyword y, z] - is it a list of 2 items or 3?
  3. Tuples: (x, .keyword y, z) - is it a list of 2 items or 3?
  4. Dictionaries/braces {...}: 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:

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".

qwertie commented 5 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
qwertie commented 4 years ago

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...