In some applications it is important not only to parse correctly structured input, but also to give a helpful responses when the input is incorrectly structured.
Lepl provides support for reporting errors in the input in three ways.
The first approach is often the best compromise and is used by default (see .config.full_first_match(), configuration). However, it is limited to a single match and gives little information about the underlying problem.
The second approach is simple, but doesn’t play well with backtracking.
The third approach is probably best for more complex situations, but remains relatively unexplored. It may not scale well, for example.
Here is an example of the second and third approaches in use:
>>> from lepl import *
>>> class Term(List): pass
>>> class Factor(List): pass
>>> class Expression(List): pass
>>> expr = Delayed()
>>> number = Digit()[1:,...]
>>> badChar = AnyBut(Space() | Digit() | '(')[1:,...]
>>> with DroppedSpace():
>>> unopen = number ** make_error('no ( before {stream_out}') & ')'
>>> unclosed = ('(' & expr & Eos()) ** make_error('no ) for {stream_in}')
>>> term = Or(
>>> (number | '(' & expr & ')') > Term,
>>> badChar ^ 'unexpected text: {results[0]}',
>>> unopen,
>>> unclosed
>>> )
>>> muldiv = Any('*/')
>>> factor = (term & (muldiv & term)[:]) > Factor
>>> addsub = Any('+-')
>>> expr += (factor & (addsub & factor)[:]) > Expression
>>> line = Empty() & Trace(expr) & Eos() >> sexpr_throw
>>> parser = line.get_parse()
>>> parser('1 + 2 * (3 + 4 - 5')[0]
File "str: '1 + 2 * (3 + 4 - 5'", line 1
1 + 2 * (3 + 4 - 5
^
lepl.matchers.error.Error: no ) for '(3 + 4...'
>>> parser('1 + 2 * 3 + 4 - 5)')[0]
File "str: '1 + 2 * 3 + 4 - 5)'", line 1
1 + 2 * 3 + 4 - 5)
^
lepl.matchers.error.Error: no ( before ')'
>>> parser('1 + 2 * (3 + four - 5)')[0]
File "str: '1 + 2 * (3 + four - 5)'", line 1
1 + 2 * (3 + four - 5)
^
lepl.matchers.error.Error: unexpected text: four
>>> parser('1 + 2 ** (3 + 4 - 5)')[0]
File "str: '1 + 2 ** (3 + 4 - 5)'", line 1
1 + 2 ** (3 + 4 - 5)
^
lepl.matchers.error.Error: unexpected text: *
Note
This example follows the > Capitalised; >> lowercase and Use Or() With Complex Alternatives patterns.
Warning
The order of expressions is important in the example above. The default Configuration will change the order of some expressions if the grammar is left–recursive. So if you have a left–recursive grammar and want to use the approach shown to error handling then you must call .config.no_optimize_or(). For more information see Memoisation.
| Name | Type | Action |
|---|---|---|
| ^ | Operator | Raises an exception, given a format string. Formatting has the same named parameters as the KApply() matcher (results, stream_in, stream_out); implemented as KApply(raise_error) |
| raise_error() | Function | See above. |
| Error() | Class | Creates a parse tree node that can be used to trigger a later exception (Error is a subclass of both Node and SyntaxError). |
| sexpr_throw() | Function | Walks a List()–based parse tree and raises the first Error found. |
| node_throw() | Function | Walks a Node()–based parse tree and raises the first Error found. |
| make_error() | Function | Creates an Error node, given a format string. |