-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FAQ: Added question about infinite loops in parsers
- Loading branch information
1 parent
ce16b4e
commit 8cd3e8f
Showing
2 changed files
with
65 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
`funcparserlib` FAQ | ||
=================== | ||
|
||
Frequently asked questions related to [funcparserlib][1]. | ||
|
||
|
||
1. Why did my parser enter an infinite loop? | ||
-------------------------------------------- | ||
|
||
Because the grammar you've defined allows infinite empty sequences. It's a | ||
general pitfall of grammar rules and it must be avoided. Let's explain why this | ||
may happen. | ||
|
||
_A universally successful parser_ is a parser that _may consume no token_ from | ||
the input sequence _returning a value_ without raising `NoParseError`. It still | ||
may consume some tokens and return values, but when it cannot, it just returns | ||
a value, not an error. | ||
|
||
The basic parser combinators that return parsers having this property are | ||
`pure`, `maybe`, and `many`: | ||
|
||
* A result of `pure` always returns its argument without even accessing the | ||
input sequence | ||
* A result of `maybe` always returns either a parsing result or `None` | ||
* A result of `many` always returns a list (maybe the empty one) | ||
|
||
By using these combinators for composing parsers you can (and you have done this | ||
actually!) create your own universally successful parsers. | ||
|
||
One more fact. Given some parser `p`, the `many` combinator returns a parser `q` | ||
that applies `p` to the input sequence unless `p` fails with `NoParseError`. | ||
|
||
So we can deduce that, given a universally successful parser, `many` returns a | ||
parser that may apply it to the input _forever._ This is the cause of an infinite | ||
loop. | ||
|
||
You **must not** pass a universally successful parser to a parser returned by | ||
`many`. | ||
|
||
Consider the following parers: | ||
|
||
from funcparserlib.parser import a, many, maybe, pure | ||
|
||
const = lambda x: lambda _: x | ||
x = a('x') | ||
|
||
p1 = maybe(x) | ||
p2 = many(p1) | ||
p3 = maybe(x) + x | ||
p4 = many(p3) | ||
p5 = x | many(x) | ||
p6 = many(p5) | ||
p7 = x + many(p4) | ||
p8 = x >> const(True) | ||
p9 = pure(True) | ||
|
||
Here `p1`, `p2`, `p4`, `p5`, `p6`, and `p9` are universally successful parsers | ||
while `p3`, `p7`, and `p8` are is not. Parsers `p2`, `p6`, and `p7` may enter an | ||
infinite loop, while others cannot. Just apply the statements we have made | ||
above to these parsers to figure out why. | ||
|
||
[1]: http://code.google.com/p/funcparserlib/ | ||
|