--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/Jasy/jasy/parse/AbstractNode.py Fri Aug 11 14:40:54 2017 +0200 @@ -0,0 +1,357 @@ +# +# Jasy - Web Tooling Framework +# Copyright 2013-2014 Sebastian Werner +# + +from __future__ import unicode_literals + +import json, copy + +class AbstractNode(list): + + __slots__ = [ + # core data + "line", "type", "tokenizer", "start", "end", "rel", "parent", + + # dynamic added data by other modules + "comments", "scope", "values", + + # node type specific + "value", "parenthesized", "fileId", "params", + "name", "initializer", "condition", "assignOp", + "thenPart", "elsePart", "statements", + "statement", "variables", "names", "postfix" + ] + + + def __init__(self, tokenizer=None, type=None, args=[]): + list.__init__(self) + + self.start = 0 + self.end = 0 + self.line = None + + if tokenizer: + token = getattr(tokenizer, "token", None) + if token: + # We may define a custom type but use the same positioning as another token + # e.g. transform curlys in block nodes, etc. + self.type = type if type else getattr(token, "type", None) + self.line = token.line + + # Start & end are file positions for error handling. + self.start = token.start + self.end = token.end + + else: + self.type = type + self.line = tokenizer.line + self.start = None + self.end = None + + self.tokenizer = tokenizer + + elif type: + self.type = type + + for arg in args: + self.append(arg) + + + def getFileName(self): + """ + Traverses up the tree to find a node with a fileId and returns it + """ + + node = self + while node: + fileId = getattr(node, "fileId", None) + if fileId is not None: + return fileId + + node = getattr(node, "parent", None) + + + def getUnrelatedChildren(self): + """Collects all unrelated children""" + + collection = [] + for child in self: + if not hasattr(child, "rel"): + collection.append(child) + + return collection + + + def getChildrenLength(self, filter=True): + """Number of (per default unrelated) children""" + + count = 0 + for child in self: + if not filter or not hasattr(child, "rel"): + count += 1 + return count + + + def remove(self, kid): + """Removes the given kid""" + + if not kid in self: + raise Exception("Given node is no child!") + + if hasattr(kid, "rel"): + delattr(self, kid.rel) + del kid.rel + del kid.parent + + list.remove(self, kid) + + + def insert(self, index, kid): + """Inserts the given kid at the given index""" + + if index is None: + return self.append(kid) + + if hasattr(kid, "parent"): + kid.parent.remove(kid) + + kid.parent = self + + return list.insert(self, index, kid) + + + def insertAll(self, index, kids): + """Inserts all kids starting with the given index""" + + if index is None: + for kid in list(kids): + self.append(kid) + else: + for pos, kid in enumerate(list(kids)): + self.insert(index+pos, kid) + + + def insertAllReplace(self, orig, kids): + """Inserts all kids at the same position as the original node (which is removed afterwards)""" + + index = self.index(orig) + for pos, kid in enumerate(list(kids)): + self.insert(index+pos, kid) + + self.remove(orig) + + + def append(self, kid, rel=None): + """Appends the given kid with an optional relation hint""" + + # kid can be null e.g. [1, , 2]. + if kid: + if hasattr(kid, "parent"): + kid.parent.remove(kid) + + # Debug + if not isinstance(kid, AbstractNode): + raise Exception("Invalid kid: %s" % kid) + + if hasattr(kid, "tokenizer"): + if hasattr(kid, "start"): + if not hasattr(self, "start") or self.start == None or kid.start < self.start: + self.start = kid.start + + if hasattr(kid, "end"): + if not hasattr(self, "end") or self.end == None or self.end < kid.end: + self.end = kid.end + + kid.parent = self + + # alias for function + if rel != None: + setattr(self, rel, kid) + setattr(kid, "rel", rel) + + # Block None kids when they should be related + if not kid and rel: + return + + return list.append(self, kid) + + + def replace(self, kid, repl): + """Replaces the given kid with a replacement kid""" + + if repl in self: + self.remove(repl) + + self[self.index(kid)] = repl + + if hasattr(kid, "rel"): + repl.rel = kid.rel + setattr(self, kid.rel, repl) + + # cleanup old kid + delattr(kid, "rel") + + elif hasattr(repl, "rel"): + # delete old relation on new child + delattr(repl, "rel") + + delattr(kid, "parent") + repl.parent = self + + return kid + + + def toXml(self, format=True, indent=0, tab=" "): + """Converts the node to XML""" + + lead = tab * indent if format else "" + innerLead = tab * (indent+1) if format else "" + lineBreak = "\n" if format else "" + + relatedChildren = [] + attrsCollection = [] + + for name in self.__slots__: + # "type" is used as node name - no need to repeat it as an attribute + # "parent" is a relation to the parent node - for serialization we ignore these at the moment + # "rel" is used internally to keep the relation to the parent - used by nodes which need to keep track of specific children + # "start" and "end" are for debugging only + if hasattr(self, name) and name not in ("type", "parent", "comments", "selector", "rel", "start", "end") and name[0] != "_": + value = getattr(self, name) + if isinstance(value, AbstractNode): + if hasattr(value, "rel"): + relatedChildren.append(value) + + elif type(value) in (bool, int, float, str, list, set, dict): + if type(value) == bool: + value = "true" if value else "false" + elif type(value) in (int, float): + value = str(value) + elif type(value) in (list, set, dict): + if type(value) == dict: + value = value.keys() + if len(value) == 0: + continue + try: + value = ",".join(value) + except TypeError as ex: + raise Exception("Invalid attribute list child at: %s: %s" % (name, ex)) + + attrsCollection.append('%s=%s' % (name, json.dumps(value))) + + attrs = (" " + " ".join(attrsCollection)) if len(attrsCollection) > 0 else "" + + comments = getattr(self, "comments", None) + scope = getattr(self, "scope", None) + selector = getattr(self, "selector", None) + + if len(self) == 0 and len(relatedChildren) == 0 and (not comments or len(comments) == 0) and not scope and not selector: + result = "%s<%s%s/>%s" % (lead, self.type, attrs, lineBreak) + + else: + result = "%s<%s%s>%s" % (lead, self.type, attrs, lineBreak) + + if comments: + for comment in comments: + result += '%s<comment context="%s" variant="%s">%s</comment>%s' % (innerLead, comment.context, comment.variant, comment.text, lineBreak) + + if scope: + for statKey in scope: + statValue = scope[statKey] + if statValue != None and len(statValue) > 0: + if type(statValue) is set: + statValue = ",".join(statValue) + elif type(statValue) is dict: + statValue = ",".join(statValue.keys()) + + result += '%s<stat name="%s">%s</stat>%s' % (innerLead, statKey, statValue, lineBreak) + + if selector: + for entry in selector: + result += '%s<selector>%s</selector>%s' % (innerLead, entry, lineBreak) + + for child in self: + if not child: + result += "%s<none/>%s" % (innerLead, lineBreak) + elif not hasattr(child, "rel"): + result += child.toXml(format, indent+1) + elif not child in relatedChildren: + raise Exception("Oops, irritated by non related: %s in %s - child says it is related as %s" % (child.type, self.type, child.rel)) + + for child in relatedChildren: + result += "%s<%s>%s" % (innerLead, child.rel, lineBreak) + result += child.toXml(format, indent+2) + result += "%s</%s>%s" % (innerLead, child.rel, lineBreak) + + result += "%s</%s>%s" % (lead, self.type, lineBreak) + + return result + + + def __deepcopy__(self, memo): + """Used by deepcopy function to clone AbstractNode instances""" + + CurrentClass = self.__class__ + + # Create copy + if hasattr(self, "tokenizer"): + result = CurrentClass(tokenizer=self.tokenizer) + else: + result = CurrentClass(type=self.type) + + # Copy children + for child in self: + if child is None: + list.append(result, None) + else: + # Using simple list appends for better performance + childCopy = copy.deepcopy(child, memo) + childCopy.parent = result + list.append(result, childCopy) + + # Sync attributes + # Note: "parent" attribute is handled by append() already + for name in self.__slots__: + if hasattr(self, name) and not name in ("parent", "tokenizer"): + value = getattr(self, name) + if value is None: + pass + elif type(value) in (bool, int, float, str): + setattr(result, name, value) + elif type(value) in (list, set, dict, CurrentClass): + setattr(result, name, copy.deepcopy(value, memo)) + # Scope can be assigned (will be re-created when needed for the copied node) + elif name == "scope": + result.scope = self.scope + + return result + + + def getSource(self): + """Returns the source code of the node""" + + if not self.tokenizer: + raise Exception("Could not find source for node '%s'" % self.type) + + if getattr(self, "start", None) is not None: + if getattr(self, "end", None) is not None: + return self.tokenizer.source[self.start:self.end] + return self.tokenizer.source[self.start:] + + if getattr(self, "end", None) is not None: + return self.tokenizer.source[:self.end] + + return self.tokenizer.source[:] + + + # Map Python built-ins + __repr__ = toXml + __str__ = toXml + + + def __eq__(self, other): + return self is other + + def __bool__(self): + return True