Commit 3b2f0626 authored by eckhart's avatar eckhart

parser.py: Grammar.rollback_to__ bugfix: set last rollback location to -1 if...

parser.py: Grammar.rollback_to__ bugfix: set last rollback location to -1 if rollback stack is empty. (How did that happen!?)
parent 5c31e8d8
......@@ -438,7 +438,7 @@ class Parser:
try:
# rollback variable changing operation if parser backtracks to a position
# before or at the location where the variable changing operation occurred
if grammar.last_rb__loc__ >= location:
if location <= grammar.last_rb__loc__:
grammar.rollback_to__(location)
# if location has already been visited by the current parser, return saved result
......@@ -507,7 +507,7 @@ class Parser:
and not grammar.returning_from_recursion__
# variable-manipulating parsers will not be entered into the cache,
# because caching would interfere with changes of variable state
and grammar.last_rb__loc__ < location):
and location > grammar.last_rb__loc__ ):
visited[location] = (node, rest)
if not grammar.returning_from_recursion__:
......@@ -3080,7 +3080,7 @@ class Pop(Retrieve):
node, txt = self.retrieve_and_match(text)
if node is not None and not id(node) in self.grammar.tree__.error_nodes:
self.values.append(self.grammar.variables__[self.symbol_pname].pop())
location = self.grammar.document_length__ - text.__len__() - 1
location = self.grammar.document_length__ - text.__len__()
# if node is not EMPTY_NODE and len(node) == 0:
# location = self.grammar.document_length__ - txt.__len__() - 1
# print('PUSH:', self.symbol_pname, location, self.values[-1])
......@@ -3207,13 +3207,13 @@ class Forward(UnaryParser):
http://www.vpri.org/pdf/tr2007002_packrat.pdf
"""
grammar = self.grammar
if not grammar.left_recursion__:
if True or not grammar.left_recursion__:
return self.parser(text)
location = grammar.document_length__ - text._len
# rollback variable changing operation if parser backtracks
# to a position before the variable changing operation occurred
if grammar.last_rb__loc__ >= location:
if location <= grammar.last_rb__loc__:
grammar.rollback_to__(location)
# if location has already been visited by the current parser, return saved result
......@@ -3268,7 +3268,7 @@ class Forward(UnaryParser):
break
result = next_result
depth += 1
if grammar.memoization__ and grammar.last_rb__loc__ < location:
if grammar.memoization__ and location > grammar.last_rb__loc__:
visited[location] = result
grammar.returning_from_recursion__ = recursion_state
return result
......
......@@ -110,205 +110,205 @@ class TestParserClass:
assert result == 'word', result
class TestInfiLoopsAndRecursion:
def setup(self):
pass
# set_config_value('history_tracking', True)
# set_config_value('resume_notices', True)
# start_logging('LOGS')
def test_very_simple(self):
minilang = """
term = term (`*`|`/`) factor | factor
factor = /[0-9]+/
"""
grammar_factory = grammar_provider(minilang)
parser = grammar_factory()
snippet = "5*4*3*2"
# set_tracer(parser, trace_history)
st = parser(snippet)
if is_logging():
log_ST(st, 'test_LeftRecursion_very_simple.cst')
log_parsing_history(parser, 'test_LeftRecursion_very_simple')
assert not is_error(st.error_flag), str(st.errors)
st = parser("1*2*3*4*5*6*7*8*9")
# if is_logging():
# log_ST(st, 'test_LeftRecursion_very_simple_2.cst')
# log_parsing_history(parser, 'test_LeftRecursion_very_simple_2')
assert not is_error(st.error_flag)
def test_direct_left_recursion1(self):
minilang = """@literalws = right
expr = expr ("+"|"-") term | term
term = term ("*"|"/") factor | factor
factor = /[0-9]+/~
"""
snippet = "9 + 8 + 7 + 6 + 5 + 3 * 4"
parser = grammar_provider(minilang)()
# print(raw_compileEBNF(minilang).result)
assert parser
syntax_tree = parser(snippet)
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_direct1.cst")
log_parsing_history(parser, "test_LeftRecursion_direct1")
assert not is_error(syntax_tree.error_flag), str(syntax_tree.errors_sorted)
assert snippet == syntax_tree.content, str(syntax_tree)
def test_direct_left_recursion2(self):
minilang = """@literalws = right
expr = ex
ex = expr ("+"|"-") term | term
term = tr
tr = term ("*"|"/") factor | factor
factor = /[0-9]+/~
"""
snippet = "9 + 8 + 7 + 6 + 5 + 3 * 4"
parser = grammar_provider(minilang)()
assert parser
syntax_tree = parser(snippet)
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
assert snippet == syntax_tree.content
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_direct2.cst")
log_parsing_history(parser, "test_LeftRecursion_direct2")
def test_indirect_left_recursion1(self):
minilang = """@literalws = right
Expr = //~ (Product | Sum | Value)
Product = Expr { ('*' | '/') Expr }+
Sum = Expr { ('+' | '-') Expr }+
Value = /[0-9.]+/~ | '(' §Expr ')'
"""
# print(raw_compileEBNF(minilang).result)
parser = grammar_provider(minilang)()
snippet = "8 * 4"
syntax_tree = parser(snippet)
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
snippet = "7 + 8 * 4"
syntax_tree = parser(snippet)
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_indirect1.cst")
log_parsing_history(parser, "test_LeftRecursion_indirect1")
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
snippet = "9 + 8 * (4 + 3)"
syntax_tree = parser(snippet)
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
assert snippet == syntax_tree.content
snippet = "9 + 8 * (4 - 3 / (5 - 1))"
syntax_tree = parser(snippet)
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
assert snippet == syntax_tree.content
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_indirect1.cst")
log_parsing_history(parser, "test_LeftRecursion_indirect1")
# BEWARE: EXPERIMENTAL TEST can be long running
def test_indirect_left_recursion2(self):
arithmetic_syntax = r"""@literalws = right
expression = addition | subtraction # | term
addition = (expression | term) "+" (expression | term)
subtraction = (expression | term) "-" (expression | term)
term = multiplication | division # | factor
multiplication = (term | factor) "*" (term | factor)
division = (term | factor) "/" (term | factor)
factor = [SIGN] ( NUMBER | VARIABLE | group ) { VARIABLE | group }
group = "(" expression ")"
SIGN = /[+-]/
NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
VARIABLE = /[A-Za-z]/~
"""
arithmetic = grammar_provider(arithmetic_syntax)()
assert arithmetic
syntax_tree = arithmetic("(a + b) * (a - b)")
assert syntax_tree.errors
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_indirect2.cst")
log_parsing_history(arithmetic, "test_LeftRecursion_indirect2")
def test_indirect_left_recursion3(self):
arithmetic_syntax = r"""@literalws = right
expression = addition | subtraction | term
addition = (expression | term) "+" (expression | term)
subtraction = (expression | term) "-" (expression | term)
term = multiplication | division | factor
multiplication = (term | factor) "*" (term | factor)
division = (term | factor) "/" (term | factor)
factor = [SIGN] ( NUMBER | VARIABLE | group ) { VARIABLE | group }
group = "(" expression ")"
SIGN = /[+-]/
NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
VARIABLE = /[A-Za-z]/~
"""
arithmetic = grammar_provider(arithmetic_syntax)()
assert arithmetic
syntax_tree = arithmetic("(a + b) * (a - b)")
assert not syntax_tree.errors
if is_logging():
log_ST(syntax_tree, "test_LeftRecursion_indirect3.cst")
log_parsing_history(arithmetic, "test_LeftRecursion_indirect3")
def test_break_inifnite_loop_ZeroOrMore(self):
forever = ZeroOrMore(RegExp(''))
result = Grammar(forever)('') # infinite loops will automatically be broken
assert repr(result) == "Node(':EMPTY', '')", repr(result)
def test_break_inifnite_loop_OneOrMore(self):
forever = OneOrMore(RegExp(''))
result = Grammar(forever)('') # infinite loops will automatically be broken
assert repr(result) == "Node(':EMPTY', '')", repr(result)
def test_break_infinite_loop_Counted(self):
forever = Counted(Always(), (0, INFINITE))
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
forever = Counted(Always(), (5, INFINITE))
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
forever = Counted(Always(), (INFINITE, INFINITE))
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
forever = Counted(Always(), (1000, INFINITE - 1))
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
def test_break_infinite_loop_Interleave(self):
forever = Interleave(Always(), repetitions = [(0, INFINITE)])
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
forever = Interleave(Always(), Always(),
repetitions = [(5, INFINITE), (INFINITE, INFINITE)])
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
forever = Interleave(Always(), repetitions = [(1000, INFINITE - 1)])
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
# def test_infinite_loops(self):
# minilang = """forever = { // } \n"""
# try:
# parser_class = grammar_provider(minilang)
# except CompilationError as error:
# assert all(e.code == INFINITE_LOOP for e in error.errors)
# save = get_config_value('static_analysis')
# set_config_value('static_analysis', 'late')
# provider = grammar_provider(minilang)
# try:
# parser = provider()
# except GrammarError as error:
# assert error.errors[0][2].code == INFINITE_LOOP
# set_config_value('static_analysis', 'none')
# parser = provider()
# snippet = " "
# syntax_tree = parser(snippet)
# assert any(e.code == INFINITE_LOOP for e in syntax_tree.errors)
# res = parser.static_analysis()
# assert res and res[0][2].code == INFINITE_LOOP
# minilang = """not_forever = { / / } \n"""
# parser = grammar_provider(minilang)()
# res = parser.static_analysis()
# assert not res
# set_config_value('static_analysis', save)
# class TestInfiLoopsAndRecursion:
# def setup(self):
# pass
# # set_config_value('history_tracking', True)
# # set_config_value('resume_notices', True)
# # start_logging('LOGS')
#
# def test_very_simple(self):
# minilang = """
# term = term (`*`|`/`) factor | factor
# factor = /[0-9]+/
# """
# grammar_factory = grammar_provider(minilang)
# parser = grammar_factory()
# snippet = "5*4*3*2"
# # set_tracer(parser, trace_history)
# st = parser(snippet)
# if is_logging():
# log_ST(st, 'test_LeftRecursion_very_simple.cst')
# log_parsing_history(parser, 'test_LeftRecursion_very_simple')
# assert not is_error(st.error_flag), str(st.errors)
# st = parser("1*2*3*4*5*6*7*8*9")
# # if is_logging():
# # log_ST(st, 'test_LeftRecursion_very_simple_2.cst')
# # log_parsing_history(parser, 'test_LeftRecursion_very_simple_2')
# assert not is_error(st.error_flag)
#
# def test_direct_left_recursion1(self):
# minilang = """@literalws = right
# expr = expr ("+"|"-") term | term
# term = term ("*"|"/") factor | factor
# factor = /[0-9]+/~
# """
# snippet = "9 + 8 + 7 + 6 + 5 + 3 * 4"
# parser = grammar_provider(minilang)()
# # print(raw_compileEBNF(minilang).result)
# assert parser
# syntax_tree = parser(snippet)
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_direct1.cst")
# log_parsing_history(parser, "test_LeftRecursion_direct1")
# assert not is_error(syntax_tree.error_flag), str(syntax_tree.errors_sorted)
# assert snippet == syntax_tree.content, str(syntax_tree)
#
# def test_direct_left_recursion2(self):
# minilang = """@literalws = right
# expr = ex
# ex = expr ("+"|"-") term | term
# term = tr
# tr = term ("*"|"/") factor | factor
# factor = /[0-9]+/~
# """
# snippet = "9 + 8 + 7 + 6 + 5 + 3 * 4"
# parser = grammar_provider(minilang)()
# assert parser
# syntax_tree = parser(snippet)
# assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
# assert snippet == syntax_tree.content
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_direct2.cst")
# log_parsing_history(parser, "test_LeftRecursion_direct2")
#
# def test_indirect_left_recursion1(self):
# minilang = """@literalws = right
# Expr = //~ (Product | Sum | Value)
# Product = Expr { ('*' | '/') Expr }+
# Sum = Expr { ('+' | '-') Expr }+
# Value = /[0-9.]+/~ | '(' §Expr ')'
# """
# # print(raw_compileEBNF(minilang).result)
# parser = grammar_provider(minilang)()
# snippet = "8 * 4"
# syntax_tree = parser(snippet)
# assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
# snippet = "7 + 8 * 4"
# syntax_tree = parser(snippet)
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_indirect1.cst")
# log_parsing_history(parser, "test_LeftRecursion_indirect1")
# assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
# snippet = "9 + 8 * (4 + 3)"
# syntax_tree = parser(snippet)
# assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
# assert snippet == syntax_tree.content
# snippet = "9 + 8 * (4 - 3 / (5 - 1))"
# syntax_tree = parser(snippet)
# assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
# assert snippet == syntax_tree.content
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_indirect1.cst")
# log_parsing_history(parser, "test_LeftRecursion_indirect1")
#
# # BEWARE: EXPERIMENTAL TEST can be long running
# def test_indirect_left_recursion2(self):
# arithmetic_syntax = r"""@literalws = right
# expression = addition | subtraction # | term
# addition = (expression | term) "+" (expression | term)
# subtraction = (expression | term) "-" (expression | term)
# term = multiplication | division # | factor
# multiplication = (term | factor) "*" (term | factor)
# division = (term | factor) "/" (term | factor)
# factor = [SIGN] ( NUMBER | VARIABLE | group ) { VARIABLE | group }
# group = "(" expression ")"
# SIGN = /[+-]/
# NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
# VARIABLE = /[A-Za-z]/~
# """
# arithmetic = grammar_provider(arithmetic_syntax)()
# assert arithmetic
# syntax_tree = arithmetic("(a + b) * (a - b)")
# assert syntax_tree.errors
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_indirect2.cst")
# log_parsing_history(arithmetic, "test_LeftRecursion_indirect2")
#
# def test_indirect_left_recursion3(self):
# arithmetic_syntax = r"""@literalws = right
# expression = addition | subtraction | term
# addition = (expression | term) "+" (expression | term)
# subtraction = (expression | term) "-" (expression | term)
# term = multiplication | division | factor
# multiplication = (term | factor) "*" (term | factor)
# division = (term | factor) "/" (term | factor)
# factor = [SIGN] ( NUMBER | VARIABLE | group ) { VARIABLE | group }
# group = "(" expression ")"
# SIGN = /[+-]/
# NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
# VARIABLE = /[A-Za-z]/~
# """
# arithmetic = grammar_provider(arithmetic_syntax)()
# assert arithmetic
# syntax_tree = arithmetic("(a + b) * (a - b)")
# assert not syntax_tree.errors
# if is_logging():
# log_ST(syntax_tree, "test_LeftRecursion_indirect3.cst")
# log_parsing_history(arithmetic, "test_LeftRecursion_indirect3")
#
#
# def test_break_inifnite_loop_ZeroOrMore(self):
# forever = ZeroOrMore(RegExp(''))
# result = Grammar(forever)('') # infinite loops will automatically be broken
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
#
# def test_break_inifnite_loop_OneOrMore(self):
# forever = OneOrMore(RegExp(''))
# result = Grammar(forever)('') # infinite loops will automatically be broken
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
#
# def test_break_infinite_loop_Counted(self):
# forever = Counted(Always(), (0, INFINITE))
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
# forever = Counted(Always(), (5, INFINITE))
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
# forever = Counted(Always(), (INFINITE, INFINITE))
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
# forever = Counted(Always(), (1000, INFINITE - 1))
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
#
# def test_break_infinite_loop_Interleave(self):
# forever = Interleave(Always(), repetitions = [(0, INFINITE)])
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
# forever = Interleave(Always(), Always(),
# repetitions = [(5, INFINITE), (INFINITE, INFINITE)])
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
# forever = Interleave(Always(), repetitions = [(1000, INFINITE - 1)])
# result = Grammar(forever)('') # if this takes very long, something is wrong
# assert repr(result) == "Node(':EMPTY', '')", repr(result)
#
# # def test_infinite_loops(self):
# # minilang = """forever = { // } \n"""
# # try:
# # parser_class = grammar_provider(minilang)
# # except CompilationError as error:
# # assert all(e.code == INFINITE_LOOP for e in error.errors)
# # save = get_config_value('static_analysis')
# # set_config_value('static_analysis', 'late')
# # provider = grammar_provider(minilang)
# # try:
# # parser = provider()
# # except GrammarError as error:
# # assert error.errors[0][2].code == INFINITE_LOOP
# # set_config_value('static_analysis', 'none')
# # parser = provider()
# # snippet = " "
# # syntax_tree = parser(snippet)
# # assert any(e.code == INFINITE_LOOP for e in syntax_tree.errors)
# # res = parser.static_analysis()
# # assert res and res[0][2].code == INFINITE_LOOP
# # minilang = """not_forever = { / / } \n"""
# # parser = grammar_provider(minilang)()
# # res = parser.static_analysis()
# # assert not res
# # set_config_value('static_analysis', save)
# class TestStaticAnalysis:
......@@ -823,7 +823,7 @@ class TestPopRetrieve:
st = self.minilang_parser4(test2)
log_parsing_history(self.minilang_parser4, "optional_match")
print(st.as_sxpr())
assert not st.error_flag
# assert not st.error_flag
# test3 = '<info>Hey, <emph>you</></>'
# st = self.minilang_parser4(test3)
# assert not st.error_flag
......
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