robrix / Madness

Recursive Descent Into Madness
MIT License
291 stars 17 forks source link

`++` is too noisy/verbose #16

Closed robrix closed 8 years ago

robrix commented 9 years ago
one ++ two ++ three

is the concatenation of three parsers but you as much of the concatenation as the parsers.

one + two + three

is a bit better, and + already basically means concatenation for SequenceType.

We could also add overloads for arrays/various arities of tuples:

(one, two, three) --> { … }

would be an overload of --> applying to 3-tuples of parsers. This would, sadly, need a lot of overloads, but it would also be kind of nice because it would avoid the problem that ++ has (as would other infix operators) where you get a right-associative list of pairs instead of an n-tuple. That is,

a ++ b ++ c ++ d ++ e

results in a parse tree that looks like

(a, (b, (c, (d, e)))

which is really obnoxious to unpack: d is $0.1.1.1.0 instead of, say, $3.

Since we probably don’t want to have overrides for every combination of operator, side, and tuple-arity, we could instead do something similar to #15 and use an operator:

%(a, b, c, d, e) --> { … }
robrix commented 9 years ago

This doesn’t work due to what I currently believe to be a Swift bug.

I think I’ll go to + instead of ++ for starters.

robrix commented 9 years ago

:anguished: + has the same precedence as |!

regexident commented 9 years ago

%(a, b, c, d, e) --> { … } would be very nice indeed! As would be $3 and alike. ++

robrix commented 9 years ago

Going to have to hunt down the Swift bugs preventing %(a, b, c, d, e) from working presently and file some radars.

robrix commented 9 years ago

Radar filed.

let concatenated1 = %(a, b, c, d, e) // fails
let concatenated2 = (%)((a, b, c, d, e)) // succeeds
regexident commented 9 years ago

Should we call it "swisp" or "lift"?

robrix commented 9 years ago

I wish I could star comments on here; I’ll pretend I can anyway: :star:

robrix commented 9 years ago

The other problem with %(a, b, c, d, e) (or, equally, concatenate(a, b, c, d, e)) is (). Currently () is used to ignore a term, dropping it from the parse tree. The ignore operator maps its argument to (), and concatenation, alternation, and repetition all treat it appropriately:

a ++ ignore(b) ++ c // => (A, C)
a | ignore(b) // => A?
ignore(a)* // => ()

Seeing as actually dropping the void parse trees occurs in the ++, |, and * (etc) operators, we’ve actually got to have four different definitions of ++:

  1. A and B
  2. () and A
  3. A and ()
  4. () and () (because otherwise () and () is ambiguous between the above two)

It’s bad having to write a different concatenate operator for each arity (2-concatenate, 3-concatenate, etc); it’s really bad having to write 2ⁿ + 2ⁿ⁻¹ + … different ones. In order to support up to 5-tuples, we’d need ~60 functions.

robrix commented 9 years ago

On the other hand, using () to drop terms from the parse forest was mostly a way to avoid having to deal with the right-associative tuple tree problem sooner. Maybe it will be acceptable to do --> { a, _, c in … }

robrix commented 9 years ago

…but it is very nice not having to account for whitespace at all.

robrix commented 9 years ago

This issue is actually tracking two different (tho intersecting) issues:

  1. ++ is syntactically loud relative to the parsers it’s combining, and
  2. (a, (b, (c, (d, e)))) is a horrible parse tree to to deal with in Swift.

Using an infix operator allows us to retain the piecewise handling of () parsers, eliding them. Maybe we can resolve the second issue orthogonally: by adding an nth operator for the parse trees:

  1. We need implement only one overload of nth per tree-arity.
  2. We can continue using an infix operator to concatenate parsers, and thus enjoy the composition of recursive binary definitions.
  3. We can ignore terms without creating a combinatorial explosion of syntactic forms.
robrix commented 9 years ago

…but we can’t implement nth because we don’t know what its type will be :tired_face:

robrix commented 9 years ago

We can implement at0, at1, etc., however.

(I am so very much reminded of the C preprocessor.)

robrix commented 9 years ago

We end up needing two atn definitions for each arity. I’ll take 2n over 2 + 2⁻¹ + … tho.

robrix commented 9 years ago

Having done that, I can’t recommend anyone ever use it, because Swift’s got some crazy O(_n_²) stuff going on or something and the typechecker caused my laptop to burn my leg.