Commit b0fe4ce4 authored by eckhart's avatar eckhart
Browse files

parse.py: more static analyses

parent ba7079c2
......@@ -66,7 +66,7 @@ __all__ = ('ErrorCode',
'OPTIONAL_REDUNDANTLY_NESTED_WARNING',
'MANDATORY_CONTINUATION',
'MANDATORY_CONTINUATION_AT_EOF',
'PARSER_DID_NOT_MATCH',
'PARSER_NEVER_TOUCHES_DOCUMENT',
'PARSER_LOOKAHEAD_FAILURE_ONLY',
'PARSER_STOPPED_BEFORE_END',
'PARSER_LOOKAHEAD_MATCH_ONLY',
......@@ -121,7 +121,7 @@ OPTIONAL_REDUNDANTLY_NESTED_WARNING = ErrorCode(630)
MANDATORY_CONTINUATION = ErrorCode(1010)
MANDATORY_CONTINUATION_AT_EOF = ErrorCode(1015)
PARSER_DID_NOT_MATCH = ErrorCode(1020)
PARSER_NEVER_TOUCHES_DOCUMENT = ErrorCode(1020)
PARSER_LOOKAHEAD_FAILURE_ONLY = ErrorCode(1030)
PARSER_STOPPED_BEFORE_END = ErrorCode(1040)
PARSER_LOOKAHEAD_MATCH_ONLY = ErrorCode(1045)
......
This diff is collapsed.
Running TestInfiLoopsAndRecursion.test_direct_left_recursion2
140202417032496 ex <class 'DHParser.parse.Alternative'> 2
140202417214968 expr <class 'DHParser.parse.Synonym'> 1
140202417214968 expr <class 'DHParser.parse.Synonym'>
140202417032496 ex <class 'DHParser.parse.Alternative'>
[]
Traceback (most recent call last):
File "test_parse.py", line 1432, in <module>
runner("", globals())
File "/home/eckhart/Entwicklung/DHParser/DHParser/testing.py", line 854, in runner
run_tests_in_class(cls_name, namespace, methods)
File "/home/eckhart/Entwicklung/DHParser/DHParser/testing.py", line 774, in run_tests_in_class
func()
File "test_parse.py", line 138, in test_direct_left_recursion2
parser = grammar_provider(minilang)()
File "/home/eckhart/Entwicklung/DHParser/DHParser/dsl.py", line 278, in grammar_provider
get_ebnf_transformer(), get_ebnf_compiler(branding, ebnf_src))
File "/home/eckhart/Entwicklung/DHParser/DHParser/dsl.py", line 199, in compileDSL
raise CompilationError(only_errors(messages), src, grammar_src, AST, result)
DHParser.dsl.CompilationError: 2:13: Error (1020): Parser expr:Synonym in definition of "expr" as expr = ex is entirely cyclical and, therefore, cannot even touch the parsed document
Running TestInfiLoopsAndRecursion.test_direct_left_recursion2
139757817540280 expr <class 'DHParser.parse.Synonym'> 1
139757817456456 ex <class 'DHParser.parse.Alternative'> 2
[]
139757817456456 ex <class 'DHParser.parse.Alternative'>
139757817540280 expr <class 'DHParser.parse.Synonym'>
......@@ -33,7 +33,7 @@ from DHParser import compile_source, INFINITE, Interleave
from DHParser.configuration import access_thread_locals, get_config_value, \
EBNF_ANY_SYNTAX_HEURISTICAL, EBNF_ANY_SYNTAX_STRICT, EBNF_CLASSIC_SYNTAX, \
EBNF_REGULAR_EXPRESSION_SYNTAX, EBNF_PARSING_EXPRESSION_GRAMMAR_SYNTAX, set_config_value
from DHParser.error import has_errors, Error, PARSER_DID_NOT_MATCH, MANDATORY_CONTINUATION, \
from DHParser.error import has_errors, Error, MANDATORY_CONTINUATION, PARSER_STOPPED_BEFORE_END, \
REDEFINED_DIRECTIVE, UNUSED_ERROR_HANDLING_WARNING, AMBIGUOUS_ERROR_HANDLING
from DHParser.syntaxtree import WHITESPACE_PTYPE
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, EBNFTransform, \
......@@ -877,7 +877,7 @@ class TestInterleaveResume:
assert st.error_flag
assert len(st.errors) == 1
st = gr('A_BCDEFG.')
assert len(st.errors) == 1 and st.errors[0].code == PARSER_DID_NOT_MATCH
assert len(st.errors) == 1 and st.errors[0].code == PARSER_STOPPED_BEFORE_END
st = gr('AB_CDEFG.')
# mandatory continuation error kicks in only, if the parsers before
# the §-sign have been exhausted!
......@@ -943,7 +943,7 @@ class TestSyntaxExtensions:
st = parser("A")
assert not st.errors and st.tag_name == "doc" and st.content == "A"
st = parser("E")
assert st.errors and any(e.code == PARSER_DID_NOT_MATCH for e in st.errors)
assert st.errors and any(e.code == PARSER_STOPPED_BEFORE_END for e in st.errors)
def test_any_char(self):
lang = 'doc = "A".'
......
......@@ -31,8 +31,9 @@ sys.path.append(os.path.abspath(os.path.join(scriptpath, '..')))
from DHParser.configuration import get_config_value, set_config_value
from DHParser.toolkit import compile_python_object, re
from DHParser.log import is_logging, log_ST, log_parsing_history
from DHParser.error import Error, is_error, adjust_error_locations, MANDATORY_CONTINUATION, PARSER_DID_NOT_MATCH, \
MALFORMED_ERROR_STRING, MANDATORY_CONTINUATION_AT_EOF, RESUME_NOTICE, PARSER_STOPPED_BEFORE_END
from DHParser.error import Error, is_error, adjust_error_locations, MANDATORY_CONTINUATION, \
MALFORMED_ERROR_STRING, MANDATORY_CONTINUATION_AT_EOF, RESUME_NOTICE, PARSER_STOPPED_BEFORE_END, \
PARSER_NEVER_TOUCHES_DOCUMENT
from DHParser.parse import ParserError, Parser, Grammar, Forward, TKN, ZeroOrMore, RE, \
RegExp, Lookbehind, NegativeLookahead, OneOrMore, Series, Alternative, \
Interleave, UnknownParserError, CombinedParser, Token, EMPTY_NODE, Capture, Drop, Whitespace, \
......@@ -148,7 +149,6 @@ class TestInfiLoopsAndRecursion:
Value = /[0-9.]+/~ | '(' §Expr ')'
"""
parser = grammar_provider(minilang)()
assert parser
snippet = "8 * 4"
syntax_tree = parser(snippet)
assert not is_error(syntax_tree.error_flag), syntax_tree.errors_sorted
......@@ -204,7 +204,7 @@ class TestInfiLoopsAndRecursion:
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, 1000000000000))
forever = Counted(Always(), (1000, INFINITE - 1))
result = Grammar(forever)('') # if this takes very long, something is wrong
assert repr(result) == "Node(':EMPTY', '')", repr(result)
......@@ -216,7 +216,7 @@ class TestInfiLoopsAndRecursion:
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, 1000000000000)])
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)
......@@ -395,7 +395,7 @@ class TestGrammar:
pyparser, messages, _ = compile_source(grammar, None, get_ebnf_grammar(),
get_ebnf_transformer(), get_ebnf_compiler("PosTest"))
assert pyparser
assert not messages
assert not messages, str(messages)
def test_pos_values_initialized(self):
# checks whether pos values in the parsing result and in the
......@@ -952,9 +952,9 @@ class TestBorderlineCases:
cst = gr('X', 'parser')
assert not cst.error_flag
cst = gr(' ', 'parser')
assert cst.error_flag and cst.errors_sorted[0].code == PARSER_DID_NOT_MATCH
assert cst.error_flag and cst.errors_sorted[0].code == PARSER_STOPPED_BEFORE_END
cst = gr('', 'parser')
assert cst.error_flag and cst.errors_sorted[0].code == PARSER_DID_NOT_MATCH
assert cst.error_flag and cst.errors_sorted[0].code == PARSER_STOPPED_BEFORE_END
def test_matching(self):
minilang = """parser = /.?/"""
......@@ -1399,7 +1399,14 @@ class TestParserCombining:
class TestStaticAnalysis:
def test_1(self):
def setup(self):
self.static_analysis = get_config_value('static_analysis')
set_config_value('static_analysis', 'early')
def teardown(self):
set_config_value('static_analysis', self.static_analysis)
def test_cannot_capture_dropped_content(self):
p = Capture(Drop(Whitespace(" ")))
try:
gr = Grammar(p)
......@@ -1408,10 +1415,22 @@ class TestStaticAnalysis:
pass
def test_cyclical_ebnf_error(self):
lang = "doc = { doc } # this parser never reaches a leaf parser."
lang2 = "doc = word | sentence # a more convoluted example" \
"word = [sentence] doc" \
"sentence = { word }+ | sentence"
doc = Token('proper'); doc.pname = "doc"
grammar = Grammar(doc)
# grammar.static_analysis()
lang = "doc = 'proper' # this works!"
lang1 = "doc = { doc } # this parser never reaches a leaf parser."
lang2 = """doc = word | sentence # a more convoluted example
word = [sentence] doc
sentence = { word }+ | sentence"""
code, errors, ast = compile_ebnf(lang, preserve_AST=True)
assert not ast.errors
code, errors, ast = compile_ebnf(lang1, preserve_AST=True)
assert any(e.code == PARSER_NEVER_TOUCHES_DOCUMENT for e in errors)
code, errors, ast = compile_ebnf(lang2, preserve_AST=True)
assert any(e.code == PARSER_NEVER_TOUCHES_DOCUMENT for e in errors)
# for e in errors:
# print(e)
if __name__ == "__main__":
......
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