com-lihaoyi / fastparse

Writing Fast Parsers Fast in Scala
https://com-lihaoyi.github.io/fastparse
MIT License
1.09k stars 164 forks source link

Scoped cuts #43

Closed Ichoran closed 8 years ago

Ichoran commented 8 years ago

Cuts are a nice feature for generating error information and for simplifying parsing, but they appear to be global in nature. That is, you can't (as far as I can tell) provide a scope for the cut that essentially says: for this parsing branch you treat this as a cut, but if you bail out on this entire branch, there's still another one to consider.

A motivating example: data may usually be in a format that can be efficiently represented, such as an array of ints, but may (rarely) contain valid yet not-efficiently-represented data (e.g. a struct). Scoped cuts can allow you to get precise information when it really must be an array of ints without having to write a second parsing routine to handle the case where non-Int input is okay.

Of course there are ways to do this: do lax parsing, then filter by sub-parsing the captured string using cuts. This, however, is inelegant and inefficient.

There is an additional consideration which is if one has scoped cuts, can you "cut more deeply" to make a cut that will escape one or more levels of scoping (or perhaps can escape scopes of particular names)? I do not yet have an opinion on whether this is a good idea.

The minimal syntax would look something like

val Num = CharsWhile(c => c >= '0' && c <= '9').!.map(_.toInt)
val Str = ("\"" ~ CharsWhile(c => c != '"').! ~ "\"")
val NumArray = Num.rep(1, " " ~! Pass)
val AnyArray = Scoped(NumArray) | (Num | Str).rep(1, " " ~! Pass)
lihaoyi commented 8 years ago

I think this is probably what you want:

https://github.com/lihaoyi/fastparse/blob/9493f9a9844a5892852ca96a4d42f9d34b965ad3/fastparse/shared/src/main/scala/fastparse/parsers/Combinators.scala#L53-L68

As you can see, it's basically undocumented, but it's used in a few places

https://github.com/lihaoyi/fastparse/search?utf8=%E2%9C%93&q=NoCut

It basically swallows any cut failures that make it up to that point and turn them into non-cut failures.

Ichoran commented 8 years ago

Yes, that's it. The simple version, anyway (i.e. without cuts of different depth). Thanks!