def translate_object(obj, lval, obj_count=1, arr_count=1): obj = obj[1:-1] # remove {} from both ends obj, obj_rep, obj_count = remove_objects(obj, obj_count) obj, arr_rep, arr_count = remove_arrays(obj, arr_count) # functions can be defined inside objects. exp translator cant translate them. # we have to remove them and translate with func translator # its better explained in translate_array function obj, hoisted, inline = functions.remove_functions(obj, all_inline=True) assert not hoisted gsetters_after = '' keys = argsplit(obj) res = [] for i, e in enumerate(keys, 1): e = e.strip() if e.startswith('set '): gsetters_after += translate_setter(lval, e) elif e.startswith('get '): gsetters_after += translate_getter(lval, e) elif ':' not in e: if i < len(keys ): # can happen legally only in the last element {3:2,} raise SyntaxError('Unexpected "," in Object literal') break else: #Not getter, setter or elision spl = argsplit(e, ':') if len(spl) < 2: raise SyntaxError('Invalid Object literal: ' + e) try: key, value = spl except: #len(spl)> 2 print 'Unusual case ' + repr(e) key = spl[0] value = ':'.join(spl[1:]) key = key.strip() if is_internal(key): key = '%s.to_string().value' % key else: key = repr(key) value = exp_translator(value) if not value: raise SyntaxError('Missing value in Object literal') res.append('%s:%s' % (key, value)) res = '%s = Js({%s})\n' % (lval, ','.join(res)) + gsetters_after # translate all the nested objects (including removed earlier functions) for nested_name, nested_info in inline.iteritems(): # functions nested_block, nested_args = nested_info new_def = FUNC_TRANSLATOR(nested_name, nested_block, nested_args) res = new_def + res for lval, obj in obj_rep.iteritems(): #objects new_def, obj_count, arr_count = translate_object( obj, lval, obj_count, arr_count) # add object definition BEFORE array definition res = new_def + res for lval, obj in arr_rep.iteritems(): # arrays new_def, obj_count, arr_count = translate_array( obj, lval, obj_count, arr_count) # add object definition BEFORE array definition res = new_def + res return res, obj_count, arr_count
def translate_js(js, top=TOP_GLOBAL): """js has to be a javascript source code. returns equivalent python code.""" # Remove constant literals no_const, constants = remove_constants(js) #print 'const count', len(constants) # Remove object literals no_obj, objects, obj_count = remove_objects(no_const) #print 'obj count', len(objects) # Remove arrays no_arr, arrays, arr_count = remove_arrays(no_obj) #print 'arr count', len(arrays) # Here remove and replace functions reset_inline_count() no_func, hoisted, inline = remove_functions(no_arr) #translate flow and expressions py_seed, to_register = translate_flow(no_func) # register variables and hoisted functions #top += '# register variables\n' top += 'var.registers(%s)\n' % str(to_register + hoisted.keys()) #Recover functions # hoisted functions recovery defs = '' #defs += '# define hoisted functions\n' #print len(hoisted) , 'HH'*40 for nested_name, nested_info in hoisted.iteritems(): nested_block, nested_args = nested_info new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) new_code += 'PyJsLvalTempHoisted.func_name = %s\n' %repr(nested_name) defs += new_code +'\nvar.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name) #defs += '# Everting ready!\n' # inline functions recovery for nested_name, nested_info in inline.iteritems(): nested_block, nested_args = nested_info new_code = translate_func(nested_name, nested_block, nested_args) py_seed = inject_before_lval(py_seed, nested_name.split('@')[0], new_code) # add hoisted definitiond - they have literals that have to be recovered py_seed = defs + py_seed #Recover arrays for arr_lval, arr_code in arrays.iteritems(): translation, obj_count, arr_count = translate_array(arr_code, arr_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, arr_lval, translation) #Recover objects for obj_lval, obj_code in objects.iteritems(): translation, obj_count, arr_count = translate_object(obj_code, obj_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, obj_lval, translation) #Recover constants py_code = recover_constants(py_seed, constants) return top + py_code
def translate_getter(lval, getter): func = 'function' + getter[3:] try: _, data, _ = functions.remove_functions(func) if not data or len(data)>1: raise Exception() except: raise SyntaxError('Could not parse getter: '+getter) prop = data.keys()[0] body, args = data[prop] if len(args)!=0: #setter must have exactly 0 argument raise SyntaxError('Invalid getter. It must take exactly 0 argument.') # now messy part res = FUNC_TRANSLATOR('getter', body, args) res += "%s.define_own_property(%s, {'get': setter})\n"%(lval, repr(prop)) return res
def translate_func(name, block, args): """Translates functions and all nested functions to Python code. name - name of that function (global functions will be available under var while inline will be available directly under this name ) block - code of the function (*with* brackets {} ) args - arguments that this function takes""" inline = name.startswith('PyJsLvalInline') real_name = '' if inline: name, real_name = name.split('@') arglist = ', '.join(args) + ', ' if args else '' code = '@Js\ndef %s(%sthis, arguments, var=var):\n' % (name, arglist) # register local variables scope = "'this':this, 'arguments':arguments" # it will be a simple dictionary for arg in args: scope += ', %s:%s' % (repr(arg), arg) if real_name: scope += ', %s:%s' % (repr(real_name), name) code += indent('var = Scope({%s}, var)\n' % scope) block, nested_hoisted, nested_inline = remove_functions(block) py_code, to_register = translate_flow(block) # register variables declared with var and names of hoisted functions. to_register += nested_hoisted.keys() if to_register: code += indent('var.registers(%s)\n' % str(to_register)) for nested_name, info in nested_hoisted.iteritems(): nested_block, nested_args = info new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) # Now put definition of hoisted function on the top code += indent(new_code) code += indent('PyJsLvalTempHoisted.func_name = %s\n' % repr(nested_name)) code += indent('var.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name)) for nested_name, info in nested_inline.iteritems(): nested_block, nested_args = info new_code = translate_func(nested_name, nested_block, nested_args) # Inject definitions of inline functions just before usage # nested inline names have this format : LVAL_NAME@REAL_NAME py_code = inject_before_lval(py_code, nested_name.split('@')[0], new_code) if py_code.strip(): code += indent(py_code) return code
def translate_func(name, block, args): """Translates functions and all nested functions to Python code. name - name of that function (global functions will be available under var while inline will be available directly under this name ) block - code of the function (*with* brackets {} ) args - arguments that this function takes""" inline = name.startswith('PyJsLvalInline') real_name = '' if inline: name, real_name = name.split('@') arglist = ', '.join(args) + ', ' if args else '' code = '@Js\ndef %s(%sthis, arguments, var=var):\n' % (name, arglist) # register local variables scope = "'this':this, 'arguments':arguments" #it will be a simple dictionary for arg in args: scope += ', %s:%s' % (repr(arg), arg) if real_name: scope += ', %s:%s' % (repr(real_name), name) code += indent('var = Scope({%s}, var)\n' % scope) block, nested_hoisted, nested_inline = remove_functions(block) py_code, to_register = translate_flow(block) #register variables declared with var and names of hoisted functions. to_register += nested_hoisted.keys() if to_register: code += indent('var.registers(%s)\n' % str(to_register)) for nested_name, info in nested_hoisted.iteritems(): nested_block, nested_args = info new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) # Now put definition of hoisted function on the top code += indent(new_code) code += indent( 'PyJsLvalTempHoisted.func_name = %s\n' % repr(nested_name)) code += indent( 'var.put(%s, PyJsLvalTempHoisted)\n' % repr(nested_name)) for nested_name, info in nested_inline.iteritems(): nested_block, nested_args = info new_code = translate_func(nested_name, nested_block, nested_args) # Inject definitions of inline functions just before usage # nested inline names have this format : LVAL_NAME@REAL_NAME py_code = inject_before_lval(py_code, nested_name.split('@')[0], new_code) if py_code.strip(): code += indent(py_code) return code
def translate_array(array, lval, obj_count=1, arr_count=1): """array has to be any js array for example [1,2,3] lval has to be name of this array. Returns python code that adds lval to the PY scope it should be put before lval""" array = array[1:-1] array, obj_rep, obj_count = remove_objects(array, obj_count) array, arr_rep, arr_count = remove_arrays(array, arr_count) #functions can be also defined in arrays, this caused many problems since in Python # functions cant be defined inside literal # remove functions (they dont contain arrays or objects so can be translated easily) # hoisted functions are treated like inline array, hoisted, inline = functions.remove_functions(array, all_inline=True) assert not hoisted arr = [] # separate elements in array for e in argsplit(array, ','): # translate expressions in array PyJsLvalInline will not be translated! e = exp_translator(e.replace('\n', '')) arr.append(e if e else 'None') arr = '%s = Js([%s])\n' % (lval, ','.join(arr)) #But we can have more code to add to define arrays/objects/functions defined inside this array # translate nested objects: # functions: for nested_name, nested_info in inline.iteritems(): nested_block, nested_args = nested_info new_def = FUNC_TRANSLATOR(nested_name, nested_block, nested_args) arr = new_def + arr for lval, obj in obj_rep.iteritems(): new_def, obj_count, arr_count = translate_object( obj, lval, obj_count, arr_count) # add object definition BEFORE array definition arr = new_def + arr for lval, obj in arr_rep.iteritems(): new_def, obj_count, arr_count = translate_array( obj, lval, obj_count, arr_count) # add object definition BEFORE array definition arr = new_def + arr return arr, obj_count, arr_count
def translate_js(js, top=TOP_GLOBAL): """js has to be a javascript source code. returns equivalent python code.""" # Remove constant literals no_const, constants = remove_constants(js) # print 'const count', len(constants) # Remove object literals no_obj, objects, obj_count = remove_objects(no_const) # print 'obj count', len(objects) # Remove arrays no_arr, arrays, arr_count = remove_arrays(no_obj) # print 'arr count', len(arrays) # Here remove and replace functions reset_inline_count() no_func, hoisted, inline = remove_functions(no_arr) # translate flow and expressions py_seed, to_register = translate_flow(no_func) # register variables and hoisted functions # top += '# register variables\n' top += 'var.registers(%s)\n' % str(to_register + hoisted.keys()) # Recover functions # hoisted functions recovery defs = '' # defs += '# define hoisted functions\n' # print len(hoisted) , 'HH'*40 for nested_name, nested_info in hoisted.iteritems(): nested_block, nested_args = nested_info new_code = translate_func('PyJsLvalTempHoisted', nested_block, nested_args) new_code += 'PyJsLvalTempHoisted.func_name = %s\n' % repr(nested_name) defs += new_code + '\nvar.put(%s, PyJsLvalTempHoisted)\n' % repr( nested_name) # defs += '# Everting ready!\n' # inline functions recovery for nested_name, nested_info in inline.iteritems(): nested_block, nested_args = nested_info new_code = translate_func(nested_name, nested_block, nested_args) py_seed = inject_before_lval(py_seed, nested_name.split('@')[0], new_code) # add hoisted definitiond - they have literals that have to be recovered py_seed = defs + py_seed # Recover arrays for arr_lval, arr_code in arrays.iteritems(): translation, obj_count, arr_count = translate_array( arr_code, arr_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, arr_lval, translation) # Recover objects for obj_lval, obj_code in objects.iteritems(): translation, obj_count, arr_count = translate_object( obj_code, obj_lval, obj_count, arr_count) py_seed = inject_before_lval(py_seed, obj_lval, translation) # Recover constants py_code = recover_constants(py_seed, constants) return top + py_code