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):
return guarded_call
ApplyFunc = Callable[['Parser'], None]
FlagFunc = Callable[[ApplyFunc, Set[ApplyFunc]], bool]
class Parser(ParserBase):
"""
(Abstract) Base class for Parser combinator parsers. Any parser
......@@ -314,8 +318,6 @@ class Parser(ParserBase):
is attached.
"""
ApplyFunc = Callable[['Parser'], None]
def __init__(self) -> None:
# assert isinstance(name, str), str(name)
super().__init__()
......@@ -350,7 +352,7 @@ class Parser(ParserBase):
`reset()`-method of the derived class."""
self.visited = dict() # type: Dict[int, Tuple[Optional[Node], StringView]]
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]:
"""Applies the parser to the given `text` and returns a node with
......@@ -390,23 +392,55 @@ class Parser(ParserBase):
assigned to a grammar."""
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
descendant parsers if any exist. The same function can never
be applied twice between calls of the ``reset()``-method!
Returns `True`, if function has been applied, `False` if function
had been applied earlier already and thus has not been applied again.
descendant parsers, if any exist.
In order to break cycles, function `flip` is called, which should
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
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)
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:
"""
......@@ -1134,9 +1168,9 @@ class UnaryOperator(Parser):
duplicate.ptype = self.ptype
return duplicate
def apply(self, func: Parser.ApplyFunc) -> bool:
if super().apply(func):
self.parser.apply(func)
def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super()._apply(func, flip):
self.parser._apply(func, flip)
return True
return False
......@@ -1165,10 +1199,10 @@ class NaryOperator(Parser):
duplicate.ptype = self.ptype
return duplicate
def apply(self, func: Parser.ApplyFunc) -> bool:
if super().apply(func):
def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super()._apply(func, flip):
for parser in self.parsers:
parser.apply(func)
parser._apply(func, flip)
return True
return False
......@@ -1979,8 +2013,8 @@ class Forward(Parser):
"""
self.parser = parser
def apply(self, func: Parser.ApplyFunc) -> bool:
if super().apply(func):
self.parser.apply(func)
def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
if super()._apply(func, flip):
self.parser._apply(func, flip)
return True
return False
......@@ -97,11 +97,10 @@ class ParserBase:
yet connected to any Grammar object, None is returned."""
raise NotImplementedError
def apply(self, func: Callable) -> bool:
"""Applies the function `func` to the parser. Returns False, if
- for whatever reason - the functions has not been applied, True
otherwise."""
return False
def apply(self, func: Callable):
"""Applies the function `func` recursively to the parser and all
descendant parsers, if any exist."""
pass
WHITESPACE_PTYPE = ':Whitespace'
......
......@@ -311,7 +311,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
def lookahead_artifact(raw_errors):
"""
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
lookahead operator at the end. See test_testing.TestLookahead.
"""
......
......@@ -27,13 +27,37 @@ sys.path.extend(['../', './'])
from DHParser.toolkit import compile_python_object
from DHParser.log import logging, is_logging, log_ST
from DHParser.error import Error
from DHParser.parse import Retrieve, Grammar, Forward, TKN, ZeroOrMore, RE, \
RegExp, Lookbehind, NegativeLookahead, OneOrMore, Series, Alternative, AllOf, SomeOf, UnknownParserError
from DHParser.parse import Retrieve, Parser, Grammar, Forward, TKN, ZeroOrMore, RE, \
RegExp, Lookbehind, NegativeLookahead, OneOrMore, Series, Alternative, AllOf, SomeOf, \
UnknownParserError
from DHParser import compile_source
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler
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:
def test_direct_left_recursion1(self):
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