forked from natural/java2python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathselector.py
More file actions
241 lines (181 loc) · 6.83 KB
/
selector.py
File metadata and controls
241 lines (181 loc) · 6.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# java2python.lang.selector -> declarative AST node selection
#
# This module provides classes for simple AST node selection that can be
# easily combined to form complex, declarative rules for retrieving AST
# nodes.
#
# The classes are similar to CSS selectors, with a nod to Python parsing
# libraries like LEPL and PyParsing. At the moment, only a few very
# basic selector types are implemented, but those that are here already
# provide all of the functionality necessary for use within the package.
#
# Projects using java2python should regard this subpackage as
# experimental. While the interfaces are not expected to change, the
# semantics may. Use with caution.
from java2python.lang import tokens
class Selector(object):
""" Base class for concrete selectors; provides operator methods. """
def __add__(self, other):
""" E + F
Like CSS "E + F": an F element immediately preceded by an E element
"""
return AdjacentSibling(self, other)
def __and__(self, other):
""" E & F
Like CSS "E F": an F element descendant of an E element
"""
return Descendant(self, other)
def __call__(self, *args, **kwds):
""" Subclasses must implement. """
raise NotImplementedError('Selector class cannot be called.')
def __getitem__(self, key):
""" E[n]
Like CSS "E:nth-child(n)": an E element, the n-th child of its parent
"""
return Nth(self, key)
def __gt__(self, other):
""" E > F
Like CSS: "E > F": an F element child of an E element
"""
return Child(self, other)
def __div__(self, other):
""" E / F
Produces a AnySibling.
"""
return AnySibling(self, other)
def walk(self, tree):
""" Select items from the tree and from the tree children. """
for item in self(tree):
yield item
for child in tree.children:
for item in self.walk(child):
yield item
class Token(Selector):
""" Token(...) -> select tokens by matching attributes.
Token is the most generic and flexible Selector; using it,
arbitrary nodes of any type, line number, position, and/or text
can be selected.
Calling Token() without any keywords is equivalent to:
Token(channel=None, index=None, input=None, line=None,
start=None, stop=None, text=None, type=None)
Note that the value of each keyword can be a constant or a
callable. When callables are specified, they are passed a the
token and should return a bool.
"""
def __init__(self, **attrs):
self.attrs = attrs
## we support strings so that the client can refer to the
## token name that way instead of via lookup or worse, integer.
if isinstance(attrs.get('type'), (basestring, )):
self.attrs['type'] = getattr(tokens, attrs.get('type'))
def __call__(self, tree):
items = self.attrs.items()
token = tree.token
def match_or_call(k, v):
if callable(v):
return v(token)
return getattr(token, k)==v
if all(match_or_call(k, v) for k, v in items if v is not None):
yield tree
def __str__(self):
items = self.attrs.items()
keys = ('{}={}'.format(k, v) for k, v in items if v is not None)
return 'Token({})'.format(', '.join(keys))
class Nth(Selector):
""" E[n] -> match any slice n of E
Similar to the :nth-child pseudo selector in CSS, but without the
support for keywords like 'odd', 'even', etc.
"""
def __init__(self, e, key):
self.e, self.key = e, key
def __call__(self, tree):
for etree in self.e(tree):
try:
matches = tree.children[self.key]
except (IndexError, ):
return
if not isinstance(matches, (list, )):
matches = [matches]
for child in matches:
yield child
def __str__(self):
return 'Nth({0})[{1}]'.format(self.e, self.key)
class Child(Selector):
""" E > F select any F that is a child of E """
def __init__(self, e, f):
self.e, self.f = e, f
def __call__(self, tree):
for ftree in self.f(tree):
for etree in self.e(tree.parent):
yield ftree
def __str__(self):
return 'Child({0} > {1})'.format(self.e, self.f)
class Type(Selector):
""" Type(T) select any token of type T
Similar to the type selector in CSS.
"""
def __init__(self, key, value=None):
self.key = key if isinstance(key, int) else getattr(tokens, key)
self.value = value
def __call__(self, tree):
if tree.token.type == self.key:
if self.value is None or self.value == tree.token.text:
yield tree
def __str__(self):
val = '' if self.value is None else '={0}'.format(self.value)
return 'Type({0}{1}:{2})'.format(tokens.map[self.key], val, self.key)
class Star(Selector):
""" * select any
Similar to the * selector in CSS.
"""
def __call__(self, tree):
yield tree
def __str__(self):
return 'Star(*)'
class Descendant(Selector):
""" E & F select any F that is a descendant of E """
def __init__(self, e, f):
self.e, self.f = e, f
def __call__(self, tree):
for ftree in self.f(tree):
root, ftree = ftree, ftree.parent
while ftree:
for etree in self.e(ftree):
yield root
ftree = ftree.parent
def __str__(self):
return 'Descendant({0} & {1})'.format(self.e, self.f)
class AdjacentSibling(Selector):
""" E + F select any F immediately preceded by a sibling E """
def __init__(self, e, f):
self.e, self.f = e, f
def __call__(self, node):
if not node.parent:
return
for ftree in self.f(node):
index = node.parent.children.index(ftree)
if not index:
return
previous = node.parent.children[index-1]
for child in self.e(previous):
yield ftree
def __str__(self):
return 'AdjacentSibling({} + {})'.format(self.e, self.f)
class AnySibling(Selector):
""" E / F select any F preceded by a sibling E """
def __init__(self, e, f):
self.e, self.f = e, f
def __call__(self, node):
if not node.parent:
return
for ftree in self.f(node):
index = node.parent.children.index(ftree)
if not index:
return
for prev in node.parent.children[:index]:
for child in self.e(prev):
yield ftree
def __str__(self):
return 'AnySibling({} / {})'.format(self.e, self.f)