from .common import is_terminal, GrammarError class Callback(object): pass def create_expand1_tree_builder_function(tree_builder): def expand1(children): if len(children) == 1: return children[0] else: return tree_builder(children) return expand1 def create_rule_handler(expansion, usermethod, keep_all_tokens): if not keep_all_tokens: to_include = [(i, sym.startswith('_')) for i, sym in enumerate(expansion) if not (is_terminal(sym) and sym.startswith('_'))] if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include): def _build_ast(match): children = [] for i, to_expand in to_include: if to_expand: children += match[i].children else: children.append(match[i]) return usermethod(children) return _build_ast # else, if no filtering required.. return usermethod class ParseTreeBuilder: def __init__(self, tree_class): self.tree_class = tree_class def _create_tree_builder_function(self, name): tree_class = self.tree_class def tree_builder_f(children): return tree_class(name, children) return tree_builder_f def create_tree_builder(self, rules, transformer): callback = Callback() new_rules = [] for origin, expansions in rules.items(): keep_all_tokens = False if origin.startswith('!'): origin=origin.lstrip('!') keep_all_tokens = True expand1 = origin.startswith('?') _origin = origin.lstrip('?') for expansion, alias in expansions: if alias and origin.startswith('_'): raise Exception("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases" % origin) if alias: alias = alias.lstrip('*') _alias = 'autoalias_%s_%s' % (_origin, '_'.join(expansion)) try: f = transformer._get_func(alias or _origin) except AttributeError: if alias: f = self._create_tree_builder_function(alias) else: f = self._create_tree_builder_function(_origin) if expand1: f = create_expand1_tree_builder_function(f) alias_handler = create_rule_handler(expansion, f, keep_all_tokens) if hasattr(callback, _alias): raise GrammarError("Rule expansion '%s' already exists in rule %s" % (' '.join(expansion), origin)) setattr(callback, _alias, alias_handler) new_rules.append(( _origin, expansion, _alias )) return new_rules, callback