def visit_Import (self, node): # Import .. can only import modules names = [alias for alias in node.names if not alias.name.startswith (self.stubsName)] if not names: self.skipSemiNew = True return for index, alias in enumerate (names): try: self.useModule (alias.name) except Exception as exception: utils.enhanceException (exception, moduleName = self.module.metadata.name, lineNr = self.lineNr, message = 'Can\'t import module \'{}\''.format (alias.name)) if alias.asname: self.emit ('var {} = __init__ (__world__.{})', self.filterId (alias.asname), self.filterId (alias.name)) else: aliasSplit = alias.name.split ('.', 1) head = aliasSplit [0] tail = aliasSplit [1] if len (aliasSplit) > 1 else '' self.importHeads.add (head) self.emit ('__nest__ ({}, \'{}\', __init__ (__world__.{}))', self.filterId (head), self.filterId (tail), self.filterId (alias.name)) if index < len (names) - 1: self.emit (';\n')
def compile (self): # Define names early, since they are cross-used in module compilation prefix = 'org.{}'.format (__base__.__envir__.transpilerName) self.coreModuleName = '{}.{}'.format (prefix, '__core__') self.baseModuleName = '{}.{}'.format (prefix, '__base__') self.standardModuleName = '{}.{}'.format (prefix, '__standard__') self.builtinModuleName = '{}.{}'.format (prefix, '__builtin__') self.mainModuleName = self.sourceFileName [ : -3] # Module compilation Module (self, ModuleMetadata (self, self.coreModuleName)) Module (self, ModuleMetadata (self, self.baseModuleName)) Module (self, ModuleMetadata (self, self.standardModuleName)) Module (self, ModuleMetadata (self, self.builtinModuleName)) try: moduleMetadata = ModuleMetadata (self, self.mainModuleName) Module (self, moduleMetadata) # Will trigger recursive compilation except Exception as exception: utils.enhanceException ( exception, message = str (exception) ) # Join all non-inline modules normallyImportedTargetCode = ''.join ([ self.moduleDict [moduleName] .targetCode for moduleName in sorted (self.moduleDict) if not moduleName in (self.coreModuleName, self.baseModuleName, self.standardModuleName, self.builtinModuleName, self.mainModuleName) ]) # And sandwich them between the in-line modules targetCode = ( self.header + 'function {} () {{\n'.format (self.mainModuleName) + self.moduleDict [self.coreModuleName].targetCode + self.moduleDict [self.baseModuleName] .targetCode + self.moduleDict [self.standardModuleName] .targetCode + self.moduleDict [self.builtinModuleName].targetCode + normallyImportedTargetCode + self.moduleDict [self.mainModuleName].targetCode + ' return __all__;\n' + '}\n' + 'window [\'{0}\'] = {0} ();\n'.format (self.mainModuleName) ) targetFileName = '{}/{}.js'.format ('{}/{}'.format (self.sourceDir, __base__.__envir__.targetSubDir), self.mainModuleName) utils.log (False, 'Saving result in: {}\n', targetFileName) with utils.create (targetFileName) as aFile: aFile.write (targetCode) miniFileName = '{}/{}/{}.min.js'.format (self.sourceDir, __base__.__envir__.targetSubDir, self.mainModuleName) utils.log (False, 'Saving minified result in: {}\n', miniFileName) if not utils.commandArgs.nomin: minify.run (targetFileName, miniFileName)
def assignTarget (target, value, pathIndices = []): def emitPathIndices (): if pathIndices: self.emit (' ') for pathIndex in pathIndices: self.emit ('[{}]'.format (pathIndex)) else: # Most frequent and simple case, only one atomary LHS pass # Special case for target slice (as opposed to target index) if type (target) == ast.Subscript and type (target.slice) == ast.Slice: self.visit (target.value) try: self.emit ('.__setslice__ (') if target.slice.lower == None: self.emit ('0') else: self.visit (target.slice.lower) self.emit (', ') if target.slice.upper == None: self.emit ('null') else: self.visit (target.slice.upper) self.emit (', ') if target.slice.step: self.visit (target.slice.step) else: self.emit ('null') self.emit (', ') self.visit (value) self.emit (')') except Exception as exception: utils.enhanceException (exception, lineNr = self.lineNr, message = 'Invalid LHS slice') else: if isPropertyAssign and not target.id == self.getTemp ('left'): self.emit ('Object.defineProperty ({}, \'{}\', '.format (self.getscope () .name, target.id)) self.visit (value) emitPathIndices () self.emit (');') else: if type (target) == ast.Name: if type (self.getscope ()) == ast.ClassDef and target.id != self.getTemp ('left'): self.emit ('{}.'.format (self.getscope () .name)) else: self.emit ('var ') self.visit (target) self.emit (' = ') self.visit (value) emitPathIndices ()
def visit_ClassDef (self, node): if not self.scopes: self.all.add (node.name) self.emit ('var {0} = __class__ (\'{0}\', [', node.name) if node.bases: for index, expr in enumerate (node.bases): try: self.emitComma (index) self.visit_Name (expr) except Exception as exception: utils.enhanceException (moduleName = self.module.metadata.name, lineNr = node.lineno, message = 'Invalid base class') else: self.emit ('object') self.emit ('], {{') self.inscope (self.classScope) self.indent () classVarAssigns = [] index = 0 for stmt in node.body: if type (stmt) == ast.FunctionDef: self.emitComma (index, False) self.visit (stmt) index += 1 elif type (stmt) == ast.Assign: classVarAssigns.append (stmt) elif ( type (stmt) == ast.Expr and type (stmt.value) == ast.Call and type (stmt.value.func) == ast.Name and stmt.value.func.id == '__pragma__' ): self.visit (stmt) self.dedent () self.emit ('\n}})') for index, classVarAssign in enumerate (classVarAssigns): self.emit (';\n') self.emit ('{}.', node.name) self.visit (classVarAssign) self.descope () # No earlier, class vars need it
def parse (self): try: utils.log (False, 'Parsing module: {}\n', self.metadata.sourcePath) with open (self.metadata.sourcePath) as sourceFile: self.sourceCode = utils.extraLines + sourceFile.read () self.parseTree = ast.parse (self.sourceCode) except SyntaxError as syntaxError: utils.enhanceException ( syntaxError, moduleName = self.metadata.name, lineNr = syntaxError.lineno, message = ( '{} <SYNTAX FAULT] {}'.format ( syntaxError.text [:syntaxError.offset].lstrip (), syntaxError.text [syntaxError.offset:].rstrip () ) if syntaxError.text else syntaxError.args [0] ) )
def assignTarget (target, value): # Special case for target slice (as opposed to target index) if type (target) == ast.Subscript and type (target.slice) == ast.Slice: self.visit (target.value) try: self.emit ('.__setslice__ (') if target.slice.lower == None: self.emit ('0') else: self.visit (target.slice.lower) self.emit (', ') if target.slice.upper == None: self.emit ('null') else: self.visit (target.slice.upper) self.emit (', ') if target.slice.step: self.visit (target.slice.step) else: self.emit ('null') self.emit (', ') self.visit (value) self.emit (')') except Exception as exception: utils.enhanceException (exception, lineNr = target.lineno, message = 'Invalid LHS slice') else: if not (self.scopes and self.scopes [-1] == self.classScope) and type (target) == ast.Name: # No class var so <className>. not already emitted self.emit ('var ') self.visit (target) self.emit (' = ') self.visit (value)
def visit_ClassDef (self, node): if type (self.getscope ()) == ast.Module: self.all.add (node.name) self.emit ('var {0} = __class__ (\'{0}\', [', self.filterId (node.name)) if node.bases: for index, expr in enumerate (node.bases): try: self.emitComma (index) self.visit_Name (expr) except Exception as exception: utils.enhanceException (moduleName = self.module.metadata.name, lineNr = self.lineNr, message = 'Invalid base class') else: self.emit ('object') self.emit ('], {{') self.inscope (node) self.indent () classVarAssigns = [] index = 0 for stmt in node.body: if type (stmt) == ast.FunctionDef: self.emitComma (index, False) self.visit (stmt) index += 1 elif type (stmt) == ast.Assign: classVarAssigns.append (stmt) # Has to be done after the class because tuple assignment requires the use of an algorithm elif self.getPragmaKindFromExpr (stmt): self.visit (stmt) self.dedent () self.emit ('\n}})') for index, classVarAssign in enumerate (classVarAssigns): self.emit (';\n') self.visit (classVarAssign) self.descope () # No earlier, class vars need it
def visit_Subscript (self, node): self.visit (node.value) if type (node.slice) == ast.Slice: # Then we're sure node.ctx == ast.Load try: if node.slice.step == None: self.emit ('.slice (') if node.slice.lower == None: self.emit ('0') else: self.visit (node.slice.lower) if node.slice.upper != None: self.emit (', ') self.visit (node.slice.upper) else: self.emit ('.__getslice__ (') if node.slice.lower == None: self.emit ('0') else: self.visit (node.slice.lower) self.emit (', ') if node.slice.upper == None: self.emit ('null') else: self.visit (node.slice.upper) self.emit (', ') self.visit (node.slice.step) self.emit (')') except Exception as exception: utils.enhanceException (exception, lineNr = self.lineNr, message = 'Invalid RHS slice') else: # Here target.slice is an ast.Index, target.ctx may vary (ast.ExtSlice not dealth with yet) self.visit (node.slice)
def visit_ImportFrom (self, node): # From .. import can import modules or entities in modules if node.module.startswith (self.stubsName): self.skipSemiNew = True return try: # N.B. It's ok to call a modules __init__ multiple times, see __core__.mod.js for index, alias in enumerate (node.names): if alias.name == '*': # * Never refers to modules, only to entities in modules if len (node.names) > 1: raise Error (moduleName = module.metadata.name, lineNr = node.lineno, message = 'Can\'t import module \'{}\''.format (alias.name)) module = self.useModule (node.module) for index, name in enumerate (module.all): self.emit ('var {0} = __init__ (__world__.{1}).{0}', self.filterId (name), self.filterId (node.module)) if index < len (module.all) - 1: self.emit (';\n') else: try: # Try if alias.name denotes a module self.useModule ('{}.{}'.format (node.module, alias.name)) if alias.asname: self.emit ('var {} = __init__ (__world__.{}.{})', self.filterId (alias.asname), self.filterId (node.module), self.filterId (alias.name)) else: self.emit ('var {0} = __init__ (__world__.{1}.{0})', self.filterId (alias.name), self.filterId (node.module)) except: # If it doesn't it denotes an entity inside a module self.useModule (node.module) if alias.asname: self.emit ('var {} = __init__ (__world__.{}).{}', self.filterId (alias.asname), self.filterId (node.module), self.filterId (alias.name)) else: self.emit ('var {0} = __init__ (__world__.{1}).{0}', self.filterId (alias.name), self.filterId (node.module)) if index < len (node.names) - 1: self.emit (';\n') except Exception as exception: utils.enhanceException (exception, lineNr = self.lineNr, message = 'Can\'t import from module \'{}\''.format (node.module))
def __init__ (self, module): self.module = module self.targetFragments = [] self.skipSemiNew = False self.indentLevel = 0 self.scopes = [] self.use = set () self.all = set () self.importHeads = set () self.aliasers = [self.getAliaser (*alias) for alias in ( # START predef_aliases ('js_sort', 'sort'), ('sort', 'py_sort'), ('js_split', 'split'), ('split', 'py_split'), ('keys', 'py_keys'), ('js_arguments', 'arguments'), ('arguments', 'py_arguments') # END predef_aliases )] self.tempIndices = {} self.stubsName = 'org.{}.stubs.'.format (__base__.__envir__.transpilerName) self.nameConsts = { None: 'null', True: 'true', False: 'false' } self.operators = { ast.Invert: ('~', 100), ast.UAdd: ('+', 100), ast.USub: ('-', 100), ast.Pow: (None, 110), # Dealt with separately ast.Mult: ('*', 90), ast.MatMult: (None, 90), # Dealt with separately ast.Div: ('/', 90), ast.FloorDiv: (None, 90), # Dealt with separately ast.Mod: ('%', 90), ast.Add: ('+', 80), ast.Sub: ('-', 80), ast.LShift: ('<<', 70), ast.RShift: ('>>', 70), ast.BitAnd: ('&', 60), ast.BitXor: ('^', 50), ast.BitOr: ('|', 40), ast.Eq: ('==', 30), ast.NotEq: ('!=', 30), ast.Lt: ('<', 30), ast.LtE: ('<=', 30), ast.Gt: ('>', 30), ast.GtE: ('>=', 30), ast.Is: ('===', 30), # Not really, but closest for now ast.IsNot: ('!==', 30), # Not really, but closest for now ast.In: (None, 30), # Dealt with separately ast.NotIn: (None, 30), # Dealt with separately ast.Not: ('!', 20), ast.And: ('&&', 10), ast.Or: ('||', 0) } self.allowKeywordArgs = utils.commandArgs.kwargs self.memoizeCalls = utils.commandArgs.fcall self.codeGeneration = True try: self.visit (module.parseTree) except Exception as exception: utils.enhanceException (exception, moduleName = self.module.metadata.name, lineNr = self.lineNr) if self.tempIndices: raise utils.Error ( message = '\n\tTemporary variables leak in code generator: {}'.format (self.tempIndices) )