| @@ -1,3 +1,5 @@ | |||||
| from functools import partial | |||||
| from .common import is_terminal, GrammarError | from .common import is_terminal, GrammarError | ||||
| from .utils import suppress | from .utils import suppress | ||||
| from .lexer import Token | from .lexer import Token | ||||
| @@ -5,15 +7,7 @@ from .grammar import Rule | |||||
| ###{standalone | ###{standalone | ||||
| class NodeBuilder: | |||||
| def __init__(self, tree_class, name): | |||||
| self.tree_class = tree_class | |||||
| self.name = name | |||||
| def __call__(self, children): | |||||
| return self.tree_class(self.name, children) | |||||
| class Expand1: | |||||
| class ExpandSingleChild: | |||||
| def __init__(self, node_builder): | def __init__(self, node_builder): | ||||
| self.node_builder = node_builder | self.node_builder = node_builder | ||||
| @@ -23,57 +17,17 @@ class Expand1: | |||||
| else: | else: | ||||
| return self.node_builder(children) | return self.node_builder(children) | ||||
| class Factory: | |||||
| def __init__(self, cls, *args): | |||||
| self.cls = cls | |||||
| self.args = args | |||||
| def __call__(self, node_builder): | |||||
| return self.cls(node_builder, *self.args) | |||||
| class TokenWrapper: | |||||
| class CreateToken: | |||||
| "Used for fixing the results of scanless parsing" | "Used for fixing the results of scanless parsing" | ||||
| def __init__(self, node_builder, token_name): | |||||
| def __init__(self, token_name, node_builder): | |||||
| self.node_builder = node_builder | self.node_builder = node_builder | ||||
| self.token_name = token_name | self.token_name = token_name | ||||
| def __call__(self, children): | def __call__(self, children): | ||||
| return self.node_builder( [Token(self.token_name, ''.join(children))] ) | return self.node_builder( [Token(self.token_name, ''.join(children))] ) | ||||
| def identity(node_builder): | |||||
| return node_builder | |||||
| class ChildFilter: | |||||
| def __init__(self, node_builder, to_include): | |||||
| self.node_builder = node_builder | |||||
| self.to_include = to_include | |||||
| def __call__(self, children): | |||||
| filtered = [] | |||||
| for i, to_expand in self.to_include: | |||||
| if to_expand: | |||||
| filtered += children[i].children | |||||
| else: | |||||
| filtered.append(children[i]) | |||||
| return self.node_builder(filtered) | |||||
| def create_rule_handler(expansion, keep_all_tokens, filter_out): | |||||
| # if not keep_all_tokens: | |||||
| to_include = [(i, not is_terminal(sym) and sym.startswith('_')) | |||||
| for i, sym in enumerate(expansion) | |||||
| if keep_all_tokens | |||||
| or not ((is_terminal(sym) and sym.startswith('_')) or sym in filter_out) | |||||
| ] | |||||
| if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include): | |||||
| return Factory(ChildFilter, to_include) | |||||
| # else, if no filtering required.. | |||||
| return identity | |||||
| class PropagatePositions: | class PropagatePositions: | ||||
| def __init__(self, node_builder): | def __init__(self, node_builder): | ||||
| @@ -98,6 +52,31 @@ class PropagatePositions: | |||||
| return res | return res | ||||
| class ChildFilter: | |||||
| def __init__(self, to_include, node_builder): | |||||
| self.node_builder = node_builder | |||||
| self.to_include = to_include | |||||
| def __call__(self, children): | |||||
| filtered = [] | |||||
| for i, to_expand in self.to_include: | |||||
| if to_expand: | |||||
| filtered += children[i].children | |||||
| else: | |||||
| filtered.append(children[i]) | |||||
| return self.node_builder(filtered) | |||||
| def _should_expand(sym): | |||||
| return not is_terminal(sym) and sym.startswith('_') | |||||
| def maybe_create_child_filter(expansion, filter_out): | |||||
| to_include = [(i, _should_expand(sym)) for i, sym in enumerate(expansion) if sym not in filter_out] | |||||
| if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include): | |||||
| return partial(ChildFilter, to_include) | |||||
| class Callback(object): | class Callback(object): | ||||
| pass | pass | ||||
| @@ -112,22 +91,20 @@ class ParseTreeBuilder: | |||||
| self.user_aliases = {} | self.user_aliases = {} | ||||
| def _init_builders(self, rules): | def _init_builders(self, rules): | ||||
| filter_out = set() | |||||
| for rule in rules: | |||||
| if rule.options and rule.options.filter_out: | |||||
| assert rule.origin.startswith('_') # Just to make sure | |||||
| filter_out.add(rule.origin) | |||||
| filter_out = {rule.origin for rule in rules if rule.options and rule.options.filter_out} | |||||
| filter_out |= {sym for rule in rules for sym in rule.expansion if is_terminal(sym) and sym.startswith('_')} | |||||
| assert all(x.startswith('_') for x in filter_out) | |||||
| for rule in rules: | for rule in rules: | ||||
| options = rule.options | options = rule.options | ||||
| keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False) | keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False) | ||||
| expand1 = options.expand1 if options else False | |||||
| expand_single_child = options.expand1 if options else False | |||||
| create_token = options.create_token if options else False | create_token = options.create_token if options else False | ||||
| wrapper_chain = filter(None, [ | wrapper_chain = filter(None, [ | ||||
| (expand1 and not rule.alias) and Expand1, | |||||
| create_token and Factory(TokenWrapper, create_token), | |||||
| create_rule_handler(rule.expansion, keep_all_tokens, filter_out), | |||||
| create_token and partial(CreateToken, create_token), | |||||
| (expand_single_child and not rule.alias) and ExpandSingleChild, | |||||
| maybe_create_child_filter(rule.expansion, () if keep_all_tokens else filter_out), | |||||
| self.propagate_positions and PropagatePositions, | self.propagate_positions and PropagatePositions, | ||||
| ]) | ]) | ||||
| @@ -144,7 +121,7 @@ class ParseTreeBuilder: | |||||
| try: | try: | ||||
| f = transformer._get_func(user_callback_name) | f = transformer._get_func(user_callback_name) | ||||
| except AttributeError: | except AttributeError: | ||||
| f = NodeBuilder(self.tree_class, user_callback_name) | |||||
| f = partial(self.tree_class, user_callback_name) | |||||
| self.user_aliases[rule] = rule.alias | self.user_aliases[rule] = rule.alias | ||||
| rule.alias = internal_callback_name | rule.alias = internal_callback_name | ||||