def setDocblock(self, text): if not text: return if isinstance(text, Docblock): self.docblock = text else: self.docblock = Docblock(text) self.setType(self.docblock.getType())
def __init__(self, parentfile, obj): parentfile.statements.append(self) self.statement = obj self.parentfile = parentfile # The original line nr self.lineNr = obj['line'] # The parent statement (the block this is a part of) #self.parent = parentStatement # The filename this statement is in self.filename = parentfile.fileName # The docblock of this statement self.docblock = Docblock(obj['docblock']) # The type of this statement (assignment or expression) self.type = obj['openType'] # The type name self.typeName = obj['openName'] # The scope id self.scopeId = obj['scopeId'] # The scope self.scope = parentfile.scopes[self.scopeId] # The possible params (function) self.params = {} # The possible properties self.properties = {} # Modified variables self.variables = {} self.process()
class WittyStatement: def __init__(self, parentfile, obj): parentfile.statements.append(self) self.statement = obj self.parentfile = parentfile # The original line nr self.lineNr = obj['line'] # The parent statement (the block this is a part of) #self.parent = parentStatement # The filename this statement is in self.filename = parentfile.fileName # The docblock of this statement self.docblock = Docblock(obj['docblock']) # The type of this statement (assignment or expression) self.type = obj['openType'] # The type name self.typeName = obj['openName'] # The scope id self.scopeId = obj['scopeId'] # The scope self.scope = parentfile.scopes[self.scopeId] # The possible params (function) self.params = {} # The possible properties self.properties = {} # Modified variables self.variables = {} self.process() # Add all the variables to the scope # for name in obj['variables']: # tempName = name.replace('[\'', '.') # tempName = tempName.replace('\']', '') # #@todo: What to do with things like [i] or ['zever_'.i] # pieces = tempName.split('.') # workingPiece = self.variables # for index, piece in enumerate(pieces): # # If this is the first piece, it's the var name # if index == 0: # if not piece in self.variables: # self.variables[piece] = {'name': piece, 'properties': {}} # workingPiece = self.variables[piece] # else: # if not piece in workingPiece['properties']: # workingPiece['properties'][piece] = {'name': piece, 'properties': {}} #thisScope['variables'][name] = {'type': '?', 'name': name, 'description': ''} # These should not be added to this scope, but the child scope #self.properties = self.docblock.getProperties() #self.params = self.docblock.getParams() #for pName, pValue in self.params.items(): # thisScope['variables'].append(pName) ## Get a statement docblock attribute def getAttribute(self, attributeName): return self.docblock.getAttribute(attributeName) ## See if a docblock attribute is present def hasAttribute(self, attributeName): return self.docblock.hasAttribute(attributeName) def process(self): if self.typeName == 'var': self.processVar() elif self.typeName == 'function': self.processFunction() elif self.type == 'statement': self.processStatement() elif self.typeName == 'expression': self.processExpression() elif self.type == 'scope': pass else: pr(self.type) pr(self.statement) die() ## Create an empty variable dict def createEmpty(self, name, type): return { 'name': name, 'type': type, 'docblock': None, 'declared': False, 'value': None, 'description': None, 'reference': False, 'options': {}, 'properties': {} } def touchProperty(self, parent, name, type = None): if name in parent['properties']: prop = parent['properties'][name] if type: prop['type'] = type else: prop = self.createEmpty(name, type) prop['type'] = type prop['declared'] = 'property' parent['properties'][name] = prop return prop ## Touch a variable, possibly creating it in the process # @param self The object pointer # @param name The name of the variable # @param type The type of the variable def touchVar(self, name, type = None): if name in self.variables: newVar = self.variables[name] if type: newVar['type'] = type else: newVar = self.createEmpty(name, type) self.variables[name] = newVar # this is always declared if name == 'this' or name == 'arguments': newVar['declared'] = True return newVar def processStatement(self): result = self.statement['result'][0] # Recursively go through all the statements in this file if 'block' in result: for stat in result['block']['parsed']: WittyStatement(self.parentfile, stat) def processExpression(self): # @todo: When there are functions in the expressions, # they're not assigned to the variable if that is needed! # If it's not an assignment, just ignore it if not self.statement['result']['assignment']: # If there are functions assigned ... # @todo: This would still declare the function if it had a name, # which function expressions don't do! if self.statement['result']['functions']: for fnc in self.statement['result']['functions']: self.processFunction(fnc) return # Get the raw expression expression = wf.normalizeExpression(self.statement['result']['text']) targetVar = False targetVars = [] prop = False for target in expression['target']: if not 'name' in target: continue targetVar = self.touchVar(target['name']) prop = targetVar # Now go over every property for part in target['parts']: prop = self.touchProperty(prop, part['text']) # If there's a type, set it to the last prop # We don't parse the value yet, so just set it to unknwon if prop != targetVar: prop['type'] = 'unknown' # If there is a docblock here, set that if self.statement['docblock']: prop['docblock'] = self.statement['docblock'] targetVars.append(prop) # If there are functions assigned ... if self.statement['result']['functions']: for fnc in self.statement['result']['functions']: if prop: self.processFunction(fnc, prop) # @todo: We need to add assign this function to the other targets, too!! # Process a var statement def processVar(self): # Go over every assignment for index, entry in enumerate(self.statement['result']): newVar = self.touchVar(entry['name']['name'], 'undefined') # Since we used the var statement, it's declared newVar['declared'] = True if '=' in entry and 'expression' in entry: if entry['expression']['functions']: self.processFunction(entry['expression']['functions'][0], newVar) else: # @todo: what goes on in this expression? newVar['value'] = entry['expression'] if entry['docblock']: newVar['docblock'] = Docblock(entry['docblock']) dbtype = newVar['docblock'].getType() if dbtype: newVar['type'] = dbtype # Process a function statement def processFunction(self, result = None, newVar = None): if not result: result = self.statement['result'][0] #if not scopeId: #scopeId = self.statement['subscopeId'] if 'scopeId' in result: scopeId = result['scopeId'] else: scopeId = self.statement['subscopeId'] # Add the function variable to this scope if not newVar and 'name' in result: newVar = self.touchVar(result['name']['name'], 'Function') newVar['declared'] = True newVar['type'] = 'Function' # Create a new statement for inside the next scope scopeStat = WittyStatement(self.parentfile, { 'line': self.lineNr, 'docblock': None, 'openType': 'scope', 'openName': 'scope', 'scopeId': scopeId, }) # Process variable in the parens, # Add them to the subscope parenVars = result['paren']['content'].split(',') paraminfo = self.docblock.getParams() for varName in parenVars: # Strip out any spaces or newlines varName = varName.strip() if varName: parVar = scopeStat.touchVar(varName.strip()) parVar['declared'] = True # Parameters are declared variables if varName in paraminfo: parVar['type'] = paraminfo[varName]['type'] parVar['description'] = paraminfo[varName]['description'] # Add the function name as a variable to the current scope if 'name' in result: parVar = scopeStat.touchVar(result['name']['name']) parVar['declared'] = True parVar['reference'] = newVar['name'] # Recursively go through all the statements in this file for stat in result['block']['parsed']: WittyStatement(self.parentfile, stat)
class WittyVariable: # Every variable has a unique id id = None # Extra options options = None # What is the scope of this variable? # This does NOT equal to where it was declared # As global variables can be declared elsewhere scope = None # In what statement was this variable declared? statement = None # Where was this variable used? statements = None # Properties of this variable properties = None propArray = None docblock = None name = None type = None types = None ## Constructor # @param self The object pointer # @param statement The statement of declaration # @param scope The parent WittyScope def __init__(self): self.statements = [] self.propArray = [] self.properties = {} self.options = {} def setDocblock(self, text): if not text: return if isinstance(text, Docblock): self.docblock = text else: self.docblock = Docblock(text) self.setType(self.docblock.getType()) def getAttribute(self, name): if self.docblock: return self.docblock.getAttribute(name) else: return None def setType(self, type): if type: self.type = type self.types = type.split(',') ## Get the properties of the types def getTypeProperties(self): # Initial value is empty variables = {} # Go over every type this variable has for typeName in self.types: # Don't do ourselves, that'll cause a loop if typeName == self.name: continue # Get the variable for the given type typeVar = self.scope.findVariable(typeName) # Update the result value with the found prototype properties if typeVar: variables.update(typeVar.getPrototypeProperties()) return variables def getPrototypeProperties(self, typePrototype = True): result = {} if 'prototype' in self.properties: result = self.properties['prototype'].properties if typePrototype: result.update(self.getTypeProperties()) return result ## Get all the properties of this variable # @param self The object pointer # @param includeTypeProperties Include the prototypal properties of the type def getProperties(self, includeTypeProperties=True): pr('Getting properties of ' + self.name) variables = {} # Now get the prototype properties of the foundVar's type if includeTypeProperties and self.types: # Create empty object variables = self.getTypeProperties() pr(self.properties) # Now overwrite anything with our own properties variables.update(self.properties) else: if self.properties: return self.properties return variables def getReturn(self): if self.docblock: return self.docblock.getReturn() else: return None def setBase(self, variable): # Store info self.info = variable if 'name' in variable: self.setName(variable['name']) # Set the type self.setType(variable['type']) if 'docblock' in variable and variable['docblock']: self.setDocblock(variable['docblock']) if not self.type or self.type in ['undefined', 'unknown']: valueResult = None if variable['value'] and 'result' in variable['value']: valueResult = variable['value']['result'] elif self.statement: # @todo: This shouldn't really go here, and be solved much earlier! valueResult = self.statement.statement['result'] tempTest = valueResult['text'].replace('===', '_EQ_') tempTest = tempTest.replace('==', '_EQ_') if len(tempTest): valueResult['text'] = tempTest[len(tempTest)-1].strip() else: valueResult = None if valueResult: # See if there is a value assignment value = valueResult['text'] # Remove trailing semicolon if value[-1:] == ';': value = value[:-1] if not len(value): pass # Do nothing if there is no text elif value[:4] == 'new ': # Constructor form is used! className = value[4:].split('(')[0].strip() self.type = className elif value[0] == '"' or value[0] == "'": self.type = 'String' elif value in ['true', 'false', 'true;', 'false;']: self.type = 'Boolean' elif value[0] == '{': self.type = 'Object' elif value[0] == '[': self.type = 'Array' elif value.isdigit(): self.type = 'Number' elif '(' in value: # It might be a function call functionName = value.split('(')[0].strip() existing = self.scope.findVariable(functionName) if existing: # Get the type this function returns! returnInfo = existing.getReturn() if returnInfo: self.setType(returnInfo['type']) elif self.scope: # See if it's an existing variable somewhere existing = self.scope.findVariable(value) if existing: self.makeReference(existing) ## Make a reference to the given variable # @param self The object pointer # @param existing The given variable def makeReference(self, existing, forceAll = False): if forceAll or existing.type in ['Function', 'Object', 'Array']: self.properties = existing.properties self.propArray = existing.propArray self.options = existing.options self.setType(existing.type) ## Set the name of this variable # @param self The object pointer # @param name The name of the variable def setName(self, name): self.name = name ## Set the scope # @param scope The parent WittyScope def setScope(self, scope): self.scope = scope ## Set the statement # @param statement The statement of declaration def setStatement(self, statement): self.statement = statement ## Indicate the variable was used here # @param self The object pointer # @param scope The scope where we appeared # @param statement The statement where we appeared def addAppearance(self, scope, statement): self.statements.append(statement) ## Look for properties inside this variable def findProperties(self, properties): if isinstance(properties, str): properties = properties.split('.') if not len(properties): return False findNext = properties.pop(0) if findNext in self.properties: if len(properties): return self.properties[findNext].findProperties(properties) else: return self.properties[findNext] else: return False def touchProperties(self, properties, statement = None): for name, prop in properties.items(): self.addProperty(prop['name'], prop, statement) ## Add a property to this variable def addProperty(self, name, info, statement = None): # If the property already exist, fetch it if name in self.properties: prop = self.properties[name] # If there is a new type set if info['type']: prop.type = info['type'] else: prop = WittyVariable() # Add some basic info prop.id = 0 prop.setScope(self.scope) # Set the current statement prop.setStatement(statement) # Set the name prop.setName(name) # Set basic info prop.setBase(info) self.properties[name] = prop self.propArray.append(prop) # See if there are any sub properties we need to touch if 'properties' in info: # Recursively add deeper properties prop.touchProperties(info['properties'], statement) # If the property is added to a globalizer, it becomes a global! if 'globalizer' in self.options and self.options['globalizer']: rootScope = self.scope.getRoot() newGlob = rootScope.intel.createEmptyVariable() newGlob.setScope(rootScope) newGlob.setStatement(statement) newGlob.setName(name) newGlob.makeReference(prop, True) rootScope.registerVariable(newGlob)