ThirdParty/Jasy/jasy/script/output/Compressor.py

changeset 6650
1dd52aa8897c
equal deleted inserted replaced
6649:f1b3a73831c9 6650:1dd52aa8897c
1 #
2 # Jasy - Web Tooling Framework
3 # Copyright 2010-2012 Zynga Inc.
4 # Copyright 2013-2014 Sebastian Werner
5 #
6
7 from __future__ import unicode_literals
8
9 import re, sys, json
10
11 from jasy.script.tokenize.Lang import keywords
12 from jasy.script.parse.Lang import expressions, futureReserved
13
14 high_unicode = re.compile(r"\\u[2-9A-Fa-f][0-9A-Fa-f]{3}")
15 ascii_encoder = json.JSONEncoder(ensure_ascii=True)
16 unicode_encoder = json.JSONEncoder(ensure_ascii=False)
17
18 #
19 # Class
20 #
21
22 class Compressor:
23 __semicolonSymbol = ";"
24 __commaSymbol = ","
25
26
27 def __init__(self, format=None):
28 if format:
29 if format.has("semicolon"):
30 self.__semicolonSymbol = ";\n"
31
32 if format.has("comma"):
33 self.__commaSymbol = ",\n"
34
35 self.__forcedSemicolon = False
36
37
38
39 #
40 # Main
41 #
42
43 def compress(self, node):
44 type = node.type
45 result = None
46
47 if type in self.__simple:
48 result = type
49 elif type in self.__prefixes:
50 if getattr(node, "postfix", False):
51 result = self.compress(node[0]) + self.__prefixes[node.type]
52 else:
53 result = self.__prefixes[node.type] + self.compress(node[0])
54
55 elif type in self.__dividers:
56 first = self.compress(node[0])
57 second = self.compress(node[1])
58 divider = self.__dividers[node.type]
59
60 # Fast path
61 if node.type not in ("plus", "minus"):
62 result = "%s%s%s" % (first, divider, second)
63
64 # Special code for dealing with situations like x + ++y and y-- - x
65 else:
66 result = first
67 if first.endswith(divider):
68 result += " "
69
70 result += divider
71
72 if second.startswith(divider):
73 result += " "
74
75 result += second
76
77 else:
78 try:
79 result = getattr(self, "type_%s" % type)(node)
80 except AttributeError:
81 raise Exception("Script compressor does not support type '%s' from line %s in file %s" % (type, node.line, node.getFileName()))
82
83 if getattr(node, "parenthesized", None):
84 return "(%s)" % result
85 else:
86 return result
87
88
89
90 #
91 # Helpers
92 #
93
94 def __statements(self, node):
95 result = []
96 for child in node:
97 result.append(self.compress(child))
98
99 return "".join(result)
100
101 def __handleForcedSemicolon(self, node):
102 if node.type == "semicolon" and not hasattr(node, "expression"):
103 self.__forcedSemicolon = True
104
105 def __addSemicolon(self, result):
106 if not result.endswith(self.__semicolonSymbol):
107 if self.__forcedSemicolon:
108 self.__forcedSemicolon = False
109
110 return result + self.__semicolonSymbol
111
112 else:
113 return result
114
115 def __removeSemicolon(self, result):
116 if self.__forcedSemicolon:
117 self.__forcedSemicolon = False
118 return result
119
120 if result.endswith(self.__semicolonSymbol):
121 return result[:-len(self.__semicolonSymbol)]
122 else:
123 return result
124
125
126 #
127 # Data
128 #
129
130 __simple_property = re.compile(r"^[a-zA-Z_$][a-zA-Z0-9_$]*$")
131 __number_property = re.compile(r"^[0-9]+$")
132
133 __simple = ["true", "false", "null", "this", "debugger"]
134
135 __dividers = {
136 "plus" : '+',
137 "minus" : '-',
138 "mul" : '*',
139 "div" : '/',
140 "mod" : '%',
141 "dot" : '.',
142 "or" : "||",
143 "and" : "&&",
144 "strict_eq" : '===',
145 "eq" : '==',
146 "strict_ne" : '!==',
147 "ne" : '!=',
148 "lsh" : '<<',
149 "le" : '<=',
150 "lt" : '<',
151 "ursh" : '>>>',
152 "rsh" : '>>',
153 "ge" : '>=',
154 "gt" : '>',
155 "bitwise_or" : '|',
156 "bitwise_xor" : '^',
157 "bitwise_and" : '&'
158 }
159
160 __prefixes = {
161 "increment" : "++",
162 "decrement" : "--",
163 "bitwise_not" : '~',
164 "not" : "!",
165 "unary_plus" : "+",
166 "unary_minus" : "-",
167 "delete" : "delete ",
168 "new" : "new ",
169 "typeof" : "typeof ",
170 "void" : "void "
171 }
172
173
174
175 #
176 # Script Scope
177 #
178
179 def type_script(self, node):
180 return self.__statements(node)
181
182
183
184 #
185 # Expressions
186 #
187
188 def type_comma(self, node):
189 return self.__commaSymbol.join(map(self.compress, node))
190
191 def type_object_init(self, node):
192 return "{%s}" % self.__commaSymbol.join(map(self.compress, node))
193
194 def type_property_init(self, node):
195 key = self.compress(node[0])
196 value = self.compress(node[1])
197
198 if type(key) in (int, float):
199 pass
200
201 elif self.__number_property.match(key):
202 pass
203
204 # Protect keywords and special characters
205 elif key in keywords or key in futureReserved or not self.__simple_property.match(key):
206 key = self.type_string(node[0])
207
208 return "%s:%s" % (key, value)
209
210 def type_array_init(self, node):
211 def helper(child):
212 return self.compress(child) if child != None else ""
213
214 return "[%s]" % ",".join(map(helper, node))
215
216 def type_array_comp(self, node):
217 return "[%s %s]" % (self.compress(node.expression), self.compress(node.tail))
218
219 def type_string(self, node):
220 # Omit writing real high unicode character which are not supported well by browsers
221 ascii = ascii_encoder.encode(node.value)
222
223 if high_unicode.search(ascii):
224 return ascii
225 else:
226 return unicode_encoder.encode(node.value)
227
228 def type_number(self, node):
229 value = node.value
230
231 # Special handling for protected float/exponential
232 if type(value) == str:
233 # Convert zero-prefix
234 if value.startswith("0.") and len(value) > 2:
235 value = value[1:]
236
237 # Convert zero postfix
238 elif value.endswith(".0"):
239 value = value[:-2]
240
241 elif int(value) == value and node.parent.type != "dot":
242 value = int(value)
243
244 return "%s" % value
245
246 def type_regexp(self, node):
247 return node.value
248
249 def type_identifier(self, node):
250 return node.value
251
252 def type_list(self, node):
253 return ",".join(map(self.compress, node))
254
255 def type_index(self, node):
256 return "%s[%s]" % (self.compress(node[0]), self.compress(node[1]))
257
258 def type_declaration(self, node):
259 names = getattr(node, "names", None)
260 if names:
261 result = self.compress(names)
262 else:
263 result = node.name
264
265 initializer = getattr(node, "initializer", None)
266 if initializer:
267 result += "=%s" % self.compress(node.initializer)
268
269 return result
270
271 def type_assign(self, node):
272 assignOp = getattr(node, "assignOp", None)
273 operator = "=" if not assignOp else self.__dividers[assignOp] + "="
274
275 return self.compress(node[0]) + operator + self.compress(node[1])
276
277 def type_call(self, node):
278 return "%s(%s)" % (self.compress(node[0]), self.compress(node[1]))
279
280 def type_new_with_args(self, node):
281 result = "new %s" % self.compress(node[0])
282
283 # Compress new Object(); => new Object;
284 if len(node[1]) > 0:
285 result += "(%s)" % self.compress(node[1])
286 else:
287 parent = getattr(node, "parent", None)
288 if parent and parent.type is "dot":
289 result += "()"
290
291 return result
292
293 def type_exception(self, node):
294 return node.value
295
296 def type_generator(self, node):
297 """ Generator Expression """
298 result = self.compress(getattr(node, "expression"))
299 tail = getattr(node, "tail", None)
300 if tail:
301 result += " %s" % self.compress(tail)
302
303 return result
304
305 def type_comp_tail(self, node):
306 """ Comprehensions Tails """
307 result = self.compress(getattr(node, "for"))
308 guard = getattr(node, "guard", None)
309 if guard:
310 result += "if(%s)" % self.compress(guard)
311
312 return result
313
314 def type_in(self, node):
315 first = self.compress(node[0])
316 second = self.compress(node[1])
317
318 if first.endswith("'") or first.endswith('"'):
319 pattern = "%sin %s"
320 else:
321 pattern = "%s in %s"
322
323 return pattern % (first, second)
324
325 def type_instanceof(self, node):
326 first = self.compress(node[0])
327 second = self.compress(node[1])
328
329 return "%s instanceof %s" % (first, second)
330
331
332
333 #
334 # Statements :: Core
335 #
336
337 def type_block(self, node):
338 return "{%s}" % self.__removeSemicolon(self.__statements(node))
339
340 def type_let_block(self, node):
341 begin = "let(%s)" % ",".join(map(self.compress, node.variables))
342 if hasattr(node, "block"):
343 end = self.compress(node.block)
344 elif hasattr(node, "expression"):
345 end = self.compress(node.expression)
346
347 return begin + end
348
349 def type_const(self, node):
350 return self.__addSemicolon("const %s" % self.type_list(node))
351
352 def type_var(self, node):
353 return self.__addSemicolon("var %s" % self.type_list(node))
354
355 def type_let(self, node):
356 return self.__addSemicolon("let %s" % self.type_list(node))
357
358 def type_semicolon(self, node):
359 expression = getattr(node, "expression", None)
360 return self.__addSemicolon(self.compress(expression) if expression else "")
361
362 def type_label(self, node):
363 return self.__addSemicolon("%s:%s" % (node.label, self.compress(node.statement)))
364
365 def type_break(self, node):
366 return self.__addSemicolon("break" if not hasattr(node, "label") else "break %s" % node.label)
367
368 def type_continue(self, node):
369 return self.__addSemicolon("continue" if not hasattr(node, "label") else "continue %s" % node.label)
370
371
372 #
373 # Statements :: Functions
374 #
375
376 def type_function(self, node):
377 if node.type == "setter":
378 result = "set"
379 elif node.type == "getter":
380 result = "get"
381 else:
382 result = "function"
383
384 name = getattr(node, "name", None)
385 if name:
386 result += " %s" % name
387
388 params = getattr(node, "params", None)
389 result += "(%s)" % self.compress(params) if params else "()"
390
391 # keep expression closure format (may be micro-optimized for other code, too)
392 if getattr(node, "expressionClosure", False):
393 result += self.compress(node.body)
394 else:
395 result += "{%s}" % self.__removeSemicolon(self.compress(node.body))
396
397 return result
398
399 def type_getter(self, node):
400 return self.type_function(node)
401
402 def type_setter(self, node):
403 return self.type_function(node)
404
405 def type_return(self, node):
406 result = "return"
407 if hasattr(node, "value"):
408 valueCode = self.compress(node.value)
409
410 # Micro optimization: Don't need a space when a block/map/array/group/strings are returned
411 if not valueCode.startswith(("(","[","{","'",'"',"!","-","/")):
412 result += " "
413
414 result += valueCode
415
416 return self.__addSemicolon(result)
417
418
419
420 #
421 # Statements :: Exception Handling
422 #
423
424 def type_throw(self, node):
425 return self.__addSemicolon("throw %s" % self.compress(node.exception))
426
427 def type_try(self, node):
428 result = "try%s" % self.compress(node.tryBlock)
429
430 for catch in node:
431 if catch.type == "catch":
432 if hasattr(catch, "guard"):
433 result += "catch(%s if %s)%s" % (self.compress(catch.exception), self.compress(catch.guard), self.compress(catch.block))
434 else:
435 result += "catch(%s)%s" % (self.compress(catch.exception), self.compress(catch.block))
436
437 if hasattr(node, "finallyBlock"):
438 result += "finally%s" % self.compress(node.finallyBlock)
439
440 return result
441
442
443
444 #
445 # Statements :: Loops
446 #
447
448 def type_while(self, node):
449 result = "while(%s)%s" % (self.compress(node.condition), self.compress(node.body))
450 self.__handleForcedSemicolon(node.body)
451 return result
452
453
454 def type_do(self, node):
455 # block unwrapping don't help to reduce size on this loop type
456 # but if it happens (don't like to modify a global function to fix a local issue), we
457 # need to fix the body and re-add braces around the statement
458 body = self.compress(node.body)
459 if not body.startswith("{"):
460 body = "{%s}" % body
461
462 return self.__addSemicolon("do%swhile(%s)" % (body, self.compress(node.condition)))
463
464
465 def type_for_in(self, node):
466 # Optional variable declarations
467 varDecl = getattr(node, "varDecl", None)
468
469 # Body is optional - at least in comprehensions tails
470 body = getattr(node, "body", None)
471 if body:
472 body = self.compress(body)
473 else:
474 body = ""
475
476 result = "for"
477 if node.isEach:
478 result += " each"
479
480 result += "(%s in %s)%s" % (self.__removeSemicolon(self.compress(node.iterator)), self.compress(node.object), body)
481
482 if body:
483 self.__handleForcedSemicolon(node.body)
484
485 return result
486
487
488 def type_for(self, node):
489 setup = getattr(node, "setup", None)
490 condition = getattr(node, "condition", None)
491 update = getattr(node, "update", None)
492
493 result = "for("
494 result += self.__addSemicolon(self.compress(setup) if setup else "")
495 result += self.__addSemicolon(self.compress(condition) if condition else "")
496 result += self.compress(update) if update else ""
497 result += ")%s" % self.compress(node.body)
498
499 self.__handleForcedSemicolon(node.body)
500 return result
501
502
503
504 #
505 # Statements :: Conditionals
506 #
507
508 def type_hook(self, node):
509 """aka ternary operator"""
510 condition = node.condition
511 thenPart = node.thenPart
512 elsePart = node.elsePart
513
514 if condition.type == "not":
515 [thenPart,elsePart] = [elsePart,thenPart]
516 condition = condition[0]
517
518 return "%s?%s:%s" % (self.compress(condition), self.compress(thenPart), self.compress(elsePart))
519
520
521 def type_if(self, node):
522 result = "if(%s)%s" % (self.compress(node.condition), self.compress(node.thenPart))
523
524 elsePart = getattr(node, "elsePart", None)
525 if elsePart:
526 result += "else"
527
528 elseCode = self.compress(elsePart)
529
530 # Micro optimization: Don't need a space when the child is a block
531 # At this time the brace could not be part of a map declaration (would be a syntax error)
532 if not elseCode.startswith(("{", "(", ";")):
533 result += " "
534
535 result += elseCode
536
537 self.__handleForcedSemicolon(elsePart)
538
539 return result
540
541
542 def type_switch(self, node):
543 result = "switch(%s){" % self.compress(node.discriminant)
544 for case in node:
545 if case.type == "case":
546 labelCode = self.compress(case.label)
547 if labelCode.startswith('"'):
548 result += "case%s:" % labelCode
549 else:
550 result += "case %s:" % labelCode
551 elif case.type == "default":
552 result += "default:"
553 else:
554 continue
555
556 for statement in case.statements:
557 temp = self.compress(statement)
558 if len(temp) > 0:
559 result += self.__addSemicolon(temp)
560
561 return "%s}" % self.__removeSemicolon(result)
562
563
564

eric ide

mercurial