05.11., 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit 9d301e10 authored by eckhart's avatar eckhart

- 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