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
172 stars 25 forks source link

LES3: Use `|` as line continuator? #86

Closed qwertie closed 4 years ago

qwertie commented 5 years ago

Currently LES3 has a prefix | operator, based on the same "operator" in Nemerle. It is unusual in that it has a lower precedence than any other operator (e.g. | x = y means | (x = y). This is kind of neat because it lowers the precedence floor: for example, x * | y = a + 1 means x * (| (y = a + 1)).

I think I need to get rid of it though. The problem is that, since newline is normally a statement terminator, users will need some way to say that a command is split across two lines. Otherwise, the following is considered to be three separate statements:

filtered := DownloadCsv("http://foo-bar-baz.net/quux")
    .Where(row => row.Count == 4)
    .Select(row => new Foo(row[0], row[1], row[2], row[3]))

The traditional way to do this is with backslashes:

filtered := DownloadCsv("http://foo-bar-baz.net/quux") \
    .Where(row => row.Count == 4) \
    .Select(row => new Foo(row[0], row[1], row[2], row[3]))

But that's pretty ugly as the backslashes aren't aligned across lines. Also, someone reading the code might not notice the backslashes. I propose using a leading | instead:

filtered := DownloadCsv("http://foo-bar-baz.net/quux")
|   .Where(row => row.Count == 4)
|   .Select(row => new Foo(row[0], row[1], row[2], row[3]))

This looks better, and it's easier to notice whether a line has been correctly continued or not.

The original prefix | operator could be replaced with, say, || or / instead. Earlier I decided to treat / as a normal prefix operator; it's probably good for it to have the same precedence as - + * because should be easy to remember that the four basic arithmetic operators - + * / have the same precedence when used as prefix operators. So I think I'll introduce || as a replacement for |.

Arguably \ could also be allowed so that, in the context of a REPL, you can specify you want to continue a line. However this would further increase the parser complexity, and in a REPL I think a better way to continue a statement across lines would be to press Shift+Enter.

qwertie commented 5 years ago

An alternative to this would be to use > as the continuator:

filtered := DownloadCsv("http://foo-bar-baz.net/quux")
>   .Where(row => row.Count == 4)
>   .Select(row => new Foo(row[0], row[1], row[2], row[3]))

I don't think this looks as nice, but it has the virtue that > is not already allowed as a prefix operator.

qwertie commented 5 years ago

Come to think of it, it would make some sense to use > as a low-precedence prefix operator. Languages could use it for a shorthand lambda notation, where > Foo() means () => Foo() and e.g. > # + ## + 1 means (a, b) => a + b + 1. Defining that operator would automatically give => the same precedence as per LES precedence rules, so language designers would have some wiggle room about how to name and use the operator... for example, one could use => expr for lambdas and > expr to define a delayed-eval expression, something akin to Lazy<T>.

And then |, of course, is free for use as the continuator.

qwertie commented 5 years ago

Note to self: test that a line continuator can bridge a keyword expr to a braced block to form a single expr:

.if foo > bar
|   // comment here doesn't affect parsing
{
    fooIsGreater()
}
|
else { fooIsLesser() }
qwertie commented 4 years ago

Added | as the continuator character for v28.0.