@@ -889,13 +889,15 @@ class GrammarBuilder: | |||
def _define(self, name, exp, params=(), options=None, override=False): | |||
if (name in self._definitions) ^ override: | |||
if override: | |||
self._grammar_error("Cannot override a nonexisting {type} {name}", name) | |||
else: | |||
if name in self._definitions: | |||
if not override: | |||
self._grammar_error("{Type} '{name}' defined more than once", name) | |||
elif override: | |||
self._grammar_error("Cannot override a nonexisting {type} {name}", name) | |||
if name.startswith('__'): | |||
self._grammar_error('Names starting with double-underscore are reserved (Error at {name})', name) | |||
self._definitions[name] = (params, exp, self._check_options(name, options)) | |||
def _extend(self, name, exp, params=(), options=None): | |||
@@ -1002,7 +1004,6 @@ class GrammarBuilder: | |||
tree = _parse_grammar(grammar_text, grammar_name) | |||
imports = {} # imports are collect over the whole file to prevent duplications | |||
for stmt in tree.children: | |||
if stmt.data == 'import': | |||
dotted_path, base_path, aliases = self._unpack_import(stmt, grammar_name) | |||
@@ -1077,7 +1078,7 @@ class GrammarBuilder: | |||
return s | |||
return mangle | |||
def check(self): | |||
def validate(self): | |||
for name, (params, exp, options) in self._definitions.items(): | |||
for i, p in enumerate(params): | |||
if p in self._definitions: | |||
@@ -1085,7 +1086,7 @@ class GrammarBuilder: | |||
if p in params[:i]: | |||
raise GrammarError("Duplicate Template Parameter %s (in template %s)" % (p, name)) | |||
if exp is None: # Remaining checks don't work for abstract rules/terminals | |||
if exp is None: # Remaining checks don't apply to abstract rules/terminals | |||
continue | |||
for temp in exp.find_data('template_usage'): | |||
@@ -1107,7 +1108,7 @@ class GrammarBuilder: | |||
raise GrammarError("Terminals %s were marked to ignore but were not defined!" % (set(self._ignore_names) - set(self._definitions))) | |||
def build(self): | |||
self.check() | |||
self.validate() | |||
rule_defs = [] | |||
term_defs = [] | |||
for name, (params, exp, options) in self._definitions.items(): | |||
@@ -35,11 +35,21 @@ class TestGrammar(TestCase): | |||
b = p.parse('[1, 2, 3, ]') | |||
assert a == b | |||
self.assertRaises(GrammarError, Lark, """ | |||
%import .test_templates_import (start, sep) | |||
%override sep{item}: item (delim item)* delim? | |||
""") | |||
self.assertRaises(GrammarError, Lark, """ | |||
%override sep{item}: item (delim item)* delim? | |||
""") | |||
def test_override_terminal(self): | |||
p = Lark(""" | |||
%import .grammars.ab (startab, A, B) | |||
%override A: "c" | |||
%override B: "d" | |||
""", start='startab', source_path=__file__) | |||
@@ -56,15 +66,63 @@ class TestGrammar(TestCase): | |||
a = p.parse('abab') | |||
self.assertEqual(a.children[0].children, ['a', Tree('expr', ['b', 'a']), 'b']) | |||
self.assertRaises(GrammarError, Lark, """ | |||
%extend expr: B A | |||
""") | |||
def test_extend_term(self): | |||
p = Lark(""" | |||
%import .grammars.ab (startab, A, B, expr) | |||
%extend A: "c" | |||
""", start='startab', source_path=__file__) | |||
a = p.parse('acbb') | |||
self.assertEqual(a.children[0].children, ['a', Tree('expr', ['c', 'b']), 'b']) | |||
def test_extend_twice(self): | |||
p = Lark(""" | |||
start: x+ | |||
x: "a" | |||
%extend x: "b" | |||
%extend x: "c" | |||
""") | |||
assert p.parse("abccbba") == p.parse("cbabbbb") | |||
def test_undefined_ignore(self): | |||
g = """!start: "A" | |||
%ignore B | |||
""" | |||
self.assertRaises( GrammarError, Lark, g) | |||
g = """!start: "A" | |||
%ignore start | |||
""" | |||
self.assertRaises( GrammarError, Lark, g) | |||
def test_alias_in_terminal(self): | |||
g = """start: TERM | |||
TERM: "a" -> alias | |||
""" | |||
self.assertRaises( GrammarError, Lark, g) | |||
def test_undefined_rule(self): | |||
self.assertRaises(GrammarError, Lark, """start: a""") | |||
def test_undefined_term(self): | |||
self.assertRaises(GrammarError, Lark, """start: A""") | |||
def test_token_multiline_only_works_with_x_flag(self): | |||
g = r"""start: ABC | |||
ABC: / a b c | |||
d | |||
e f | |||
/i | |||
""" | |||
self.assertRaises( GrammarError, Lark, g) | |||
if __name__ == '__main__': | |||
@@ -1380,12 +1380,6 @@ def _make_parser_test(LEXER, PARSER): | |||
# A: "a" """) | |||
# self.assertRaises(LexError, g.parse, 'aab') | |||
def test_undefined_rule(self): | |||
self.assertRaises(GrammarError, _Lark, """start: a""") | |||
def test_undefined_token(self): | |||
self.assertRaises(GrammarError, _Lark, """start: A""") | |||
def test_rule_collision(self): | |||
g = _Lark("""start: "a"+ "b" | |||
| "a"+ """) | |||
@@ -1619,15 +1613,6 @@ def _make_parser_test(LEXER, PARSER): | |||
x = g.parse('abcdef') | |||
self.assertEqual(x.children, ['abcdef']) | |||
def test_token_multiline_only_works_with_x_flag(self): | |||
g = r"""start: ABC | |||
ABC: / a b c | |||
d | |||
e f | |||
/i | |||
""" | |||
self.assertRaises( GrammarError, _Lark, g) | |||
@unittest.skipIf(PARSER == 'cyk', "No empty rules") | |||
def test_twice_empty(self): | |||
g = """!start: ("A"?)? | |||
@@ -1639,18 +1624,6 @@ def _make_parser_test(LEXER, PARSER): | |||
tree = l.parse('') | |||
self.assertEqual(tree.children, []) | |||
def test_undefined_ignore(self): | |||
g = """!start: "A" | |||
%ignore B | |||
""" | |||
self.assertRaises( GrammarError, _Lark, g) | |||
def test_alias_in_terminal(self): | |||
g = """start: TERM | |||
TERM: "a" -> alias | |||
""" | |||
self.assertRaises( GrammarError, _Lark, g) | |||
def test_line_and_column(self): | |||
g = r"""!start: "A" bc "D" | |||