| @@ -39,6 +39,7 @@ class LarkOptions(object): | |||
| postlex - Lexer post-processing (Default: None) | |||
| start - The start symbol (Default: start) | |||
| profile - Measure run-time usage in Lark. Read results from the profiler proprety (Default: False) | |||
| propagate_positions - Experimental. Don't use yet. | |||
| """ | |||
| __doc__ += OPTIONS_DOC | |||
| def __init__(self, options_dict): | |||
| @@ -55,6 +56,7 @@ class LarkOptions(object): | |||
| self.start = o.pop('start', 'start') | |||
| self.profile = o.pop('profile', False) | |||
| self.ambiguity = o.pop('ambiguity', 'auto') | |||
| self.propagate_positions = o.pop('propagate_positions', False) | |||
| assert self.parser in ('earley', 'lalr', None) | |||
| @@ -160,7 +162,7 @@ class Lark: | |||
| def _build_parser(self): | |||
| self.parser_class = get_frontend(self.options.parser, self.options.lexer) | |||
| self.parse_tree_builder = ParseTreeBuilder(self.options.tree_class) | |||
| self.parse_tree_builder = ParseTreeBuilder(self.options.tree_class, self.options.propagate_positions) | |||
| rules, callback = self.parse_tree_builder.create_tree_builder(self.rules, self.options.transformer) | |||
| if self.profiler: | |||
| for f in dir(callback): | |||
| @@ -155,17 +155,27 @@ class Lexer(object): | |||
| if m: | |||
| value = m.group(0) | |||
| type_ = type_from_index[m.lastindex] | |||
| if type_ not in ignore_types: | |||
| to_yield = type_ not in ignore_types | |||
| if to_yield: | |||
| t = Token(type_, value, lex_pos, line, lex_pos - col_start_pos) | |||
| if t.type in self.callback: | |||
| t = self.callback[t.type](t) | |||
| yield t | |||
| end_col = t.column + len(value) | |||
| if type_ in newline_types: | |||
| newlines = value.count(self.newline_char) | |||
| if newlines: | |||
| line += newlines | |||
| col_start_pos = lex_pos + value.rindex(self.newline_char) | |||
| last_newline_index = value.rindex(self.newline_char) + 1 | |||
| col_start_pos = lex_pos + last_newline_index | |||
| end_col = len(value) - last_newline_index | |||
| if to_yield: | |||
| t.end_line = line | |||
| t.end_col = end_col | |||
| yield t | |||
| lex_pos += len(value) | |||
| break | |||
| else: | |||
| @@ -1,4 +1,5 @@ | |||
| from .common import is_terminal, GrammarError | |||
| from .utils import suppress | |||
| from .lexer import Token | |||
| class Callback(object): | |||
| @@ -42,10 +43,31 @@ def create_rule_handler(expansion, usermethod, keep_all_tokens, filter_out): | |||
| # else, if no filtering required.. | |||
| return usermethod | |||
| def propagate_positions_wrapper(f): | |||
| def _f(args): | |||
| res = f(args) | |||
| if args: | |||
| for a in args: | |||
| with suppress(AttributeError): | |||
| res.line = a.line | |||
| res.column = a.column | |||
| break | |||
| for a in reversed(args): | |||
| with suppress(AttributeError): | |||
| res.end_line = a.end_line | |||
| res.end_col = a.end_col | |||
| break | |||
| return res | |||
| return _f | |||
| class ParseTreeBuilder: | |||
| def __init__(self, tree_class): | |||
| def __init__(self, tree_class, propagate_positions=False): | |||
| self.tree_class = tree_class | |||
| self.propagate_positions = propagate_positions | |||
| def _create_tree_builder_function(self, name): | |||
| tree_class = self.tree_class | |||
| @@ -92,6 +114,9 @@ class ParseTreeBuilder: | |||
| alias_handler = create_rule_handler(expansion, f, keep_all_tokens, filter_out) | |||
| if self.propagate_positions: | |||
| alias_handler = propagate_positions_wrapper(alias_handler) | |||
| callback_name = 'autoalias_%s_%s' % (_origin, '_'.join(expansion)) | |||
| if hasattr(callback, callback_name): | |||
| raise GrammarError("Rule expansion '%s' already exists in rule %s" % (' '.join(expansion), origin)) | |||
| @@ -1,6 +1,7 @@ | |||
| import functools | |||
| import types | |||
| from collections import deque | |||
| from contextlib import contextmanager | |||
| class fzset(frozenset): | |||
| def __repr__(self): | |||
| @@ -88,5 +89,24 @@ except NameError: | |||
| return -1 | |||
| try: | |||
| from contextlib import suppress # Python 3 | |||
| except ImportError: | |||
| @contextmanager | |||
| def suppress(*excs): | |||
| '''Catch and dismiss the provided exception | |||
| >>> x = 'hello' | |||
| >>> with suppress(IndexError): | |||
| ... x = x[10] | |||
| >>> x | |||
| 'hello' | |||
| ''' | |||
| try: | |||
| yield | |||
| except excs: | |||
| pass | |||