"""
Various pyparsing grammar elements and a few useful routines.
"""
from contextlib import contextmanager
import pyparsing as pp
from .decorators import cache
[docs]
@contextmanager
def generate_grammar():
""" Set the pyparsing newline handling
and then restore to the original state """
try:
old = None
kchars = None
pe = pp.ParserElement
if hasattr(pe, "DEFAULT_WHITE_CHARS"):
old = pe.DEFAULT_WHITE_CHARS
if hasattr(pp.Keyword, "DEFAULT_KEYWORD_CHARS"):
kchars = pp.Keyword.DEFAULT_KEYWORD_CHARS + '-'
pe.setDefaultWhitespaceChars(' \t\r')
yield
finally:
if old is not None:
pe.setDefaultWhitespaceChars(old)
if kchars is not None:
pp.Keyword.DEFAULT_KEYWORD_CHARS = kchars
[docs]
def replace_whitechars(expr):
expr = expr.copy()
expr.setWhitespaceChars(' \t\r')
return expr
with generate_grammar():
optional_line_end = pp.Suppress(pp.LineEnd() | pp.WordStart() ).setName(' ')
""" Grammar for an optinal newline """
line_end = pp.Suppress(pp.LineEnd()).setName('\n')
""" Grammar for a required newline """
end_of_file = (pp.Regex(r'[\s]*') + pp.StringEnd()).suppress().setName('<EOF>')
""" Grammar for an end of file (ending whitespaces are allowed) """
optional_quote = pp.Optional("'").suppress()
""" Grammar for an optional quote """
[docs]
def separator_pattern(char):
return f'[{char}]' * 11 + '*'
[docs]
@cache
def separator_grammar(char):
""" Pattern for separating sections in an input file """
separator = pp.Regex(separator_pattern(char)).setName(f"{char*10}[{char*4}....]").suppress()
return separator
[docs]
def delimitedList(expr, delim):
""" Delimited list with already suppressed delimiter (or with a in-results-wanted one) """
return expr + pp.ZeroOrMore(delim + expr)
[docs]
def addConditionEx(self, condition, message):
""" Add check condition to the pyparsing ParseElement,
that, if it failed, raise a parse exception with a given message. """
def check_condition(s, loc, tocs):
m = message
if condition(tocs):
return tocs
if not isinstance(m, str):
m = m(tocs)
raise pp.ParseException(s, loc, m)
self.addParseAction(check_condition)
return self
[docs]
def addParseActionEx(self, pa, message = None):
"""
Add parse action to a given pyparsing ParseElemenet,
that, if it raise an exception, fail with a given message
"""
def parse_action(s, loc, x):
try:
return pa(x)
except Exception as e:
if message:
msg = f'{message}\n{e}'
else:
msg = str(e)
raise pp.ParseException(s, loc, msg).with_traceback(e.__traceback__) from e
self.addParseAction(parse_action)
[docs]
class White(pp.White):
""" Fix for whitechars in pp.White
In Python 3.10, pyparsing.White do not respect default_white_chars.
This class fixes this.
"""
[docs]
def __init__(self, white):
super().__init__(white)
self.setWhitespaceChars('')
pp.ParserElement.addConditionEx = addConditionEx
pp.ParserElement.addParseActionEx = addParseActionEx