Commit 9af7ce39 authored by eckhart's avatar eckhart
Browse files

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

parent 94a4b568
Loading
Loading
Loading
Loading
+3 −15
Original line number Diff line number Diff line
@@ -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:]


+35 −14
Original line number Diff line number Diff line
@@ -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] + '...'
            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)),
                               .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)
            errors.append(notice)

        grammar.history__.append(HistoryRecord(
                grammar.call_stack__, None, text_, lc, [notice]))
            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!
+21 −14
Original line number Diff line number Diff line
@@ -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)()