@@ -34,6 +34,8 @@ class Token(Str): | |||
self.value = value | |||
self.line = line | |||
self.column = column | |||
self.end_line = None | |||
self.end_column = None | |||
return self | |||
@classmethod | |||
@@ -112,6 +114,7 @@ class _Lex: | |||
if t: | |||
t.end_line = line_ctr.line | |||
t.end_column = line_ctr.column | |||
break | |||
else: | |||
if line_ctr.char_pos < len(stream): | |||
@@ -14,7 +14,8 @@ from .parsers.lalr_parser import UnexpectedToken | |||
from .common import is_terminal, GrammarError, LexerConf, ParserConf, PatternStr, PatternRE, TokenDef | |||
from .grammar import RuleOptions, Rule | |||
from .tree import Tree, Transformer, InlineTransformer, Visitor, SlottedTree as ST | |||
from .tree import Tree, Visitor, SlottedTree as ST | |||
from .transformers import Transformer_Children, Transformer_ChildrenInline | |||
__path__ = os.path.dirname(__file__) | |||
IMPORT_PATHS = [os.path.join(__path__, 'grammars')] | |||
@@ -130,7 +131,7 @@ RULES = { | |||
} | |||
class EBNF_to_BNF(InlineTransformer): | |||
class EBNF_to_BNF(Transformer_ChildrenInline): | |||
def __init__(self): | |||
self.new_rules = [] | |||
self.rules_by_expr = {} | |||
@@ -226,7 +227,7 @@ class SimplifyRule_Visitor(Visitor): | |||
tree.children = list(set(tree.children)) | |||
class RuleTreeToText(Transformer): | |||
class RuleTreeToText(Transformer_Children): | |||
def expansions(self, x): | |||
return x | |||
def expansion(self, symbols): | |||
@@ -237,7 +238,7 @@ class RuleTreeToText(Transformer): | |||
return expansion, alias.value | |||
class CanonizeTree(InlineTransformer): | |||
class CanonizeTree(Transformer_ChildrenInline): | |||
def maybe(self, expr): | |||
return ST('expr', [expr, Token('OP', '?', -1)]) | |||
@@ -247,7 +248,7 @@ class CanonizeTree(InlineTransformer): | |||
tokenmods, value = args | |||
return tokenmods + [value] | |||
class ExtractAnonTokens(InlineTransformer): | |||
class ExtractAnonTokens(Transformer_ChildrenInline): | |||
"Create a unique list of anonymous tokens. Attempt to give meaningful names to them when we add them" | |||
def __init__(self, tokens): | |||
@@ -351,7 +352,7 @@ def _literal_to_pattern(literal): | |||
'REGEXP': PatternRE }[literal.type](s, flags) | |||
class PrepareLiterals(InlineTransformer): | |||
class PrepareLiterals(Transformer_ChildrenInline): | |||
def literal(self, literal): | |||
return ST('pattern', [_literal_to_pattern(literal)]) | |||
@@ -363,13 +364,13 @@ class PrepareLiterals(InlineTransformer): | |||
regexp = '[%s-%s]' % (start, end) | |||
return ST('pattern', [PatternRE(regexp)]) | |||
class SplitLiterals(InlineTransformer): | |||
class SplitLiterals(Transformer_ChildrenInline): | |||
def pattern(self, p): | |||
if isinstance(p, PatternStr) and len(p.value)>1: | |||
return ST('expansion', [ST('pattern', [PatternStr(ch, flags=p.flags)]) for ch in p.value]) | |||
return ST('pattern', [p]) | |||
class TokenTreeToPattern(Transformer): | |||
class TokenTreeToPattern(Transformer_Children): | |||
def pattern(self, ps): | |||
p ,= ps | |||
return p | |||
@@ -2,6 +2,7 @@ from .common import is_terminal, GrammarError | |||
from .utils import suppress | |||
from .lexer import Token | |||
from .grammar import Rule | |||
from .tree import Tree | |||
###{standalone | |||
from functools import partial | |||
@@ -38,15 +39,23 @@ class PropagatePositions: | |||
if children: | |||
for a in children: | |||
with suppress(AttributeError): | |||
res.line = a.line | |||
res.column = a.column | |||
if isinstance(a, Tree): | |||
res.meta.line = a.meta.line | |||
res.meta.column = a.meta.column | |||
elif isinstance(a, Token): | |||
res.meta.line = a.line | |||
res.meta.column = a.column | |||
break | |||
for a in reversed(children): | |||
with suppress(AttributeError): | |||
res.end_line = a.end_line | |||
res.end_column = a.end_column | |||
# with suppress(AttributeError): | |||
if isinstance(a, Tree): | |||
res.meta.end_line = a.meta.end_line | |||
res.meta.end_column = a.meta.end_column | |||
elif isinstance(a, Token): | |||
res.meta.end_line = a.end_line | |||
res.meta.end_column = a.end_column | |||
break | |||
return res | |||
@@ -0,0 +1,93 @@ | |||
from functools import wraps | |||
from .tree import Tree | |||
class Discard(Exception): | |||
pass | |||
class Transformer: | |||
def _get_userfunc(self, name): | |||
return getattr(self, name) | |||
def _call_userfunc(self, tree): | |||
# Assumes tree is already transformed | |||
try: | |||
f = self._get_userfunc(tree.data) | |||
except AttributeError: | |||
return self.__default__(tree) | |||
else: | |||
return f(tree) | |||
def _transform(self, tree): | |||
children = [] | |||
for c in tree.children: | |||
try: | |||
children.append(self._transform(c) if isinstance(c, Tree) else c) | |||
except Discard: | |||
pass | |||
tree = Tree(tree.data, children) | |||
return self._call_userfunc(tree) | |||
def __default__(self, tree): | |||
return tree | |||
def transform(self, tree): | |||
return self._transform(tree) | |||
def __mul__(self, other): | |||
return TransformerChain(self, other) | |||
class Transformer_Children(Transformer): | |||
def _call_userfunc(self, tree): | |||
# Assumes tree is already transformed | |||
try: | |||
f = self._get_userfunc(tree.data) | |||
except AttributeError: | |||
return self.__default__(tree) | |||
else: | |||
return f(tree.children) | |||
class Transformer_ChildrenInline(Transformer): | |||
def _call_userfunc(self, tree): | |||
# Assumes tree is already transformed | |||
try: | |||
f = self._get_userfunc(tree.data) | |||
except AttributeError: | |||
return self.__default__(tree) | |||
else: | |||
return f(*tree.children) | |||
class TransformerChain(object): | |||
def __init__(self, *transformers): | |||
self.transformers = transformers | |||
def transform(self, tree): | |||
for t in self.transformers: | |||
tree = t.transform(tree) | |||
return tree | |||
def __mul__(self, other): | |||
return TransformerChain(*self.transformers + (other,)) | |||
#### XXX PSEUDOCODE TODO | |||
# def items(obj): | |||
# if isinstance(obj, Transformer): | |||
# def new_get_userfunc(self, name): | |||
# uf = self._get_userfunc(name) | |||
# def _f(tree): | |||
# return uf(tree.children) | |||
# return _f | |||
# obj._get_userfunc = new_get_userfunc | |||
# else: | |||
# assert callable(obj) | |||
# # apply decorator | |||
# def _f(tree): | |||
# return obj(tree.children) | |||
# return _f | |||
@@ -7,11 +7,23 @@ from copy import deepcopy | |||
from .utils import inline_args | |||
class Meta: | |||
pass | |||
###{standalone | |||
class Tree(object): | |||
__slots__ = ('data', 'children', '_meta', 'rule') | |||
def __init__(self, data, children): | |||
self.data = data | |||
self.children = children | |||
self._meta = None | |||
@property | |||
def meta(self): | |||
if self._meta is None: | |||
self._meta = Meta() | |||
return self._meta | |||
def __repr__(self): | |||
return 'Tree(%s, %s)' % (self.data, self.children) | |||
@@ -60,7 +60,7 @@ class TestParsers(unittest.TestCase): | |||
""", propagate_positions=True) | |||
r = g.parse('a') | |||
self.assertEqual( r.children[0].line, 1 ) | |||
self.assertEqual( r.children[0].meta.line, 1 ) | |||
def test_expand1(self): | |||