Source code for ase2sprkkr.common.grammar

"""
Various pyparsing grammar elements and a few useful routines.
"""

from contextlib import contextmanager
import pyparsing as pp
import re
from .decorators import cache
from typing import Optional


[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.set_default_whitespace_chars(" \t\r") yield finally: if old is not None: pe.set_default_whitespace_chars(old) if kchars is not None: pp.Keyword.DEFAULT_KEYWORD_CHARS = kchars
[docs] def replace_whitechars(expr): expr = expr.copy() expr.set_whitespace_chars(" \t\r") return expr
with generate_grammar(): optional_line_end = pp.Suppress(pp.LineEnd() | pp.WordStart()).set_name(" ") """ Grammar for an optinal newline """ line_end = pp.Suppress(pp.LineEnd()).set_name("\n") """ Grammar for a required newline """ end_of_file = (pp.Regex(r"[\s]*") + pp.StringEnd()).suppress().set_name("<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)).set_name(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 add_condition_ex(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.add_parse_action(check_condition) return self
[docs] def add_parse_action_ex(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.add_parse_action(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.set_whitespace_chars("")
[docs] class SkipToRegex(pp.Token): """Skip to given regex"""
[docs] def __init__(self, pattern, include_pattern: Optional[bool] = None, parse_pattern: bool = False): if not isinstance(pattern, re.Pattern): pattern = re.compile(pattern) self.pattern = pattern if include_pattern is None: include_pattern = parse_pattern self.include_pattern = include_pattern self.parse_pattern = parse_pattern super().__init__()
@property def custom_name(self): self.customName = "skipTo{self.pattern}"
[docs] def parseImpl(self, instr, loc, doActions=True): result = self.pattern.search(instr, loc) if result: start = result.start() out = instr[loc:start] if self.parse_pattern: out = (out, instr[start : result.end()]) if self.include_pattern: loc = result.end() else: loc = result.start() return loc, pp.ParseResults(out) raise pp.ParseException(instr, loc, "Pattern {self.pattern} not found", self)
pp.ParserElement.add_condition_ex = add_condition_ex pp.ParserElement.add_parse_action_ex = add_parse_action_ex