--- a/src/eric7/ThirdParty/Jasy/jasy/script/util/__init__.py Fri Mar 31 13:39:51 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,416 +0,0 @@ -# -# Jasy - Web Tooling Framework -# Copyright 2010-2012 Zynga Inc. -# Copyright 2013-2014 Sebastian Werner -# - -from __future__ import unicode_literals - -from jasy.script.output.Compressor import Compressor - -# Shared instance -compressor = Compressor() - -pseudoTypes = set(["any", "var", "undefined", "null", "true", "false", "this", "arguments"]) -builtinTypes = set(["Object", "String", "Number", "Boolean", "Array", "Function", "RegExp", "Date"]) - -# Basic user friendly node type to human type -nodeTypeToDocType = { - - # Primitives - "string": "String", - "number": "Number", - "not": "Boolean", - "true": "Boolean", - "false": "Boolean", - - # Literals - "function": "Function", - "regexp": "RegExp", - "object_init": "Map", - "array_init": "Array", - - # We could figure out the real class automatically - at least that's the case quite often - "new": "Object", - "new_with_args": "Object", - - # Comparisons - "eq" : "Boolean", - "ne" : "Boolean", - "strict_eq" : "Boolean", - "strict_ne" : "Boolean", - "lt" : "Boolean", - "le" : "Boolean", - "gt" : "Boolean", - "ge" : "Boolean", - "in" : "Boolean", - "instanceof" : "Boolean", - - # Numbers - "lsh": "Number", - "rsh": "Number", - "ursh": "Number", - "minus": "Number", - "mul": "Number", - "div": "Number", - "mod": "Number", - "bitwise_and": "Number", - "bitwise_xor": "Number", - "bitwise_or": "Number", - "bitwise_not": "Number", - "increment": "Number", - "decrement": "Number", - "unary_minus": "Number", - "unary_plus": "Number", - - # This is not 100% correct, but I don't like to introduce a BooleanLike type. - # If the author likes something different he is still able to override it via API docs - "and": "Boolean", - "or": "Boolean", - - # Operators/Built-ins - "void": "undefined", - "null": "null", - "typeof": "String", - "delete": "Boolean", - "this": "This", - - # These are not real types, we try to figure out the real value behind automatically - "call": "Call", - "hook": "Hook", - "assign": "Assign", - "plus": "Plus", - "identifier" : "Identifier", - "dot": "Object", - "index": "var" -} - - -def getVisibility(name): - """ - Returns the visibility of the given name by convention - """ - - if name.startswith("__"): - return "private" - elif name.startswith("_"): - return "internal" - else: - return "public" - - -def requiresDocumentation(name): - """ - Whether the given name suggests that documentation is required - """ - - return not name.startswith("_") - - -def getKeyValue(dict, key): - """ - Returns the value node of the given key inside the given object initializer. - """ - - for propertyInit in dict: - if propertyInit[0].value == key: - return propertyInit[1] - - -def findAssignments(name, node): - """ - Returns a list of assignments which might have impact on the value used in the given node. - """ - - # Looking for all script blocks - scripts = [] - parent = node - while parent: - if parent.type == "script": - scope = getattr(parent, "scope", None) - if scope and name in scope.modified: - scripts.append(parent) - - parent = getattr(parent, "parent", None) - - def assignMatcher(node): - if node.type == "assign" and node[0].type == "identifier" and node[0].value == name: - return True - - if node.type == "declaration" and node.name == name and getattr(node, "initializer", None): - return True - - if node.type == "function" and node.functionForm == "declared_form" and node.name == name: - return True - - return False - - # Query all relevant script nodes - assignments = [] - for script in scripts: - queryResult = queryAll(script, assignMatcher, False) - assignments.extend(queryResult) - - # Collect assigned values - values = [] - for assignment in assignments: - if assignment.type == "function": - values.append(assignment) - elif assignment.type == "assign": - values.append(assignment[1]) - else: - values.append(assignment.initializer) - - return assignments, values - - -def findFunction(node): - """ - Returns the first function inside the given node - """ - - return query(node, lambda node: node.type == "function") - - -def findCommentNode(node): - """ - Finds the first doc comment node inside the given node - """ - - def matcher(node): - comments = getattr(node, "comments", None) - if comments: - for comment in comments: - if comment.variant == "doc": - return True - - return query(node, matcher) - - -def getDocComment(node): - """ - Returns the first doc comment of the given node. - """ - - comments = getattr(node, "comments", None) - if comments: - for comment in comments: - if comment.variant == "doc": - return comment - - return None - - -def findReturn(node): - """ - Finds the first return inside the given node - """ - - return query(node, lambda node: node.type == "return", True) - - - -def valueToString(node): - """ - Converts the value of the given node into something human friendly - """ - - if node.type in ("number", "string", "false", "true", "regexp", "null"): - return compressor.compress(node) - elif node.type in nodeTypeToDocType: - if node.type == "plus": - return detectPlusType(node) - elif node.type in ("new", "new_with_args", "dot"): - return detectObjectType(node) - else: - return nodeTypeToDocType[node.type] - else: - return "Other" - - - -def queryAll(node, matcher, deep=True, inner=False, result=None): - """ - Recurses the tree starting with the given node and returns a list of nodes - matched by the given matcher method - - - node: any node - - matcher: function which should return a truish value when node matches - - deep: whether inner scopes should be scanned, too - - inner: used internally to differentiate between current and inner nodes - - result: can be used to extend an existing list, otherwise a new list is created and returned - """ - - if result == None: - result = [] - - # Don't do in closure functions - if inner and node.type == "script" and not deep: - return None - - if matcher(node): - result.append(node) - - for child in node: - queryAll(child, matcher, deep, True, result) - - return result - - - -def query(node, matcher, deep=True, inner=False): - """ - Recurses the tree starting with the given node and returns the first node - which is matched by the given matcher method. - - - node: any node - - matcher: function which should return a truish value when node matches - - deep: whether inner scopes should be scanned, too - - inner: used internally to differentiate between current and inner nodes - """ - - # Don't do in closure functions - if inner and node.type == "script" and not deep: - return None - - if matcher(node): - return node - - for child in node: - result = query(child, matcher, deep, True) - if result is not None: - return result - - return None - - -def findCall(node, methodName): - """ - Recurses the tree starting with the given node and returns the first node - which calls the given method name (supports namespaces, too) - """ - - if type(methodName) is str: - methodName = set([methodName]) - - def matcher(node): - call = getCallName(node) - if call and call in methodName: - return call - - return query(node, matcher) - - -def getCallName(node): - if node.type == "call": - if node[0].type == "dot": - return assembleDot(node[0]) - elif node[0].type == "identifier": - return node[0].value - - return None - - -def getParameterFromCall(call, index=0): - """ - Returns a parameter node by index on the call node - """ - - try: - return call[1][index] - except: - return None - - -def getParamNamesFromFunction(func): - """ - Returns a human readable list of parameter names (sorted by their order in the given function) - """ - - params = getattr(func, "params", None) - if params: - return [identifier.value for identifier in params] - else: - return None - - -def detectPlusType(plusNode): - """ - Analyses the given "plus" node and tries to figure out if a "string" or "number" result is produced. - """ - - if plusNode[0].type == "string" or plusNode[1].type == "string": - return "String" - elif plusNode[0].type == "number" and plusNode[1].type == "number": - return "Number" - elif plusNode[0].type == "plus" and detectPlusType(plusNode[0]) == "String": - return "String" - else: - return "var" - - -def detectObjectType(objectNode): - """ - Returns a human readable type information of the given node - """ - - if objectNode.type in ("new", "new_with_args"): - construct = objectNode[0] - else: - construct = objectNode - - # Only support built-in top level constructs - if construct.type == "identifier" and construct.value in ("Array", "Boolean", "Date", "Function", "Number", "Object", "String", "RegExp"): - return construct.value - - # And namespaced custom classes - elif construct.type == "dot": - assembled = assembleDot(construct) - if assembled: - return assembled - - return "Object" - - - -def resolveIdentifierNode(identifierNode): - assignNodes, assignValues = findAssignments(identifierNode.value, identifierNode) - if assignNodes: - - assignCommentNode = None - - # Find first relevant assignment with comment! Otherwise just first one. - for assign in assignNodes: - - # The parent is the relevant doc comment container - # It's either a "var" (declaration) or "semicolon" (assignment) - if getDocComment(assign): - assignCommentNode = assign - break - elif getDocComment(assign.parent): - assignCommentNode = assign.parent - break - - return assignValues[0], assignCommentNode or assignValues[0] - - return None, None - - - -def assembleDot(node, result=None): - """ - Joins a dot node (cascaded supported, too) into a single string like "foo.bar.Baz" - """ - - if result == None: - result = [] - - for child in node: - if child.type == "identifier": - result.append(child.value) - elif child.type == "dot": - assembleDot(child, result) - else: - return None - - return ".".join(result)