Commit b2c21cbd authored by Eckhart Arnold's avatar Eckhart Arnold
Browse files

- Forward-Parser __str__ method implemented;

  better error reporting for mandatory continuations
- example: BibTeX.enbf: bugs fixed
parent ce3ea52e
......@@ -201,10 +201,10 @@ class EBNFGrammar(Grammar):
regexp = RE('~?/(?:\\\\/|[^/])*?/~?')
literal = Alternative(RE('"(?:[^"]|\\\\")*?"'), RE("'(?:[^']|\\\\')*?'"))
symbol = RE('(?!\\d)\\w+')
option = Series(Token("["), expression, Token("]"), mandatory=2)
repetition = Series(Token("{"), expression, Token("}"), mandatory=2)
option = Series(Token("["), expression, Token("]"), mandatory=1)
repetition = Series(Token("{"), expression, Token("}"), mandatory=1)
oneormore = Series(Token("{"), expression, Token("}+"))
group = Series(Token("("), expression, Token(")"), mandatory=2)
group = Series(Token("("), expression, Token(")"), mandatory=1)
retrieveop = Alternative(Token("::"), Token(":"))
flowmarker = Alternative(Token("!"), Token("&"), Token("-!"), Token("-&"))
factor = Alternative(Series(Option(flowmarker), Option(retrieveop), symbol, NegativeLookahead(Token("="))),
......
......@@ -1433,9 +1433,9 @@ class Series(NaryOperator):
m = text.search(Series.RX_ARGUMENT)
i = max(1, text.index(m.regs[1][0])) if m else 1
node = Node(self, text_[:i])
text_ = text_[i:]
node.add_error('%s expected; "%s" found!' % (str(parser), text[:10]),
node.add_error('%s expected; "%s" found!' % (str(parser), text_[:10]),
code=Error.MANDATORY_CONTINUATION)
text_ = text_[i:]
results += (node,)
# if node.error_flag: # break on first error
# break
......@@ -1509,7 +1509,8 @@ class Alternative(NaryOperator):
super(Alternative, self).__init__(*parsers, name=name)
assert len(self.parsers) >= 1
# only the last alternative may be optional. Could this be checked at compile time?
assert all(not isinstance(p, Option) for p in self.parsers[:-1])
assert all(not isinstance(p, Option) for p in self.parsers[:-1]), \
"Parser-specification Error (EBNF): only the last alternative may be optional!"
self.been_here = dict() # type: Dict[int, int]
def __call__(self, text: StringView) -> Tuple[Node, StringView]:
......@@ -1793,18 +1794,31 @@ class Forward(Parser):
def __call__(self, text: StringView) -> Tuple[Node, StringView]:
return self.parser(text)
def __repr__(self):
def __cycle_guard(self, func, alt_return):
"""
Returns the value of `func()` or `alt_return` if a cycle has
been reached (which can happen if `func` calls methods of
child parsers).
"""
if self.cycle_reached:
return "..."
return alt_return
else:
self.cycle_reached = True
s = repr(self.parser)
ret = func()
self.cycle_reached = False
return s
return ret
def __repr__(self):
return self.__cycle_guard(lambda : repr(self.parser), '...')
def __str__(self):
return self.__cycle_guard(lambda : str(self.parser), '...')
def set(self, parser: Parser):
"""Sets the parser to which the calls to this Forward-object
shall be delegated."""
"""
Sets the parser to which the calls to this Forward-object
shall be delegated.
"""
self.parser = parser
def apply(self, func: Parser.ApplyFunc):
......
# BibTeX-Grammar
#######################################################################
#
# EBNF-Directives
#
######################################################################
@ testing = True
@ whitespace = /\s*/
@ ignorecase = True
@ comment = /%.*(?:\n|$)/
......@@ -45,4 +45,4 @@ WORD = /\w+/
WORD_ = /\w+/~
NO_BLANK_STRING = /[^ \t\n,%]+/~
COMMA_TERMINATED_STRING = { /[^,%]+/ | /(?=%)/~ }
CONTENT_STRING = { [^{}%]+ | /(?=%)/~ }
CONTENT_STRING = { /[^{}%]+/ | /(?=%)/~ }+
......@@ -22,10 +22,10 @@ flowmarker = "!" | "&" # '!' negative lookahead, '&' p
| "-!" | "-&" # '-' negative lookbehind, '-&' positive lookbehind
retrieveop = "::" | ":" # '::' pop, ':' retrieve
group = "(" expression §")"
group = "(" §expression ")"
oneormore = "{" expression "}+"
repetition = "{" expression §"}"
option = "[" expression §"]"
repetition = "{" §expression "}"
option = "[" §expression "]"
symbol = /(?!\d)\w+/~ # e.g. expression, factor, parameter_list
literal = /"(?:[^"]|\\")*?"/~ # e.g. "(", '+', 'while'
......
......@@ -274,16 +274,12 @@ class TestSeries:
series = "A" "B" §"C" "D"
"""
parser = grammar_provider(lang)()
st = parser("ABCD");
assert not st.error_flag
st = parser("A_CD");
assert not st.error_flag
st = parser("AB_D");
assert st.error_flag
st = parser("ABCD"); assert not st.error_flag
st = parser("A_CD"); assert not st.error_flag
st = parser("AB_D"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
# transitivity of mandatory-operator
st = parser("ABC_");
assert st.error_flag
st = parser("ABC_"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
def test_series_composition(self):
......@@ -293,31 +289,21 @@ class TestSeries:
combined = Alternative(s1 + s2, RegExp('.*'))
parser = Grammar(combined)
st = parser("ABCDE");
assert not st.error_flag
st = parser("A_CDE");
assert not st.error_flag
st = parser("AB_DE");
assert st.error_flag
st = parser("ABCDE"); assert not st.error_flag
st = parser("A_CDE"); assert not st.error_flag
st = parser("AB_DE"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
st = parser("ABC_E");
assert st.error_flag
st = parser("ABC_E"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
combined = Alternative(s2 + s1, RegExp('.*'))
parser = Grammar(combined)
st = parser("DEABC");
assert not st.error_flag
st = parser("_EABC");
assert not st.error_flag
st = parser("D_ABC");
assert not st.error_flag
st = parser("DE_BC");
assert not st.error_flag
st = parser("DEA_C");
assert not st.error_flag
st = parser("DEAB_");
assert st.error_flag
st = parser("DEABC"); assert not st.error_flag
st = parser("_EABC"); assert not st.error_flag
st = parser("D_ABC"); assert not st.error_flag
st = parser("DE_BC"); assert not st.error_flag
st = parser("DEA_C"); assert not st.error_flag
st = parser("DEAB_"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
......
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