Commit 543f48a3 authored by eckhart's avatar eckhart
Browse files

left recursion 1st try

parent f939c268
{
"python.pythonPath": "/bin/python",
"python.pythonPath": "/Applications/Xcode.app/Contents/Developer/usr/bin/python3",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.testing.nosetestArgs": [
......
......@@ -204,7 +204,7 @@ CONFIG_PRESET['flatten_tree'] = True
# because of python's recursion depth limit
# Left recursion handling can be turned off by setting this value to zero
# Default value: 5
CONFIG_PRESET['left_recursion_depth'] = 5
CONFIG_PRESET['left_recursion_depth'] = 0
# Maximum allowed number of retries after errors where the parser
# would exit before the complete document has been parsed. Should
......
......@@ -583,10 +583,10 @@ class Parser:
@property
def grammar(self) -> 'Grammar':
try:
if self._grammar != GRAMMAR_PLACEHOLDER:
if self._grammar is not GRAMMAR_PLACEHOLDER:
return self._grammar
else:
raise AssertionError('Grammar has not yet been set!')
raise ValueError('Grammar has not yet been set!')
except (AttributeError, NameError):
raise AttributeError('Parser placeholder does not have a grammar!')
......@@ -3163,8 +3163,12 @@ class Forward(UnaryParser):
def __init__(self):
super(Forward, self).__init__(PARSER_PLACEHOLDER)
# self.parser = PARSER_PLACEHOLDER # type: Parser
self.cycle_reached = False
# self.recursion = dict() # type: Dict[int, Tuple[int, int]]
self.cycle_reached = False # type: bool
self.memoization = True # type: bool
def reset(self):
super(Forward, self).reset()
self.recursion = dict() # type: Dict[int, Tuple[int, int]]
def __deepcopy__(self, memo):
duplicate = self.__class__()
......@@ -3185,25 +3189,46 @@ class Forward(UnaryParser):
`Synonym`, which might be a meaningful marker for the syntax tree,
parser Forward should never appear in the syntax tree.
"""
return self.parser(text)
# location = self.grammar.document_length__ - text._len
# if location in self.recursion:
# depth, oracle = self.recursion[location]
# if depth >= oracle:
# self.recursion[location] = (0, oracle + 1)
# return None, text
# else:
# self.recursion[location] = (depth + 1, oracle)
# return self.parser(text)
# else:
# self.recursion[location] = (0, 0)
# best = None, text
# while True:
# node, text_ = self.parser(text)
# oracle = self.recursion[location][1]
# if node is None or oracle == 0:
# break
# best = node, text_
# if not isinstance(self.parser, Alternative):
# return self.parser(text)
# TODO: For indirect recursion, recursion counters should not only
# depend on location, but on location and call stack depth
location = self.grammar.document_length__ - text._len
depth, oracle = self.recursion.get(location, (-1, -1))
if oracle >= 0:
if depth >= oracle:
self.recursion[location] = (0, oracle + 1)
node, _text = None, text
else:
self.recursion[location] = (depth + 1, oracle)
node, _text = self.parser(text)
oracle = self.recursion[location][1]
self.recursion[location] = (depth, oracle)
self.memoization = self.grammar.memoization__
self.grammar.memoization__ = False
return node, _text
else:
self.recursion[location] = (0, 0)
longest = None, text
length = 0
while True:
node, text_ = self.parser(text)
depth, oracle = self.recursion[location]
if oracle == 0:
longest = node, text_
break
elif node is None:
break
else:
l = len(node)
if l <= length:
break
length = l
longest = node, text_
self.recursion[location] = (-1, -1)
self.grammar.memoization__ = self.memoization
return longest
def set_proxy(self, proxy: Optional[ParseFunc]):
"""`set_proxy` has no effects on Forward-objects!"""
......
......@@ -111,6 +111,16 @@ class TestParserClass:
class TestInfiLoopsAndRecursion:
def test_very_simple(self):
minilang = """
term = term (`*`|`/`) factor | factor
factor = /[0-9]+/
"""
parser = grammar_provider(minilang)()
snippet = "5*4*3"
st = parser(snippet)
assert not is_error(st.error_flag)
def test_direct_left_recursion1(self):
minilang = """@literalws = right
expr = expr ("+"|"-") term | term
......@@ -149,6 +159,7 @@ class TestInfiLoopsAndRecursion:
Sum = Expr { ('+' | '-') Expr }+
Value = /[0-9.]+/~ | '(' §Expr ')'
"""
# print(raw_compileEBNF(minilang).result)
parser = grammar_provider(minilang)()
snippet = "8 * 4"
syntax_tree = parser(snippet)
......@@ -395,7 +406,7 @@ class TestGrammar:
"""
pyparser, messages, _ = compile_source(grammar, None, get_ebnf_grammar(),
get_ebnf_transformer(), get_ebnf_compiler("PosTest"))
assert pyparser
assert pyparser, str(messages)
assert not messages, str(messages)
def test_pos_values_initialized(self):
......
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