@@ -49,19 +49,21 @@ class NonTerminal(Symbol): | |||||
class RuleOptions(Serialize): | class RuleOptions(Serialize): | ||||
__serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'empty_indices' | |||||
__serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' | |||||
def __init__(self, keep_all_tokens=False, expand1=False, priority=None, empty_indices=()): | |||||
def __init__(self, keep_all_tokens=False, expand1=False, priority=None, template_source=None, empty_indices=()): | |||||
self.keep_all_tokens = keep_all_tokens | self.keep_all_tokens = keep_all_tokens | ||||
self.expand1 = expand1 | self.expand1 = expand1 | ||||
self.priority = priority | self.priority = priority | ||||
self.template_source = template_source | |||||
self.empty_indices = empty_indices | self.empty_indices = empty_indices | ||||
def __repr__(self): | def __repr__(self): | ||||
return 'RuleOptions(%r, %r, %r)' % ( | |||||
return 'RuleOptions(%r, %r, %r, %r)' % ( | |||||
self.keep_all_tokens, | self.keep_all_tokens, | ||||
self.expand1, | self.expand1, | ||||
self.priority, | self.priority, | ||||
self.template_source | |||||
) | ) | ||||
@@ -383,17 +383,6 @@ class ApplyTemplates(Transformer_InPlace): | |||||
result_tree = deepcopy(tree) | result_tree = deepcopy(tree) | ||||
self.replacer.names = dict(zip(params, args)) | self.replacer.names = dict(zip(params, args)) | ||||
self.replacer.transform(result_tree) | self.replacer.transform(result_tree) | ||||
if name[0] != '_': | |||||
if result_tree.data == 'expansions': | |||||
t = result_tree | |||||
while len(t.children) == 2: | |||||
if t.children[-1].data != 'alias': | |||||
t.children[-1] = ST('alias', [t.children[-1], name]) | |||||
t = t.children[0] | |||||
if t.children[-1].data != 'alias': | |||||
t.children[-1] = ST('alias', [t.children[-1], name]) | |||||
elif result_tree.data != 'alias': | |||||
result_tree = ST('alias', [result_tree, name]) | |||||
self.rule_defs.append((result_name, [], result_tree, deepcopy(options))) | self.rule_defs.append((result_name, [], result_tree, deepcopy(options))) | ||||
return NonTerminal(result_name) | return NonTerminal(result_name) | ||||
@@ -736,7 +725,8 @@ def options_from_rule(name, params, *x): | |||||
expand1 = name.startswith('?') | expand1 = name.startswith('?') | ||||
name = name.lstrip('?') | name = name.lstrip('?') | ||||
return name, params, expansions, RuleOptions(keep_all_tokens, expand1, priority=priority) | |||||
return name, params, expansions, RuleOptions(keep_all_tokens, expand1, priority=priority, | |||||
template_source=(name if params else None)) | |||||
def symbols_from_strcase(expansion): | def symbols_from_strcase(expansion): | ||||
@@ -227,9 +227,10 @@ class ParseTreeBuilder: | |||||
options = rule.options | options = rule.options | ||||
keep_all_tokens = self.always_keep_all_tokens or options.keep_all_tokens | keep_all_tokens = self.always_keep_all_tokens or options.keep_all_tokens | ||||
expand_single_child = options.expand1 | expand_single_child = options.expand1 | ||||
from_template = options.template_source is not None | |||||
wrapper_chain = list(filter(None, [ | wrapper_chain = list(filter(None, [ | ||||
(expand_single_child and not rule.alias) and ExpandSingleChild, | |||||
(expand_single_child and not (rule.alias and not from_template)) and ExpandSingleChild, | |||||
maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), | maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), | ||||
self.propagate_positions and PropagatePositions, | self.propagate_positions and PropagatePositions, | ||||
self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), | self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), | ||||
@@ -243,7 +244,7 @@ class ParseTreeBuilder: | |||||
for rule, wrapper_chain in self.rule_builders: | for rule, wrapper_chain in self.rule_builders: | ||||
user_callback_name = rule.alias or rule.origin.name | |||||
user_callback_name = rule.alias or rule.options.template_source or rule.origin.name | |||||
try: | try: | ||||
f = getattr(transformer, user_callback_name) | f = getattr(transformer, user_callback_name) | ||||
# XXX InlineTransformer is deprecated! | # XXX InlineTransformer is deprecated! | ||||
@@ -863,6 +863,38 @@ def _make_parser_test(LEXER, PARSER): | |||||
x = g.parse("[1]") | x = g.parse("[1]") | ||||
self.assertSequenceEqual(x.children, [Tree('sep', ['1'])]) | self.assertSequenceEqual(x.children, [Tree('sep', ['1'])]) | ||||
def test_templates_alias(self): | |||||
g = _Lark(r""" | |||||
start: expr{"C"} | |||||
expr{t}: "A" t | |||||
| "B" t -> b | |||||
""") | |||||
x = g.parse("AC") | |||||
self.assertSequenceEqual(x.children, [Tree('expr', [])]) | |||||
x = g.parse("BC") | |||||
self.assertSequenceEqual(x.children, [Tree('b', [])]) | |||||
def test_templates_modifiers(self): | |||||
g = _Lark(r""" | |||||
start: expr{"B"} | |||||
!expr{t}: "A" t | |||||
""") | |||||
x = g.parse("AB") | |||||
self.assertSequenceEqual(x.children, [Tree('expr', ["A", "B"])]) | |||||
g = _Lark(r""" | |||||
start: _expr{"B"} | |||||
!_expr{t}: "A" t | |||||
""") | |||||
x = g.parse("AB") | |||||
self.assertSequenceEqual(x.children, ["A", "B"]) | |||||
g = _Lark(r""" | |||||
start: expr{b} | |||||
b: "B" | |||||
?expr{t}: "A" t | |||||
""") | |||||
x = g.parse("AB") | |||||
self.assertSequenceEqual(x.children, [Tree('b',[])]) | |||||
def test_g_regex_flags(self): | def test_g_regex_flags(self): | ||||
g = _Lark(""" | g = _Lark(""" | ||||
start: "a" /b+/ C | start: "a" /b+/ C | ||||