Commit 9af7ce39 authored by eckhart's avatar eckhart

parse.py/trace.py -- moved generation of skipping error message and history record to trace.py

parent 94a4b568
......@@ -1935,27 +1935,15 @@ class MandatoryElementsParser(NaryParser):
for pname, _ in reversed(grammar.call_stack__):
if not pname.startswith(':'):
break
msg = '%s expected by parser %s, »%s« found!' % (expected, pname, found)
msg = '%s expected by parser %s, »%s« found!' % (expected, repr(pname), found)
else:
msg = '%s expected, »%s« found!' % (expected, found)
error = Error(msg, location, Error.MANDATORY_CONTINUATION_AT_EOF
if (failed_on_lookahead and not text_) else Error.MANDATORY_CONTINUATION)
grammar.tree__.add_error(err_node, error)
if reloc >= 0:
errors = [error]
if grammar.resume_notices__:
target = text_[reloc:]
if len(target) >= 10:
target = target[:7] + '...'
notice = Error('Skipping within parser {} to point {}'
.format(self.pname or self.ptype, repr(target)),
self._grammar.document_length__ - len(text_), Error.RESUME_NOTICE)
grammar.tree__.add_error(err_node, notice)
errors.append(notice)
if grammar.history_tracking__:
lc = line_col(grammar.document_lbreaks__, location)
grammar.history__.append(HistoryRecord(
grammar.call_stack__, None, text_, lc, errors))
# signal error to tracer directly, because this error is not raised!
grammar.most_recent_error__ = ParserError(err_node, text_, error, first_throw=False)
return error, err_node, text_[i:]
......
......@@ -28,7 +28,7 @@ from typing import Tuple, Optional, List, Iterable, Union
from DHParser.error import Error, line_col
from DHParser.stringview import StringView
from DHParser.syntaxtree import Node, REGEXP_PTYPE, TOKEN_PTYPE, WHITESPACE_PTYPE, PLACEHOLDER
from DHParser.syntaxtree import Node, REGEXP_PTYPE, TOKEN_PTYPE, WHITESPACE_PTYPE, ZOMBIE_TAG
from DHParser.log import HistoryRecord
from DHParser.parse import Grammar, Parser, ParserError, ParseFunc
......@@ -45,18 +45,37 @@ def trace_history(self: Parser, text: StringView) -> Tuple[Optional[Node], Strin
# `parse.MandatoryElementsParser.mandatory_violation()`
pe = grammar.most_recent_error__
grammar.most_recent_error__ = None
errors = [pe.error]
# ignore inflated length due to gap jumping (see parse.Parser.__call__)
l = sum(len(nd) for nd in pe.node.select_if(lambda n: True, include_root=True)
if not nd.children and nd.tag_name != ZOMBIE_TAG)
text_ = pe.rest[l:]
lc = line_col(grammar.document_lbreaks__, pe.error.pos)
# grammar.history__.append(
# HistoryRecord(grammar.call_stack__, pe.node, text_, lc, [pe.error]))
if grammar.resume_notices__:
text_ = pe.rest[len(pe.node):]
target = text_
target = text
if len(target) >= 10:
target = target[:7] + '...'
notice = Error('Resuming from parser "{}" with parser "{}" at point: {}'
.format(pe.node.tag_name, grammar.call_stack__[-1][0], repr(target)),
grammar.document_length__ - len(text_), Error.RESUME_NOTICE)
if pe.first_throw:
# resume notice
notice = Error('Resuming from parser "{}" with parser "{}" at point: {}'
.format(pe.node.tag_name, grammar.call_stack__[-1][0],
repr(target)),
grammar.document_length__ - len(text_), Error.RESUME_NOTICE)
else:
# skip notice
notice = Error('Skipping within parser {} to point {}'
.format(grammar.call_stack__[-1][0], repr(target)),
self._grammar.document_length__ - len(text_), Error.RESUME_NOTICE)
grammar.tree__.add_error(pe.node, notice)
lc = line_col(grammar.document_lbreaks__, location)
grammar.history__.append(HistoryRecord(
grammar.call_stack__, None, text_, lc, [notice]))
errors.append(notice)
grammar.history__.append(HistoryRecord(
grammar.call_stack__, pe.node, text_, lc, errors))
grammar.call_stack__.append(
((self.repr if self.tag_name in (REGEXP_PTYPE, TOKEN_PTYPE)
......@@ -64,16 +83,18 @@ def trace_history(self: Parser, text: StringView) -> Tuple[Optional[Node], Strin
grammar.moving_forward__ = True
try:
node, rest = self._parse(text)
node, rest = self._parse(text) # <===== call to the actual parser!
except ParserError as pe:
grammar.call_stack__.pop()
if pe.first_throw:
# add error message to history
grammar.most_recent_error__ = pe
lc = line_col(grammar.document_lbreaks__, pe.error.pos)
nd = pe.node
if self == grammar.start_parser__:
fe = grammar.most_recent_error__
lc = line_col(grammar.document_lbreaks__, fe.error.pos)
# TODO: get the call stack from when the error occured, here
nd = fe.node
grammar.history__.append(
HistoryRecord(grammar.call_stack__, nd, pe.rest[len(nd):], lc, [pe.error]))
HistoryRecord(grammar.call_stack__, nd, fe.rest[len(nd):], lc, [fe.error]))
raise pe
# Mind that memoized parser calls will not appear in the history record!
......
......@@ -26,12 +26,15 @@ import sys
scriptpath = os.path.dirname(__file__) or '.'
sys.path.append(os.path.abspath(os.path.join(scriptpath, '..')))
REVEAL = False
from DHParser import grammar_provider, all_descendants, \
set_tracer, trace_history, log_parsing_history, start_logging, log_dir, \
set_config_value, resume_notices_on, Error
def get_history(name, show: bool = False) -> str:
def get_history(name, show: bool = REVEAL) -> str:
history_fname = os.path.join(log_dir() or '', name + "_full_parser.log.html")
if show:
import webbrowser, time
......@@ -42,6 +45,12 @@ def get_history(name, show: bool = False) -> str:
return history_file
def reveal(grammar, name):
if REVEAL:
log_parsing_history(grammar, name)
get_history(name, show=True)
class TestTrace:
def setup(self):
start_logging()
......@@ -186,8 +195,7 @@ class TestErrorReporting:
break
else:
assert False, "Missing Error!"
# log_parsing_history(gr, 'trace_noskip')
# get_history('trace_noskip')
reveal(gr, 'trace_noskip')
def test_trace_skip_clause(self):
lang = """
......@@ -197,15 +205,15 @@ class TestErrorReporting:
"""
gr = grammar_provider(lang)()
resume_notices_on(gr)
_ = gr('AB_D')
st = gr('AB_D')
assert 'Skipping' in str(st.errors_sorted[1])
for record in gr.history__:
if record.status.startswith(record.ERROR):
assert record.excerpt == '_D'
break
else:
assert False, "Missing Error!"
# log_parsing_history(gr, 'trace_skip_clause')
# get_history('trace_skip_clause')
reveal(gr, 'trace_skip_clause')
def test_trace_resume(self):
gr = self.gr; gr.resume_rules = dict()
......@@ -218,8 +226,7 @@ class TestErrorReporting:
assert cst.pick('alpha').content.startswith('ALPHA')
# because of resuming, there should be only one error message
assert len([err for err in cst.errors_sorted if err.code >= 1000]) == 1
# log_parsing_history(gr, 'trace_resume')
# get_history('trace_resume')
reveal(gr, 'trace_resume')
def test_trace_resume_complex_case(self):
lang = r"""
......@@ -232,19 +239,19 @@ class TestErrorReporting:
def mini_suite(grammar):
tree = grammar('abc/*x*/xyz')
assert not tree.errors
tree = grammar('abDxyz')
mandatory_cont = (Error.MANDATORY_CONTINUATION, Error.MANDATORY_CONTINUATION_AT_EOF)
assert len(tree.errors) > 1 and tree.errors[0].code in mandatory_cont
# log_parsing_history(grammar, 'trace_resume_complex_1')
# get_history('trace_resume_complex_1')
reveal(grammar, 'trace_resume_complex_1')
tree = grammar('abD/*x*/xyz')
assert len(tree.errors) > 1 and tree.errors[0].code in mandatory_cont
# log_parsing_history(grammar, 'trace_resume_complex_2')
# get_history('trace_resume_complex_2', show=True)
reveal(grammar, 'trace_resume_complex_2')
tree = grammar('aD /*x*/ c /* a */ /*x*/xyz')
assert len(tree.errors) > 1 and tree.errors[0].code in mandatory_cont
# log_parsing_history(grammar, 'trace_resume_complex_3')
# get_history('trace_resume_complex_3')
reveal(grammar, 'trace_resume_complex_3')
# test regex-defined resume rule
grammar = grammar_provider(lang)()
......
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