diff --git a/lark/load_grammar.py b/lark/load_grammar.py index 051f8cd..9356d7e 100644 --- a/lark/load_grammar.py +++ b/lark/load_grammar.py @@ -73,6 +73,8 @@ TERMINALS = { '_RPAR': r'\)', '_LBRA': r'\[', '_RBRA': r'\]', + '_LBRACE': r'\{', + '_RBRACE': r'\}', 'OP': '[+*]|[?](?![a-z])', '_COLON': ':', '_COMMA': ',', @@ -97,7 +99,13 @@ TERMINALS = { RULES = { 'start': ['_list'], '_list': ['_item', '_list _item'], - '_item': ['rule', 'term', 'statement', '_NL'], + '_item': ['rule', 'rule_template', 'term', 'statement', '_NL'], + + 'template': ['RULE _LBRACE template_params _RBRACE _COLON expansions _NL', + 'RULE _LBRACE template_params _RBRACE _DOT NUMBER _COLON expansions _NL'], + + 'template_params': ['RULE', + 'template_params _COMMA RULE'], 'rule': ['RULE _COLON expansions _NL', 'RULE _DOT NUMBER _COLON expansions _NL'], @@ -123,7 +131,8 @@ RULES = { 'value': ['terminal', 'nonterminal', 'literal', - 'range'], + 'range', + 'template_usage'], 'terminal': ['TERMINAL'], 'nonterminal': ['RULE'], @@ -132,9 +141,13 @@ RULES = { 'maybe': ['_LBRA expansions _RBRA'], 'range': ['STRING _DOTDOT STRING'], + + 'template_usage': ['RULE _LBRACE template_args _RBRACE'], + 'template_args': ['atom', + 'template_args _COMMA atom'], 'term': ['TERMINAL _COLON expansions _NL', - 'TERMINAL _DOT NUMBER _COLON expansions _NL'], + 'TERMINAL _DOT NUMBER _COLON expansions _NL'], 'statement': ['ignore', 'import', 'declare'], 'ignore': ['_IGNORE expansions _NL'], 'declare': ['_DECLARE _declare_args _NL'], @@ -648,8 +661,8 @@ def resolve_term_references(term_defs): raise GrammarError("Recursion in terminal '%s' (recursion is only allowed in rules, not terminals)" % name) -def options_from_rule(name, *x): - if len(x) > 1: +def options_from_rule(name, *x,is_template=False): + if len(x) > (1+is_template): priority, expansions = x priority = int(priority) else: @@ -728,12 +741,14 @@ class GrammarLoader: defs = classify(tree.children, lambda c: c.data, lambda c: c.children) term_defs = defs.pop('term', []) rule_defs = defs.pop('rule', []) + template_defs = defs.pop('template', []) statements = defs.pop('statement', []) assert not defs term_defs = [td if len(td)==3 else (td[0], 1, td[1]) for td in term_defs] term_defs = [(name.value, (t, int(p))) for name, p, t in term_defs] rule_defs = [options_from_rule(*x) for x in rule_defs] + template_defs = [options_from_rule(*x, is_template=True) for x in rule_defs] # Execute statements ignore, imports = [], {}