Commit 52612d1d authored by di68kap's avatar di68kap
Browse files

parse.py: bugfix in doctest of Grammar.associated_symbol()

parent 89067068
...@@ -212,7 +212,8 @@ def reentry_point(rest: StringView, ...@@ -212,7 +212,8 @@ def reentry_point(rest: StringView,
######################################################################## ########################################################################
ApplyFunc = Callable[['Parser'], Optional[bool]] # A return value of True stops any further application ApplyFunc = Callable[[List['Parser']], Optional[bool]]
# The return value of `True` stops any further application
FlagFunc = Callable[[ApplyFunc, Set[ApplyFunc]], bool] FlagFunc = Callable[[ApplyFunc, Set[ApplyFunc]], bool]
ParseFunc = Callable[[StringView], Tuple[Optional[Node], StringView]] ParseFunc = Callable[[StringView], Tuple[Optional[Node], StringView]]
...@@ -583,7 +584,7 @@ class Parser: ...@@ -583,7 +584,7 @@ class Parser:
""" """
return tuple() return tuple()
def _apply(self, func: ApplyFunc, flag_cycle: FlagFunc) -> bool: def _apply(self, func: ApplyFunc, context: List['Parser'], 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 as long as `func()` returns `None` or `False`. descendant parsers as long as `func()` returns `None` or `False`.
...@@ -597,12 +598,12 @@ class Parser: ...@@ -597,12 +598,12 @@ class Parser:
class Parser or any of its descendants. The entry point for external class Parser or any of its descendants. The entry point for external
calls is the method `apply()` without underscore! calls is the method `apply()` without underscore!
""" """
if not flag_cycle(func, self.cycle_detection): if not flip(func, self.cycle_detection):
if func(self): if func(context + [self]):
return True return True
else: else:
for parser in self.sub_parsers(): for parser in self.sub_parsers():
if parser._apply(func, flag_cycle): if parser._apply(func, context + [self], flip):
return True return True
return False return False
return False return False
...@@ -646,9 +647,9 @@ class Parser: ...@@ -646,9 +647,9 @@ class Parser:
return False return False
if func in self.cycle_detection: if func in self.cycle_detection:
return self._apply(func, negative_flip) return self._apply(func, [], negative_flip)
else: else:
return self._apply(func, positive_flip) return self._apply(func, [], positive_flip)
def static_error(self, msg: str, code: ErrorCode) -> 'AnalysisError': def static_error(self, msg: str, code: ErrorCode) -> 'AnalysisError':
return (self.symbol, self, Error(msg, 0, code)) return (self.symbol, self, Error(msg, 0, code))
...@@ -1187,11 +1188,12 @@ class Grammar: ...@@ -1187,11 +1188,12 @@ class Grammar:
return self._reversed__ return self._reversed__
def _add_parser__(self, parser: Parser) -> None: def _add_parser__(self, context: List[Parser]) -> None:
""" """
Adds the particular copy of the parser object to this Adds the particular copy of the parser object to this
particular instance of Grammar. particular instance of Grammar.
""" """
parser = context[-1]
if parser.pname: if parser.pname:
# prevent overwriting instance variables or parsers of a different class # prevent overwriting instance variables or parsers of a different class
assert (parser.pname not in self.__dict__ assert (parser.pname not in self.__dict__
...@@ -1430,20 +1432,24 @@ class Grammar: ...@@ -1430,20 +1432,24 @@ class Grammar:
If `parser` is not connected to any symbol in the Grammar, If `parser` is not connected to any symbol in the Grammar,
`None` is returned. `None` is returned.
>>> anonymous_re = RegExp(r'\w+') >>> word = Series(RegExp(r'\w+'), Whitespace(r'\s*'))
>>> word = Series(anonymous_re, Whitespace(r'\s*'))
>>> word.pname = 'word' >>> word.pname = 'word'
>>> gr = Grammar(word) >>> gr = Grammar(word)
>>> anonymous_re = gr['word'].parsers[0]
>>> gr.associated_symbol(anonymous_re).pname >>> gr.associated_symbol(anonymous_re).pname
'word' 'word'
""" """
symbol = None # type: Optional[Parser] symbol = None # type: Optional[Parser]
def find_symbol_for_parser(p: Parser) -> Optional[bool]: def find_symbol_for_parser(context: List[Parser]) -> Optional[bool]:
nonlocal symbol, parser nonlocal symbol, parser
if p.pname: if parser in context[-1].sub_parsers():
symbol = p for p in reversed(context):
return parser in p.sub_parsers() if p.pname:
# save the name of the closest containing named parser
symbol = p
return True # stop searching
return False # continue searching
if parser.pname: if parser.pname:
return parser return parser
...@@ -1464,8 +1470,9 @@ class Grammar: ...@@ -1464,8 +1470,9 @@ class Grammar:
""" """
error_list = [] # type: List[AnalysisError] error_list = [] # type: List[AnalysisError]
def visit_parser(parser: Parser) -> Optional[bool]: def visit_parser(context: List[Parser]) -> Optional[bool]:
nonlocal error_list nonlocal error_list
parser = context[-1]
errors = parser.static_analysis() errors = parser.static_analysis()
if errors is not None: if errors is not None:
error_list.extend(errors) error_list.extend(errors)
...@@ -2675,7 +2682,7 @@ class Capture(UnaryParser): ...@@ -2675,7 +2682,7 @@ class Capture(UnaryParser):
'Capture only works as named parser! Error in parser: ' + str(self), 'Capture only works as named parser! Error in parser: ' + str(self),
0, Error.CAPTURE_WITHOUT_PARSERNAME 0, Error.CAPTURE_WITHOUT_PARSERNAME
))) )))
if self.parser.apply(lambda p: p.drop_content): if self.parser.apply(lambda plist: plist[-1].drop_content):
errors.append((self.pname, self, Error( errors.append((self.pname, self, Error(
'Captured symbol "%s" contains parsers that drop content, ' 'Captured symbol "%s" contains parsers that drop content, '
'which can lead to unintended results!' % (self.pname or str(self)), 'which can lead to unintended results!' % (self.pname or str(self)),
......
...@@ -326,7 +326,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT' ...@@ -326,7 +326,7 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT'
def has_lookahead(parser_name: str) -> bool: def has_lookahead(parser_name: str) -> bool:
"""Returns True if the parser or any of its descendant parsers is a """Returns True if the parser or any of its descendant parsers is a
Lookahead parser.""" Lookahead parser."""
return parser[parser_name].apply(lambda p: isinstance(p, Lookahead)) return parser[parser_name].apply(lambda ctx: isinstance(ctx[-1], Lookahead))
# lookahead_found = False # lookahead_found = False
# #
# def find_lookahead(p: Parser): # def find_lookahead(p: Parser):
......
...@@ -122,8 +122,8 @@ def all_descendants(root: Parser) -> List[Parser]: ...@@ -122,8 +122,8 @@ def all_descendants(root: Parser) -> List[Parser]:
"""Returns a list with the parser `root` and all of its descendants.""" """Returns a list with the parser `root` and all of its descendants."""
descendants = [] descendants = []
def visit(parser: Parser): def visit(context: List[Parser]):
descendants.append(parser) descendants.append(context[-1])
root.apply(visit) root.apply(visit)
return descendants return descendants
......
...@@ -22,6 +22,7 @@ limitations under the License. ...@@ -22,6 +22,7 @@ limitations under the License.
import os import os
import sys import sys
from functools import partial from functools import partial
from typing import List
scriptpath = os.path.dirname(__file__) or '.' scriptpath = os.path.dirname(__file__) or '.'
sys.path.append(os.path.abspath(os.path.join(scriptpath, '..'))) sys.path.append(os.path.abspath(os.path.join(scriptpath, '..')))
...@@ -82,7 +83,8 @@ class TestParserClass: ...@@ -82,7 +83,8 @@ class TestParserClass:
""" """
gr = grammar_provider(minilang)() gr = grammar_provider(minilang)()
l = [] l = []
def visitor(p: Parser): def visitor(context: List[Parser]):
p = context[-1]
l.append(p.pname + p.ptype) l.append(p.pname + p.ptype)
gr.root__.apply(visitor) gr.root__.apply(visitor)
s1 = ", ".join(l) s1 = ", ".join(l)
......
Supports Markdown
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