From 859854079ca2b9dc3f80e045280faa11013cbd3b Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Sat, 23 Jul 2022 18:26:57 +0200 Subject: [PATCH] Optional line numbers in expecting and allow flags in front of regex --- pyleri/__init__.py | 2 +- pyleri/regex.py | 19 ++++++++++++++++++- pyleri/result.py | 10 ++++++++-- test/test_list.py | 12 ++++++++++++ test/test_regex.py | 9 +++++++++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/pyleri/__init__.py b/pyleri/__init__.py index 08f0631..d2e8858 100644 --- a/pyleri/__init__.py +++ b/pyleri/__init__.py @@ -41,4 +41,4 @@ __author__ = 'Jeroen van der Heijden' __maintainer__ = 'Jeroen van der Heijden' __email__ = 'jeroen@cesbit.com' -__version__ = '1.3.4' +__version__ = '1.4.0' diff --git a/pyleri/regex.py b/pyleri/regex.py index 3020483..9636b65 100644 --- a/pyleri/regex.py +++ b/pyleri/regex.py @@ -5,6 +5,18 @@ import re from .elements import NamedElement, c_export, go_export, java_export +_RE_FLAGS = re.compile( + r'^(' + r'(\(\?i\))|' + r'(\(\?m\))|' + r'(\(\?s\))|' + r'(\(\?A\))|' + r'(\(\?x\))|' + r'(\(\?X\))|' + r'(\(\?U\))|' + r'(\(\?:\))' + r')(.)') + class Regex(NamedElement): @@ -12,7 +24,12 @@ class Regex(NamedElement): def __init__(self, pattern, flags=0): if not pattern.startswith('^'): - pattern = '^' + pattern + m = _RE_FLAGS.match(pattern) + if not m: + pattern = '^' + pattern + elif m.group(2) != '^': + pos = m.end() - 1 + pattern = pattern[:pos] + '^' + pattern[pos:] assert flags == 0 or flags == re.IGNORECASE, \ 'Only re.IGNORECASE is currently accepted as flag' self._compiled = re.compile(pattern, flags=flags) diff --git a/pyleri/result.py b/pyleri/result.py index bc4e765..d080e8f 100644 --- a/pyleri/result.py +++ b/pyleri/result.py @@ -21,10 +21,16 @@ def __init__(self, is_valid, pos): self.expecting = None self.tree = None - def as_str(self, translate=None): + def as_str(self, translate=None, line_number=False): if self.is_valid: return 'parsed successfully' - res = ['error at position {}'.format(self.pos)] + if line_number: + string = self.tree._string + lnr = string.count('\n', 0, self.pos) + 1 + pos = self.pos - string.rfind('\n', 0, self.pos) + res = ['error at line {}, col {}'.format(lnr, pos)] + else: + res = ['error at position {}'.format(self.pos)] arr = [] for elem in (self.expecting): expectstr = translate(elem) if translate else None diff --git a/test/test_list.py b/test/test_list.py index 5e83eef..d91cdf6 100644 --- a/test/test_list.py +++ b/test/test_list.py @@ -63,6 +63,18 @@ def test_list_all_options(self): 'error at position 0, expecting: hi' ) + self.assertEqual( + grammar.parse('').as_str(line_number=True), + 'error at line 1, col 1, expecting: hi' + ) + + self.assertEqual( + grammar.parse(""" + hi-hi-hi-hi-hi + """).as_str(line_number=True), + 'error at line 2, col 26, expecting: end_of_statement' + ) + if __name__ == '__main__': unittest.main() diff --git a/test/test_regex.py b/test/test_regex.py index 6ec0c2c..da622ac 100644 --- a/test/test_regex.py +++ b/test/test_regex.py @@ -28,6 +28,15 @@ def test_regex(self): if elem is regex else None), 'error at position 0, expecting: single_quoted_string' ) + self.assertTrue(grammar.parse("'hi'").is_valid) + + def test_flags_regex(self): + regex = Regex(r'(?s)//.*?(\r?\n|$)') + grammar = create_grammar(regex) + + self.assertTrue(grammar.parse(""" + //test + """).is_valid) if __name__ == '__main__':