ThirdParty/Jasy/jasy/js/parse/Node.py

branch
maintenance
changeset 6693
3629d88ae235
parent 6647
2a11e1b2dcbe
parent 6692
c104c120e043
child 6694
1cccd74fd355
equal deleted inserted replaced
6647:2a11e1b2dcbe 6693:3629d88ae235
1 #
2 # Jasy - Web Tooling Framework
3 # Copyright 2010-2012 Zynga Inc.
4 #
5
6 #
7 # License: MPL 1.1/GPL 2.0/LGPL 2.1
8 # Authors:
9 # - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004)
10 # - Sebastian Werner <info@sebastian-werner.net> (Refactoring Python) (2010)
11 #
12
13 from __future__ import unicode_literals
14
15 import json
16 import copy
17
18 class Node(list):
19
20 __slots__ = [
21 # core data
22 "line", "type", "tokenizer", "start", "end", "rel", "parent",
23
24 # dynamic added data by other modules
25 "comments", "scope",
26
27 # node type specific
28 "value", "expression", "body", "functionForm", "parenthesized",
29 "fileId", "params", "name", "readOnly", "initializer", "condition",
30 "isLoop", "isEach", "object", "assignOp", "iterator", "thenPart",
31 "exception", "elsePart", "setup", "postfix", "update", "tryBlock",
32 "block", "defaultIndex", "discriminant", "label", "statements",
33 "finallyBlock", "statement", "variables", "names", "guard", "for",
34 "tail", "expressionClosure"
35 ]
36
37
38 def __init__(self, tokenizer=None, type=None, args=[]):
39 list.__init__(self)
40
41 self.start = 0
42 self.end = 0
43 self.line = None
44
45 if tokenizer:
46 token = getattr(tokenizer, "token", None)
47 if token:
48 # We may define a custom type but use the same positioning as another token
49 # e.g. transform curlys in block nodes, etc.
50 self.type = type if type else getattr(token, "type", None)
51 self.line = token.line
52
53 # Start & end are file positions for error handling.
54 self.start = token.start
55 self.end = token.end
56
57 else:
58 self.type = type
59 self.line = tokenizer.line
60 self.start = None
61 self.end = None
62
63 self.tokenizer = tokenizer
64
65 elif type:
66 self.type = type
67
68 for arg in args:
69 self.append(arg)
70
71
72 def getUnrelatedChildren(self):
73 """Collects all unrelated children"""
74
75 collection = []
76 for child in self:
77 if not hasattr(child, "rel"):
78 collection.append(child)
79
80 return collection
81
82
83 def getChildrenLength(self, filter=True):
84 """Number of (per default unrelated) children"""
85
86 count = 0
87 for child in self:
88 if not filter or not hasattr(child, "rel"):
89 count += 1
90 return count
91
92
93 def remove(self, kid):
94 """Removes the given kid"""
95
96 if not kid in self:
97 raise Exception("Given node is no child!")
98
99 if hasattr(kid, "rel"):
100 delattr(self, kid.rel)
101 del kid.rel
102 del kid.parent
103
104 list.remove(self, kid)
105
106
107 def insert(self, index, kid):
108 """Inserts the given kid at the given index"""
109
110 if index is None:
111 return self.append(kid)
112
113 if hasattr(kid, "parent"):
114 kid.parent.remove(kid)
115
116 kid.parent = self
117
118 return list.insert(self, index, kid)
119
120
121 def append(self, kid, rel=None):
122 """Appends the given kid with an optional relation hint"""
123
124 # kid can be null e.g. [1, , 2].
125 if kid:
126 if hasattr(kid, "parent"):
127 kid.parent.remove(kid)
128
129 # Debug
130 if not isinstance(kid, Node):
131 raise Exception("Invalid kid: %s" % kid)
132
133 if hasattr(kid, "tokenizer"):
134 if hasattr(kid, "start"):
135 if not hasattr(self, "start") or \
136 self.start == None or \
137 kid.start < self.start:
138 self.start = kid.start
139
140 if hasattr(kid, "end"):
141 if not hasattr(self, "end") or \
142 self.end == None or \
143 self.end < kid.end:
144 self.end = kid.end
145
146 kid.parent = self
147
148 # alias for function
149 if rel != None:
150 setattr(self, rel, kid)
151 setattr(kid, "rel", rel)
152
153 # Block None kids when they should be related
154 if not kid and rel:
155 return
156
157 return list.append(self, kid)
158
159
160 def replace(self, kid, repl):
161 """Replaces the given kid with a replacement kid"""
162
163 if repl in self:
164 self.remove(repl)
165
166 self[self.index(kid)] = repl
167
168 if hasattr(kid, "rel"):
169 repl.rel = kid.rel
170 setattr(self, kid.rel, repl)
171
172 # cleanup old kid
173 delattr(kid, "rel")
174
175
176 elif hasattr(repl, "rel"):
177 # delete old relation on new child
178 delattr(repl, "rel")
179
180 delattr(kid, "parent")
181 repl.parent = self
182
183 return kid
184
185
186 def toXml(self, format=True, indent=0, tab=" "):
187 """Converts the node to XML"""
188
189 lead = tab * indent if format else ""
190 innerLead = tab * (indent+1) if format else ""
191 lineBreak = "\n" if format else ""
192
193 relatedChildren = []
194 attrsCollection = []
195
196 for name in self.__slots__:
197 # "type" is used as node name - no need to repeat it as an attribute
198 # "parent" is a relation to the parent node - for serialization we ignore these at the moment
199 # "rel" is used internally to keep the relation to the parent - used by nodes which need to keep track of specific children
200 # "start" and "end" are for debugging only
201 if hasattr(self, name) and name not in ("type", "parent", "comments", "rel", "start", "end") and name[0] != "_":
202 value = getattr(self, name)
203 if isinstance(value, Node):
204 if hasattr(value, "rel"):
205 relatedChildren.append(value)
206
207 elif type(value) in (bool, int, float, str, list, set, dict):
208 if type(value) == bool:
209 value = "true" if value else "false"
210 elif type(value) in (int, float):
211 value = str(value)
212 elif type(value) in (list, set, dict):
213 if type(value) == dict:
214 value = value.keys()
215 if len(value) == 0:
216 continue
217 try:
218 value = ",".join(value)
219 except TypeError:
220 raise Exception("Invalid attribute list child at: %s" % name)
221
222 attrsCollection.append('%s=%s' % (name, json.dumps(value)))
223
224 attrs = (" " + " ".join(attrsCollection)) if len(attrsCollection) > 0 else ""
225
226 comments = getattr(self, "comments", None)
227 scope = getattr(self, "scope", None)
228
229 if len(self) == 0 and len(relatedChildren) == 0 and (not comments or len(comments) == 0) and not scope:
230 result = "%s<%s%s/>%s" % (lead, self.type, attrs, lineBreak)
231
232 else:
233 result = "%s<%s%s>%s" % (lead, self.type, attrs, lineBreak)
234
235 if comments:
236 for comment in comments:
237 result += '%s<comment context="%s" variant="%s">%s</comment>%s' % (innerLead, comment.context, comment.variant, comment.text, lineBreak)
238
239 if scope:
240 for statKey in scope:
241 statValue = scope[statKey]
242 if statValue != None and len(statValue) > 0:
243 if type(statValue) is set:
244 statValue = ",".join(statValue)
245 elif type(statValue) is dict:
246 statValue = ",".join(statValue.keys())
247
248 result += '%s<stat name="%s">%s</stat>%s' % (innerLead, statKey, statValue, lineBreak)
249
250 for child in self:
251 if not child:
252 result += "%s<none/>%s" % (innerLead, lineBreak)
253 elif not hasattr(child, "rel"):
254 result += child.toXml(format, indent+1)
255 elif not child in relatedChildren:
256 raise Exception("Oops, irritated by non related: %s in %s - child says it is related as %s" % (child.type, self.type, child.rel))
257
258 for child in relatedChildren:
259 result += "%s<%s>%s" % (innerLead, child.rel, lineBreak)
260 result += child.toXml(format, indent+2)
261 result += "%s</%s>%s" % (innerLead, child.rel, lineBreak)
262
263 result += "%s</%s>%s" % (lead, self.type, lineBreak)
264
265 return result
266
267
268 def __deepcopy__(self, memo):
269 """Used by deepcopy function to clone Node instances"""
270
271 # Create copy
272 if hasattr(self, "tokenizer"):
273 result = Node(tokenizer=self.tokenizer)
274 else:
275 result = Node(type=self.type)
276
277 # Copy children
278 for child in self:
279 if child is None:
280 list.append(result, None)
281 else:
282 # Using simple list appends for better performance
283 childCopy = copy.deepcopy(child, memo)
284 childCopy.parent = result
285 list.append(result, childCopy)
286
287 # Sync attributes
288 # Note: "parent" attribute is handled by append() already
289 for name in self.__slots__:
290 if hasattr(self, name) and not name in ("parent", "tokenizer"):
291 value = getattr(self, name)
292 if value is None:
293 pass
294 elif type(value) in (bool, int, float, str):
295 setattr(result, name, value)
296 elif type(value) in (list, set, dict, Node):
297 setattr(result, name, copy.deepcopy(value, memo))
298 # Scope can be assigned (will be re-created when needed for the copied node)
299 elif name == "scope":
300 result.scope = self.scope
301
302 return result
303
304
305 def getSource(self):
306 """Returns the source code of the node"""
307
308 if not self.tokenizer:
309 raise Exception("Could not find source for node '%s'" % node.type)
310
311 if getattr(self, "start", None) is not None:
312 if getattr(self, "end", None) is not None:
313 return self.tokenizer.source[self.start:self.end]
314 return self.tokenizer.source[self.start:]
315
316 if getattr(self, "end", None) is not None:
317 return self.tokenizer.source[:self.end]
318
319 return self.tokenizer.source[:]
320
321
322 # Map Python built-ins
323 __repr__ = toXml
324 __str__ = toXml
325
326
327 def __eq__(self, other):
328 return self is other
329
330 def __bool__(self):
331 return True

eric ide

mercurial