Some guidelines for writing good parsers.
Use Or(..., ..., ...) for alternatives with productions. The | syntax can lead to errors because it binds more tightly than >.
Nodes are typically made with constructors and invoked with >, while the processing of results is usually done with mapped functions. So > is followed by a Capitalised name, while >> is followed by a lowercase name. Noticing and following this convention can help avoid issues with the behaviour of Apply() with raw=False (the default implementation of >), which adds an extra level of nesting and is usually inappropriate for use with functions.
throw can be used with >> or >.
DroppedSpace() and Separator() simplify the handling of spaces in the grammar. To avoid confusion, split your grammar into two. The first part, defining words, should come before with; the second part should come after.
>>> # words defined here >>> word = Letter()[:,...] >>> with DroppedSpace(): >>> # sentences defined here >>> sentence = word[1:]
So handling spaces in a grammar takes two steps:
Sometimes a grammar needs to refer to matchers before they are defined. The Delayed() matcher acts as a placeholder which can be passed to other functions. It can be defined later using +=:
>>> expr = Delayed() >>> number = Digit()[1:,...] >>> with Separator(r'\s*'): >>> term = number | '(' & expr & ')' >>> muldiv = Any('*/') >>> factor = term & (muldiv & term)[:] >>> addsub = Any('+-') >>> expr += factor & (addsub & factor)[:]