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

changeset 6650
1dd52aa8897c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/output/Compressor.py	Sat Jan 12 12:11:42 2019 +0100
@@ -0,0 +1,564 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import re, sys, json
+
+from jasy.script.tokenize.Lang import keywords
+from jasy.script.parse.Lang import expressions, futureReserved
+
+high_unicode = re.compile(r"\\u[2-9A-Fa-f][0-9A-Fa-f]{3}")
+ascii_encoder = json.JSONEncoder(ensure_ascii=True)
+unicode_encoder = json.JSONEncoder(ensure_ascii=False)
+
+#
+# Class
+#
+
+class Compressor:
+    __semicolonSymbol = ";"
+    __commaSymbol = ","
+
+
+    def __init__(self, format=None):
+        if format:
+            if format.has("semicolon"):
+                self.__semicolonSymbol = ";\n"
+
+            if format.has("comma"):
+                self.__commaSymbol = ",\n"
+
+        self.__forcedSemicolon = False
+
+
+
+    #
+    # Main
+    #
+
+    def compress(self, node):
+        type = node.type
+        result = None
+
+        if type in self.__simple:
+            result = type
+        elif type in self.__prefixes:
+            if getattr(node, "postfix", False):
+                result = self.compress(node[0]) + self.__prefixes[node.type]
+            else:
+                result = self.__prefixes[node.type] + self.compress(node[0])
+
+        elif type in self.__dividers:
+            first = self.compress(node[0])
+            second = self.compress(node[1])
+            divider = self.__dividers[node.type]
+
+            # Fast path
+            if node.type not in ("plus", "minus"):
+                result = "%s%s%s" % (first, divider, second)
+
+            # Special code for dealing with situations like x + ++y and y-- - x
+            else:
+                result = first
+                if first.endswith(divider):
+                    result += " "
+
+                result += divider
+
+                if second.startswith(divider):
+                    result += " "
+
+                result += second
+
+        else:
+            try:
+                result = getattr(self, "type_%s" % type)(node)
+            except AttributeError:
+                raise Exception("Script compressor does not support type '%s' from line %s in file %s" % (type, node.line, node.getFileName()))
+
+        if getattr(node, "parenthesized", None):
+            return "(%s)" % result
+        else:
+            return result
+
+
+
+    #
+    # Helpers
+    #
+
+    def __statements(self, node):
+        result = []
+        for child in node:
+            result.append(self.compress(child))
+
+        return "".join(result)
+
+    def __handleForcedSemicolon(self, node):
+        if node.type == "semicolon" and not hasattr(node, "expression"):
+            self.__forcedSemicolon = True
+
+    def __addSemicolon(self, result):
+        if not result.endswith(self.__semicolonSymbol):
+            if self.__forcedSemicolon:
+                self.__forcedSemicolon = False
+
+            return result + self.__semicolonSymbol
+
+        else:
+            return result
+
+    def __removeSemicolon(self, result):
+        if self.__forcedSemicolon:
+            self.__forcedSemicolon = False
+            return result
+
+        if result.endswith(self.__semicolonSymbol):
+            return result[:-len(self.__semicolonSymbol)]
+        else:
+            return result
+
+
+    #
+    # Data
+    #
+
+    __simple_property = re.compile(r"^[a-zA-Z_$][a-zA-Z0-9_$]*$")
+    __number_property = re.compile(r"^[0-9]+$")
+
+    __simple = ["true", "false", "null", "this", "debugger"]
+
+    __dividers = {
+        "plus"        : '+',
+        "minus"       : '-',
+        "mul"         : '*',
+        "div"         : '/',
+        "mod"         : '%',
+        "dot"         : '.',
+        "or"          : "||",
+        "and"         : "&&",
+        "strict_eq"   : '===',
+        "eq"          : '==',
+        "strict_ne"   : '!==',
+        "ne"          : '!=',
+        "lsh"         : '<<',
+        "le"          : '<=',
+        "lt"          : '<',
+        "ursh"        : '>>>',
+        "rsh"         : '>>',
+        "ge"          : '>=',
+        "gt"          : '>',
+        "bitwise_or"  : '|',
+        "bitwise_xor" : '^',
+        "bitwise_and" : '&'
+    }
+
+    __prefixes = {
+        "increment"   : "++",
+        "decrement"   : "--",
+        "bitwise_not" : '~',
+        "not"         : "!",
+        "unary_plus"  : "+",
+        "unary_minus" : "-",
+        "delete"      : "delete ",
+        "new"         : "new ",
+        "typeof"      : "typeof ",
+        "void"        : "void "
+    }
+
+
+
+    #
+    # Script Scope
+    #
+
+    def type_script(self, node):
+        return self.__statements(node)
+
+
+
+    #
+    # Expressions
+    #
+
+    def type_comma(self, node):
+        return self.__commaSymbol.join(map(self.compress, node))
+
+    def type_object_init(self, node):
+        return "{%s}" % self.__commaSymbol.join(map(self.compress, node))
+
+    def type_property_init(self, node):
+        key = self.compress(node[0])
+        value = self.compress(node[1])
+
+        if type(key) in (int, float):
+            pass
+
+        elif self.__number_property.match(key):
+            pass
+
+        # Protect keywords and special characters
+        elif key in keywords or key in futureReserved or not self.__simple_property.match(key):
+            key = self.type_string(node[0])
+
+        return "%s:%s" % (key, value)
+
+    def type_array_init(self, node):
+        def helper(child):
+            return self.compress(child) if child != None else ""
+
+        return "[%s]" % ",".join(map(helper, node))
+
+    def type_array_comp(self, node):
+        return "[%s %s]" % (self.compress(node.expression), self.compress(node.tail))
+
+    def type_string(self, node):
+        # Omit writing real high unicode character which are not supported well by browsers
+        ascii = ascii_encoder.encode(node.value)
+
+        if high_unicode.search(ascii):
+            return ascii
+        else:
+            return unicode_encoder.encode(node.value)
+
+    def type_number(self, node):
+        value = node.value
+
+        # Special handling for protected float/exponential
+        if type(value) == str:
+            # Convert zero-prefix
+            if value.startswith("0.") and len(value) > 2:
+                value = value[1:]
+
+            # Convert zero postfix
+            elif value.endswith(".0"):
+                value = value[:-2]
+
+        elif int(value) == value and node.parent.type != "dot":
+            value = int(value)
+
+        return "%s" % value
+
+    def type_regexp(self, node):
+        return node.value
+
+    def type_identifier(self, node):
+        return node.value
+
+    def type_list(self, node):
+        return ",".join(map(self.compress, node))
+
+    def type_index(self, node):
+        return "%s[%s]" % (self.compress(node[0]), self.compress(node[1]))
+
+    def type_declaration(self, node):
+        names = getattr(node, "names", None)
+        if names:
+            result = self.compress(names)
+        else:
+            result = node.name
+
+        initializer = getattr(node, "initializer", None)
+        if initializer:
+            result += "=%s" % self.compress(node.initializer)
+
+        return result
+
+    def type_assign(self, node):
+        assignOp = getattr(node, "assignOp", None)
+        operator = "=" if not assignOp else self.__dividers[assignOp] + "="
+
+        return self.compress(node[0]) + operator + self.compress(node[1])
+
+    def type_call(self, node):
+        return "%s(%s)" % (self.compress(node[0]), self.compress(node[1]))
+
+    def type_new_with_args(self, node):
+        result = "new %s" % self.compress(node[0])
+
+        # Compress new Object(); => new Object;
+        if len(node[1]) > 0:
+            result += "(%s)" % self.compress(node[1])
+        else:
+            parent = getattr(node, "parent", None)
+            if parent and parent.type is "dot":
+                result += "()"
+
+        return result
+
+    def type_exception(self, node):
+        return node.value
+
+    def type_generator(self, node):
+        """ Generator Expression """
+        result = self.compress(getattr(node, "expression"))
+        tail = getattr(node, "tail", None)
+        if tail:
+            result += " %s" % self.compress(tail)
+
+        return result
+
+    def type_comp_tail(self, node):
+        """  Comprehensions Tails """
+        result = self.compress(getattr(node, "for"))
+        guard = getattr(node, "guard", None)
+        if guard:
+            result += "if(%s)" % self.compress(guard)
+
+        return result
+
+    def type_in(self, node):
+        first = self.compress(node[0])
+        second = self.compress(node[1])
+
+        if first.endswith("'") or first.endswith('"'):
+            pattern = "%sin %s"
+        else:
+            pattern = "%s in %s"
+
+        return pattern % (first, second)
+
+    def type_instanceof(self, node):
+        first = self.compress(node[0])
+        second = self.compress(node[1])
+
+        return "%s instanceof %s" % (first, second)
+
+
+
+    #
+    # Statements :: Core
+    #
+
+    def type_block(self, node):
+        return "{%s}" % self.__removeSemicolon(self.__statements(node))
+
+    def type_let_block(self, node):
+        begin = "let(%s)" % ",".join(map(self.compress, node.variables))
+        if hasattr(node, "block"):
+            end = self.compress(node.block)
+        elif hasattr(node, "expression"):
+            end = self.compress(node.expression)
+
+        return begin + end
+
+    def type_const(self, node):
+        return self.__addSemicolon("const %s" % self.type_list(node))
+
+    def type_var(self, node):
+        return self.__addSemicolon("var %s" % self.type_list(node))
+
+    def type_let(self, node):
+        return self.__addSemicolon("let %s" % self.type_list(node))
+
+    def type_semicolon(self, node):
+        expression = getattr(node, "expression", None)
+        return self.__addSemicolon(self.compress(expression) if expression else "")
+
+    def type_label(self, node):
+        return self.__addSemicolon("%s:%s" % (node.label, self.compress(node.statement)))
+
+    def type_break(self, node):
+        return self.__addSemicolon("break" if not hasattr(node, "label") else "break %s" % node.label)
+
+    def type_continue(self, node):
+        return self.__addSemicolon("continue" if not hasattr(node, "label") else "continue %s" % node.label)
+
+
+    #
+    # Statements :: Functions
+    #
+
+    def type_function(self, node):
+        if node.type == "setter":
+            result = "set"
+        elif node.type == "getter":
+            result = "get"
+        else:
+            result = "function"
+
+        name = getattr(node, "name", None)
+        if name:
+            result += " %s" % name
+
+        params = getattr(node, "params", None)
+        result += "(%s)" % self.compress(params) if params else "()"
+
+        # keep expression closure format (may be micro-optimized for other code, too)
+        if getattr(node, "expressionClosure", False):
+            result += self.compress(node.body)
+        else:
+            result += "{%s}" % self.__removeSemicolon(self.compress(node.body))
+
+        return result
+
+    def type_getter(self, node):
+        return self.type_function(node)
+
+    def type_setter(self, node):
+        return self.type_function(node)
+
+    def type_return(self, node):
+        result = "return"
+        if hasattr(node, "value"):
+            valueCode = self.compress(node.value)
+
+            # Micro optimization: Don't need a space when a block/map/array/group/strings are returned
+            if not valueCode.startswith(("(","[","{","'",'"',"!","-","/")):
+                result += " "
+
+            result += valueCode
+
+        return self.__addSemicolon(result)
+
+
+
+    #
+    # Statements :: Exception Handling
+    #
+
+    def type_throw(self, node):
+        return self.__addSemicolon("throw %s" % self.compress(node.exception))
+
+    def type_try(self, node):
+        result = "try%s" % self.compress(node.tryBlock)
+
+        for catch in node:
+            if catch.type == "catch":
+                if hasattr(catch, "guard"):
+                    result += "catch(%s if %s)%s" % (self.compress(catch.exception), self.compress(catch.guard), self.compress(catch.block))
+                else:
+                    result += "catch(%s)%s" % (self.compress(catch.exception), self.compress(catch.block))
+
+        if hasattr(node, "finallyBlock"):
+            result += "finally%s" % self.compress(node.finallyBlock)
+
+        return result
+
+
+
+    #
+    # Statements :: Loops
+    #
+
+    def type_while(self, node):
+        result = "while(%s)%s" % (self.compress(node.condition), self.compress(node.body))
+        self.__handleForcedSemicolon(node.body)
+        return result
+
+
+    def type_do(self, node):
+        # block unwrapping don't help to reduce size on this loop type
+        # but if it happens (don't like to modify a global function to fix a local issue), we
+        # need to fix the body and re-add braces around the statement
+        body = self.compress(node.body)
+        if not body.startswith("{"):
+            body = "{%s}" % body
+
+        return self.__addSemicolon("do%swhile(%s)" % (body, self.compress(node.condition)))
+
+
+    def type_for_in(self, node):
+        # Optional variable declarations
+        varDecl = getattr(node, "varDecl", None)
+
+        # Body is optional - at least in comprehensions tails
+        body = getattr(node, "body", None)
+        if body:
+            body = self.compress(body)
+        else:
+            body = ""
+
+        result = "for"
+        if node.isEach:
+            result += " each"
+
+        result += "(%s in %s)%s" % (self.__removeSemicolon(self.compress(node.iterator)), self.compress(node.object), body)
+
+        if body:
+            self.__handleForcedSemicolon(node.body)
+
+        return result
+
+
+    def type_for(self, node):
+        setup = getattr(node, "setup", None)
+        condition = getattr(node, "condition", None)
+        update = getattr(node, "update", None)
+
+        result = "for("
+        result += self.__addSemicolon(self.compress(setup) if setup else "")
+        result += self.__addSemicolon(self.compress(condition) if condition else "")
+        result += self.compress(update) if update else ""
+        result += ")%s" % self.compress(node.body)
+
+        self.__handleForcedSemicolon(node.body)
+        return result
+
+
+
+    #
+    # Statements :: Conditionals
+    #
+
+    def type_hook(self, node):
+        """aka ternary operator"""
+        condition = node.condition
+        thenPart = node.thenPart
+        elsePart = node.elsePart
+
+        if condition.type == "not":
+            [thenPart,elsePart] = [elsePart,thenPart]
+            condition = condition[0]
+
+        return "%s?%s:%s" % (self.compress(condition), self.compress(thenPart), self.compress(elsePart))
+
+
+    def type_if(self, node):
+        result = "if(%s)%s" % (self.compress(node.condition), self.compress(node.thenPart))
+
+        elsePart = getattr(node, "elsePart", None)
+        if elsePart:
+            result += "else"
+
+            elseCode = self.compress(elsePart)
+
+            # Micro optimization: Don't need a space when the child is a block
+            # At this time the brace could not be part of a map declaration (would be a syntax error)
+            if not elseCode.startswith(("{", "(", ";")):
+                result += " "
+
+            result += elseCode
+
+            self.__handleForcedSemicolon(elsePart)
+
+        return result
+
+
+    def type_switch(self, node):
+        result = "switch(%s){" % self.compress(node.discriminant)
+        for case in node:
+            if case.type == "case":
+                labelCode = self.compress(case.label)
+                if labelCode.startswith('"'):
+                    result += "case%s:" % labelCode
+                else:
+                    result += "case %s:" % labelCode
+            elif case.type == "default":
+                result += "default:"
+            else:
+                continue
+
+            for statement in case.statements:
+                temp = self.compress(statement)
+                if len(temp) > 0:
+                    result += self.__addSemicolon(temp)
+
+        return "%s}" % self.__removeSemicolon(result)
+
+
+

eric ide

mercurial