Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 9d301e10 authored by eckhart's avatar eckhart
Browse files

- mypy type errors corrected

parent 72976d8f
...@@ -256,6 +256,10 @@ def add_parser_guard(parser_func): ...@@ -256,6 +256,10 @@ def add_parser_guard(parser_func):
return guarded_call return guarded_call
ApplyFunc = Callable[['Parser'], None]
FlagFunc = Callable[[ApplyFunc, Set[ApplyFunc]], bool]
class Parser(ParserBase): class Parser(ParserBase):
""" """
(Abstract) Base class for Parser combinator parsers. Any parser (Abstract) Base class for Parser combinator parsers. Any parser
...@@ -314,8 +318,6 @@ class Parser(ParserBase): ...@@ -314,8 +318,6 @@ class Parser(ParserBase):
is attached. is attached.
""" """
ApplyFunc = Callable[['Parser'], None]
def __init__(self) -> None: def __init__(self) -> None:
# assert isinstance(name, str), str(name) # assert isinstance(name, str), str(name)
super().__init__() super().__init__()
...@@ -350,7 +352,7 @@ class Parser(ParserBase): ...@@ -350,7 +352,7 @@ class Parser(ParserBase):
`reset()`-method of the derived class.""" `reset()`-method of the derived class."""
self.visited = dict() # type: Dict[int, Tuple[Optional[Node], StringView]] self.visited = dict() # type: Dict[int, Tuple[Optional[Node], StringView]]
self.recursion_counter = defaultdict(lambda: 0) # type: DefaultDict[int, int] self.recursion_counter = defaultdict(lambda: 0) # type: DefaultDict[int, int]
self.cycle_detection = set() # type: Set[Callable] self.cycle_detection = set() # type: Set[ApplyFunc]
def __call__(self, text: StringView) -> Tuple[Optional[Node], StringView]: def __call__(self, text: StringView) -> Tuple[Optional[Node], StringView]:
"""Applies the parser to the given `text` and returns a node with """Applies the parser to the given `text` and returns a node with
...@@ -390,23 +392,55 @@ class Parser(ParserBase): ...@@ -390,23 +392,55 @@ class Parser(ParserBase):
assigned to a grammar.""" assigned to a grammar."""
pass pass
def apply(self, func: ApplyFunc) -> bool: def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
""" """
Applies function `func(parser)` recursively to this parser and all Applies function `func(parser)` recursively to this parser and all
descendant parsers if any exist. The same function can never descendant parsers, if any exist.
be applied twice between calls of the ``reset()``-method!
Returns `True`, if function has been applied, `False` if function In order to break cycles, function `flip` is called, which should
had been applied earlier already and thus has not been applied again. return `True`, if this parser has already been visited. If not, it
flips the cycle detection flag and returns `False`.
This is a protected function and should not called from outside
class Parser or any of its descendants. The entry point for external
calls is the method `apply()` without underscore!
""" """
if func in self.cycle_detection: if flip(func, self.cycle_detection):
return False return False
else: else:
assert not self.visited, "No calls to Parser.apply() during or " \
"after ongoing parsing process. (Call Parser.reset() first.)"
self.cycle_detection.add(func)
func(self) func(self)
return True return True
def apply(self, func: ApplyFunc):
"""
Applies function `func(parser)` recursively to this parser and all
descendant parsers, if any exist. Traversal is pre-order.
"""
def positive_flip(f: ApplyFunc, flagged: Set[Callable]) -> bool:
"""Returns True, if function `f` has already been applied to this
parser and sets the flag accordingly. Interprets `f in flagged == True`
as meaning that `f` has already been applied."""
if f in flagged:
return True
else:
flagged.add(f)
return False
def negative_flip(f: ApplyFunc, flagged: Set[Callable]) -> bool:
"""Returns True, if function `f` has already been applied to this
parser and sets the flag accordingly. Interprets `f in flagged == False`
as meaning that `f` has already been applied."""
if f not in flagged:
return True
else:
flagged.remove(f)
return False
if func in self.cycle_detection:
self._apply(func, negative_flip)
else:
self._apply(func, positive_flip)
def mixin_comment(whitespace: str, comment: str) -> str: def mixin_comment(whitespace: str, comment: str) -> str:
""" """
...@@ -1134,9 +1168,9 @@ class UnaryOperator(Parser): ...@@ -1134,9 +1168,9 @@ class UnaryOperator(Parser):
duplicate.ptype = self.ptype duplicate.ptype = self.ptype
return duplicate return duplicate
def apply(self, func: Parser.ApplyFunc) -> bool: def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super().apply(func): if super()._apply(func, flip):
self.parser.apply(func) self.parser._apply(func, flip)
return True return True
return False return False
...@@ -1165,10 +1199,10 @@ class NaryOperator(Parser): ...@@ -1165,10 +1199,10 @@ class NaryOperator(Parser):
duplicate.ptype = self.ptype duplicate.ptype = self.ptype
return duplicate return duplicate
def apply(self, func: Parser.ApplyFunc) -> bool: def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super().apply(func): if super()._apply(func, flip):
for parser in self.parsers: for parser in self.parsers:
parser.apply(func) parser._apply(func, flip)
return True return True
return False return False
...@@ -1979,8 +2013,8 @@ class Forward(Parser): ...@@ -1979,8 +2013,8 @@ class Forward(Parser):
""" """
self.parser = parser self.parser = parser
def apply(self, func: Parser.ApplyFunc) -> bool: def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super().apply(func): if super()._apply(func, flip):
self.parser.apply(func) self.parser._apply(func, flip)
return True return True
return False return False
...@@ -97,11 +97,10 @@ class ParserBase: ...@@ -97,11 +97,10 @@ class ParserBase:
yet connected to any Grammar object, None is returned.""" yet connected to any Grammar object, None is returned."""
raise NotImplementedError raise NotImplementedError
def apply(self, func: Callable) -> bool: def apply(self, func: Callable):
"""Applies the function `func` to the parser. Returns False, if """Applies the function `func` recursively to the parser and all
- for whatever reason - the functions has not been applied, True descendant parsers, if any exist."""
otherwise.""" pass
return False
WHITESPACE_PTYPE = ':Whitespace' WHITESPACE_PTYPE = ':Whitespace'
......
...@@ -311,7 +311,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve ...@@ -311,7 +311,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
def lookahead_artifact(raw_errors): def lookahead_artifact(raw_errors):
""" """
Returns True, if the error merely occured, because the parser Returns True, if the error merely occured, because the parser
stopped in front of a seuqence that was captured by a lookahead stopped in front of a sequence that was captured by a lookahead
operator. This is required for testing of parsers that put a operator. This is required for testing of parsers that put a
lookahead operator at the end. See test_testing.TestLookahead. lookahead operator at the end. See test_testing.TestLookahead.
""" """
......
...@@ -27,13 +27,37 @@ sys.path.extend(['../', './']) ...@@ -27,13 +27,37 @@ sys.path.extend(['../', './'])
from DHParser.toolkit import compile_python_object from DHParser.toolkit import compile_python_object
from DHParser.log import logging, is_logging, log_ST from DHParser.log import logging, is_logging, log_ST
from DHParser.error import Error from DHParser.error import Error
from DHParser.parse import Retrieve, Grammar, Forward, TKN, ZeroOrMore, RE, \ from DHParser.parse import Retrieve, Parser, Grammar, Forward, TKN, ZeroOrMore, RE, \
RegExp, Lookbehind, NegativeLookahead, OneOrMore, Series, Alternative, AllOf, SomeOf, UnknownParserError RegExp, Lookbehind, NegativeLookahead, OneOrMore, Series, Alternative, AllOf, SomeOf, \
UnknownParserError
from DHParser import compile_source from DHParser import compile_source
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler
from DHParser.dsl import grammar_provider, DHPARSER_IMPORTS from DHParser.dsl import grammar_provider, DHPARSER_IMPORTS
class TestParserClass:
def test_apply(self):
minilang ="""
expr = expr ("+"|"-") term | term
term = term ("*"|"/") factor | factor
factor = /[0-9]+/~
"""
gr = grammar_provider(minilang)()
l = []
def visitor(p: Parser):
l.append(p.name + p.ptype)
gr.root__.apply(visitor)
s1 = ", ".join(l)
l = []
gr.root__.apply(visitor)
s2 = ", ".join(l)
l = []
gr.root__.apply(visitor)
s3 = ", ".join(l)
# print(s1); print(s2); print(s3)
assert s1 == s2 == s3
class TestInfiLoopsAndRecursion: class TestInfiLoopsAndRecursion:
def test_direct_left_recursion1(self): def test_direct_left_recursion1(self):
minilang =""" minilang ="""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment