Skip to content

Commit

Permalink
FAQ: Added question about infinite loops in parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
vlasovskikh committed Jul 28, 2009
1 parent ce16b4e commit 8cd3e8f
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ There a couple of examples available in `./examples` directory:
* GraphViz DOT parser
* JSON paser

See also [the changelog][3].
See also [the changelog][3] and [FAQ][4].

[1]: Tutorial
[2]: Brackets
[3]: Changes
[4]: FAQ

<!-- vim:set ft=markdown tw=80: -->

63 changes: 63 additions & 0 deletions doc/FAQ.md
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/

0 comments on commit 8cd3e8f

Please sign in to comment.