Error Reporting

Introduction

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.

  1. By checking when the entire input was consumed and, if not, reporting on the location of deepest match.
  2. By allowing a matcher to directly raise an exception.
  3. By creating a parse tree with nodes that represent errors; these error nodes can then be used, later, to raise exceptions.

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.

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.

Operators, Functions and Classes

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.

Table Of Contents

Previous topic

Results

Next topic

Debugging

This Page