Test - CF per XBOX
This commit is contained in:
+2
-1
@@ -409,7 +409,8 @@ def downloadpage(url, **opt):
|
|||||||
"""
|
"""
|
||||||
load_cookies()
|
load_cookies()
|
||||||
import requests
|
import requests
|
||||||
if 'Linux' in os.name and 'ANDROID_STORAGE' not in os.environ and config.get_platform(True)['num_version'] > 18.2:
|
if ('Linux' in os.name and 'ANDROID_STORAGE' not in os.environ and config.get_platform(True)['num_version'] > 18.2) \
|
||||||
|
or ('nt' in os.name and 'ProfileDrive' in os.environ and 'LogDrive' in os.environ):
|
||||||
from lib.cloudscraper import cloudscraper
|
from lib.cloudscraper import cloudscraper
|
||||||
else:
|
else:
|
||||||
from lib.cloudscraper import cloudscraper_mod as cloudscraper
|
from lib.cloudscraper import cloudscraper_mod as cloudscraper
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import js2py
|
from js2py import js2py
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import js2py
|
from js2py import js2py_old as js2py
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
# The MIT License
|
|
||||||
#
|
|
||||||
# Copyright 2014, 2015 Piotr Dabkowski
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
# a copy of this software and associated documentation files (the 'Software'),
|
|
||||||
# to deal in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
|
||||||
# to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
# substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
||||||
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
||||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
|
||||||
""" This module allows you to translate and execute Javascript in pure python.
|
|
||||||
Basically its implementation of ECMAScript 5.1 in pure python.
|
|
||||||
|
|
||||||
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
|
||||||
|
|
||||||
EXAMPLE:
|
|
||||||
>>> import js2py
|
|
||||||
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
|
||||||
>>> add(1, 2) + 3
|
|
||||||
6
|
|
||||||
>>> add('1', 2, 3)
|
|
||||||
u'12'
|
|
||||||
>>> add.constructor
|
|
||||||
function Function() { [python code] }
|
|
||||||
|
|
||||||
|
|
||||||
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
|
||||||
variable from the context!
|
|
||||||
|
|
||||||
>>> js = js2py.EvalJs()
|
|
||||||
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
|
||||||
>>> js.f(9)
|
|
||||||
81
|
|
||||||
>>> js.a
|
|
||||||
10
|
|
||||||
|
|
||||||
Also you can use its console method to play with interactive javascript console.
|
|
||||||
|
|
||||||
|
|
||||||
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
|
||||||
|
|
||||||
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
|
||||||
|
|
||||||
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
|
||||||
|
|
||||||
NOTE: This module is still not fully finished:
|
|
||||||
|
|
||||||
Date and JSON builtin objects are not implemented
|
|
||||||
Array prototype is not fully finished (will be soon)
|
|
||||||
|
|
||||||
Other than that everything should work fine.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
__author__ = 'Piotr Dabkowski'
|
|
||||||
__all__ = [
|
|
||||||
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
|
||||||
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
|
||||||
'translate_js6', 'PyJsException', 'get_file_contents',
|
|
||||||
'write_file_contents', 'require'
|
|
||||||
]
|
|
||||||
|
|
||||||
from .base import PyJsException
|
|
||||||
from .evaljs import *
|
|
||||||
from .translators import parse as parse_js
|
|
||||||
from .node_import import require
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# The MIT License
|
||||||
|
#
|
||||||
|
# Copyright 2014, 2015 Piotr Dabkowski
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the 'Software'),
|
||||||
|
# to deal in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
# the Software, and to permit persons to whom the Software is furnished to do so, subject
|
||||||
|
# to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
# substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||||
|
""" This module allows you to translate and execute Javascript in pure python.
|
||||||
|
Basically its implementation of ECMAScript 5.1 in pure python.
|
||||||
|
|
||||||
|
Use eval_js method to execute javascript code and get resulting python object (builtin if possible).
|
||||||
|
|
||||||
|
EXAMPLE:
|
||||||
|
>>> import js2py
|
||||||
|
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||||
|
>>> add(1, 2) + 3
|
||||||
|
6
|
||||||
|
>>> add('1', 2, 3)
|
||||||
|
u'12'
|
||||||
|
>>> add.constructor
|
||||||
|
function Function() { [python code] }
|
||||||
|
|
||||||
|
|
||||||
|
Or use EvalJs to execute many javascript code fragments under same context - you would be able to get any
|
||||||
|
variable from the context!
|
||||||
|
|
||||||
|
>>> js = js2py.EvalJs()
|
||||||
|
>>> js.execute('var a = 10; function f(x) {return x*x};')
|
||||||
|
>>> js.f(9)
|
||||||
|
81
|
||||||
|
>>> js.a
|
||||||
|
10
|
||||||
|
|
||||||
|
Also you can use its console method to play with interactive javascript console.
|
||||||
|
|
||||||
|
|
||||||
|
Use parse_js to parse (syntax tree is just like in esprima.js) and translate_js to trasnlate JavaScript.
|
||||||
|
|
||||||
|
Finally, you can use pyimport statement from inside JS code to import and use python libraries.
|
||||||
|
|
||||||
|
>>> js2py.eval_js('pyimport urllib; urllib.urlopen("https://www.google.com")')
|
||||||
|
|
||||||
|
NOTE: This module is still not fully finished:
|
||||||
|
|
||||||
|
Date and JSON builtin objects are not implemented
|
||||||
|
Array prototype is not fully finished (will be soon)
|
||||||
|
|
||||||
|
Other than that everything should work fine.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'Piotr Dabkowski'
|
||||||
|
__all__ = [
|
||||||
|
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'parse_js',
|
||||||
|
'translate_file', 'run_file', 'disable_pyimport', 'eval_js6',
|
||||||
|
'translate_js6', 'PyJsException', 'get_file_contents',
|
||||||
|
'write_file_contents', 'require'
|
||||||
|
]
|
||||||
|
|
||||||
|
from .base import PyJsException
|
||||||
|
from .evaljs import *
|
||||||
|
from .translators import parse as parse_js
|
||||||
|
from .node_import import require
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,281 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from .translators import translate_js, DEFAULT_HEADER
|
||||||
|
from .es6 import js6_to_js5
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import six
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'EvalJs', 'translate_js', 'import_js', 'eval_js', 'translate_file',
|
||||||
|
'eval_js6', 'translate_js6', 'run_file', 'disable_pyimport',
|
||||||
|
'get_file_contents', 'write_file_contents'
|
||||||
|
]
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
|
def disable_pyimport():
|
||||||
|
import pyjsparser.parser
|
||||||
|
pyjsparser.parser.ENABLE_PYIMPORT = False
|
||||||
|
|
||||||
|
|
||||||
|
def path_as_local(path):
|
||||||
|
if os.path.isabs(path):
|
||||||
|
return path
|
||||||
|
# relative to cwd
|
||||||
|
return os.path.join(os.getcwd(), path)
|
||||||
|
|
||||||
|
|
||||||
|
def import_js(path, lib_name, globals):
|
||||||
|
"""Imports from javascript source file.
|
||||||
|
globals is your globals()"""
|
||||||
|
with codecs.open(path_as_local(path), "r", "utf-8") as f:
|
||||||
|
js = f.read()
|
||||||
|
e = EvalJs()
|
||||||
|
e.execute(js)
|
||||||
|
var = e.context['var']
|
||||||
|
globals[lib_name] = var.to_python()
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_contents(path_or_file):
|
||||||
|
if hasattr(path_or_file, 'read'):
|
||||||
|
js = path_or_file.read()
|
||||||
|
else:
|
||||||
|
with codecs.open(path_as_local(path_or_file), "r", "utf-8") as f:
|
||||||
|
js = f.read()
|
||||||
|
return js
|
||||||
|
|
||||||
|
|
||||||
|
def write_file_contents(path_or_file, contents):
|
||||||
|
if hasattr(path_or_file, 'write'):
|
||||||
|
path_or_file.write(contents)
|
||||||
|
else:
|
||||||
|
with open(path_as_local(path_or_file), 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
|
def translate_file(input_path, output_path):
|
||||||
|
'''
|
||||||
|
Translates input JS file to python and saves the it to the output path.
|
||||||
|
It appends some convenience code at the end so that it is easy to import JS objects.
|
||||||
|
|
||||||
|
For example we have a file 'example.js' with: var a = function(x) {return x}
|
||||||
|
translate_file('example.js', 'example.py')
|
||||||
|
|
||||||
|
Now example.py can be easily importend and used:
|
||||||
|
>>> from example import example
|
||||||
|
>>> example.a(30)
|
||||||
|
30
|
||||||
|
'''
|
||||||
|
js = get_file_contents(input_path)
|
||||||
|
|
||||||
|
py_code = translate_js(js)
|
||||||
|
lib_name = os.path.basename(output_path).split('.')[0]
|
||||||
|
head = '__all__ = [%s]\n\n# Don\'t look below, you will not understand this Python code :) I don\'t.\n\n' % repr(
|
||||||
|
lib_name)
|
||||||
|
tail = '\n\n# Add lib to the module scope\n%s = var.to_python()' % lib_name
|
||||||
|
out = head + py_code + tail
|
||||||
|
write_file_contents(output_path, out)
|
||||||
|
|
||||||
|
|
||||||
|
def run_file(path_or_file, context=None):
|
||||||
|
''' Context must be EvalJS object. Runs given path as a JS program. Returns (eval_value, context).
|
||||||
|
'''
|
||||||
|
if context is None:
|
||||||
|
context = EvalJs()
|
||||||
|
if not isinstance(context, EvalJs):
|
||||||
|
raise TypeError('context must be the instance of EvalJs')
|
||||||
|
eval_value = context.eval(get_file_contents(path_or_file))
|
||||||
|
return eval_value, context
|
||||||
|
|
||||||
|
|
||||||
|
def eval_js(js):
|
||||||
|
"""Just like javascript eval. Translates javascript to python,
|
||||||
|
executes and returns python object.
|
||||||
|
js is javascript source code
|
||||||
|
|
||||||
|
EXAMPLE:
|
||||||
|
>>> import js2py
|
||||||
|
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
|
||||||
|
>>> add(1, 2) + 3
|
||||||
|
6
|
||||||
|
>>> add('1', 2, 3)
|
||||||
|
u'12'
|
||||||
|
>>> add.constructor
|
||||||
|
function Function() { [python code] }
|
||||||
|
|
||||||
|
NOTE: For Js Number, String, Boolean and other base types returns appropriate python BUILTIN type.
|
||||||
|
For Js functions and objects, returns Python wrapper - basically behaves like normal python object.
|
||||||
|
If you really want to convert object to python dict you can use to_dict method.
|
||||||
|
"""
|
||||||
|
e = EvalJs()
|
||||||
|
return e.eval(js)
|
||||||
|
|
||||||
|
|
||||||
|
def eval_js6(js):
|
||||||
|
"""Just like eval_js but with experimental support for js6 via babel."""
|
||||||
|
return eval_js(js6_to_js5(js))
|
||||||
|
|
||||||
|
|
||||||
|
def translate_js6(js):
|
||||||
|
"""Just like translate_js but with experimental support for js6 via babel."""
|
||||||
|
return translate_js(js6_to_js5(js))
|
||||||
|
|
||||||
|
|
||||||
|
class EvalJs(object):
|
||||||
|
"""This class supports continuous execution of javascript under same context.
|
||||||
|
|
||||||
|
>>> ctx = EvalJs()
|
||||||
|
>>> ctx.execute('var a = 10;function f(x) {return x*x};')
|
||||||
|
>>> ctx.f(9)
|
||||||
|
81
|
||||||
|
>>> ctx.a
|
||||||
|
10
|
||||||
|
|
||||||
|
context is a python dict or object that contains python variables that should be available to JavaScript
|
||||||
|
For example:
|
||||||
|
>>> ctx = EvalJs({'a': 30})
|
||||||
|
>>> ctx.execute('var x = a')
|
||||||
|
>>> ctx.x
|
||||||
|
30
|
||||||
|
|
||||||
|
You can enable JS require function via enable_require. With this feature enabled you can use js modules
|
||||||
|
from npm, for example:
|
||||||
|
>>> ctx = EvalJs(enable_require=True)
|
||||||
|
>>> ctx.execute("var esprima = require('esprima');")
|
||||||
|
>>> ctx.execute("esprima.parse('var a = 1')")
|
||||||
|
|
||||||
|
You can run interactive javascript console with console method!"""
|
||||||
|
|
||||||
|
def __init__(self, context={}, enable_require=False):
|
||||||
|
self.__dict__['_context'] = {}
|
||||||
|
exec (DEFAULT_HEADER, self._context)
|
||||||
|
self.__dict__['_var'] = self._context['var'].to_python()
|
||||||
|
|
||||||
|
if enable_require:
|
||||||
|
def _js_require_impl(npm_module_name):
|
||||||
|
from .node_import import require
|
||||||
|
from .base import to_python
|
||||||
|
return require(to_python(npm_module_name), context=self._context)
|
||||||
|
setattr(self._var, 'require', _js_require_impl)
|
||||||
|
|
||||||
|
if not isinstance(context, dict):
|
||||||
|
try:
|
||||||
|
context = context.__dict__
|
||||||
|
except:
|
||||||
|
raise TypeError(
|
||||||
|
'context has to be either a dict or have __dict__ attr')
|
||||||
|
for k, v in six.iteritems(context):
|
||||||
|
setattr(self._var, k, v)
|
||||||
|
|
||||||
|
def execute(self, js=None, use_compilation_plan=False):
|
||||||
|
"""executes javascript js in current context
|
||||||
|
|
||||||
|
During initial execute() the converted js is cached for re-use. That means next time you
|
||||||
|
run the same javascript snippet you save many instructions needed to parse and convert the
|
||||||
|
js code to python code.
|
||||||
|
|
||||||
|
This cache causes minor overhead (a cache dicts is updated) but the Js=>Py conversion process
|
||||||
|
is typically expensive compared to actually running the generated python code.
|
||||||
|
|
||||||
|
Note that the cache is just a dict, it has no expiration or cleanup so when running this
|
||||||
|
in automated situations with vast amounts of snippets it might increase memory usage.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cache = self.__dict__['cache']
|
||||||
|
except KeyError:
|
||||||
|
cache = self.__dict__['cache'] = {}
|
||||||
|
hashkey = hashlib.md5(js.encode('utf-8')).digest()
|
||||||
|
try:
|
||||||
|
compiled = cache[hashkey]
|
||||||
|
except KeyError:
|
||||||
|
code = translate_js(
|
||||||
|
js, '', use_compilation_plan=use_compilation_plan)
|
||||||
|
compiled = cache[hashkey] = compile(code, '<EvalJS snippet>',
|
||||||
|
'exec')
|
||||||
|
exec (compiled, self._context)
|
||||||
|
|
||||||
|
def eval(self, expression, use_compilation_plan=False):
|
||||||
|
"""evaluates expression in current context and returns its value"""
|
||||||
|
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||||
|
self.execute(code, use_compilation_plan=use_compilation_plan)
|
||||||
|
return self['PyJsEvalResult']
|
||||||
|
|
||||||
|
def execute_debug(self, js):
|
||||||
|
"""executes javascript js in current context
|
||||||
|
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||||
|
to set breakpoints and inspect the generated python code
|
||||||
|
"""
|
||||||
|
code = translate_js(js, '')
|
||||||
|
# make sure you have a temp folder:
|
||||||
|
filename = 'temp' + os.sep + '_' + hashlib.md5(
|
||||||
|
code.encode("utf-8")).hexdigest() + '.py'
|
||||||
|
try:
|
||||||
|
with open(filename, mode='w') as f:
|
||||||
|
f.write(code)
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
pyCode = compile(f.read(), filename, 'exec')
|
||||||
|
exec(pyCode, self._context)
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
raise err
|
||||||
|
finally:
|
||||||
|
os.remove(filename)
|
||||||
|
try:
|
||||||
|
os.remove(filename + 'c')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def eval_debug(self, expression):
|
||||||
|
"""evaluates expression in current context and returns its value
|
||||||
|
as opposed to the (faster) self.execute method, you can use your regular debugger
|
||||||
|
to set breakpoints and inspect the generated python code
|
||||||
|
"""
|
||||||
|
code = 'PyJsEvalResult = eval(%s)' % json.dumps(expression)
|
||||||
|
self.execute_debug(code)
|
||||||
|
return self['PyJsEvalResult']
|
||||||
|
|
||||||
|
def __getattr__(self, var):
|
||||||
|
return getattr(self._var, var)
|
||||||
|
|
||||||
|
def __getitem__(self, var):
|
||||||
|
return getattr(self._var, var)
|
||||||
|
|
||||||
|
def __setattr__(self, var, val):
|
||||||
|
return setattr(self._var, var, val)
|
||||||
|
|
||||||
|
def __setitem__(self, var, val):
|
||||||
|
return setattr(self._var, var, val)
|
||||||
|
|
||||||
|
def console(self):
|
||||||
|
"""starts to interact (starts interactive console) Something like code.InteractiveConsole"""
|
||||||
|
while True:
|
||||||
|
if six.PY2:
|
||||||
|
code = raw_input('>>> ')
|
||||||
|
else:
|
||||||
|
code = input('>>>')
|
||||||
|
try:
|
||||||
|
print(self.eval(code))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
if DEBUG:
|
||||||
|
sys.stderr.write(traceback.format_exc())
|
||||||
|
else:
|
||||||
|
sys.stderr.write('EXCEPTION: ' + str(e) + '\n')
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
#print x
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
#with open('C:\Users\Piotrek\Desktop\esprima.js', 'rb') as f:
|
||||||
|
# x = f.read()
|
||||||
|
e = EvalJs()
|
||||||
|
e.execute('square(x)')
|
||||||
|
#e.execute(x)
|
||||||
|
e.console()
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
from ..base import *
|
||||||
|
from ..conversions import *
|
||||||
|
from ..func_utils import *
|
||||||
|
from pyjsparser import parse
|
||||||
|
from ..byte_trans import ByteCodeGenerator, Code
|
||||||
|
|
||||||
|
|
||||||
|
def Function(this, args):
|
||||||
|
# convert arguments to python list of strings
|
||||||
|
a = map(to_string, tuple(args))
|
||||||
|
_body = u';'
|
||||||
|
_args = ()
|
||||||
|
if len(a):
|
||||||
|
_body = u'%s;' % a[-1]
|
||||||
|
_args = a[:-1]
|
||||||
|
return executable_function(_body, _args, args.space, global_context=True)
|
||||||
|
|
||||||
|
|
||||||
|
def executable_function(_body, _args, space, global_context=True):
|
||||||
|
func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body)
|
||||||
|
|
||||||
|
co = executable_code(
|
||||||
|
code_str=func_str, space=space, global_context=global_context)
|
||||||
|
return co()
|
||||||
|
|
||||||
|
|
||||||
|
# you can use this one lovely piece of function to compile and execute code on the fly! Watch out though as it may generate lots of code.
|
||||||
|
# todo tape cleanup? we dont know which pieces are needed and which are not so rather impossible without smarter machinery something like GC,
|
||||||
|
# a one solution would be to have a separate tape for functions
|
||||||
|
def executable_code(code_str, space, global_context=True):
|
||||||
|
# parse first to check if any SyntaxErrors
|
||||||
|
parsed = parse(code_str)
|
||||||
|
|
||||||
|
old_tape_len = len(space.byte_generator.exe.tape)
|
||||||
|
space.byte_generator.record_state()
|
||||||
|
start = space.byte_generator.exe.get_new_label()
|
||||||
|
skip = space.byte_generator.exe.get_new_label()
|
||||||
|
space.byte_generator.emit('JUMP', skip)
|
||||||
|
space.byte_generator.emit('LABEL', start)
|
||||||
|
space.byte_generator.emit(parsed)
|
||||||
|
space.byte_generator.emit('NOP')
|
||||||
|
space.byte_generator.emit('LABEL', skip)
|
||||||
|
space.byte_generator.emit('NOP')
|
||||||
|
space.byte_generator.restore_state()
|
||||||
|
|
||||||
|
space.byte_generator.exe.compile(
|
||||||
|
start_loc=old_tape_len
|
||||||
|
) # dont read the code from the beginning, dont be stupid!
|
||||||
|
|
||||||
|
ctx = space.GlobalObj if global_context else space.exe.current_ctx
|
||||||
|
|
||||||
|
def ex_code():
|
||||||
|
ret, status, token = space.byte_generator.exe.execute_fragment_under_context(
|
||||||
|
ctx, start, skip)
|
||||||
|
# todo Clean up the tape!
|
||||||
|
# this is NOT a way to do that because the fragment may contain the executable code! We dont want to remove it
|
||||||
|
#del space.byte_generator.exe.tape[old_tape_len:]
|
||||||
|
if status == 0:
|
||||||
|
return ret
|
||||||
|
elif status == 3:
|
||||||
|
raise token
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Unexpected return status during JIT execution: %d' % status)
|
||||||
|
|
||||||
|
return ex_code
|
||||||
|
|
||||||
|
|
||||||
|
def _eval(this, args):
|
||||||
|
code_str = to_string(get_arg(args, 0))
|
||||||
|
return executable_code(code_str, args.space, global_context=True)()
|
||||||
|
|
||||||
|
|
||||||
|
def log(this, args):
|
||||||
|
print(' '.join(map(to_string, args)))
|
||||||
|
return undefined
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
from ..conversions import *
|
||||||
|
from ..func_utils import *
|
||||||
|
from .jsregexp import RegExpExec
|
||||||
|
|
||||||
|
DIGS = set(u'0123456789')
|
||||||
|
WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||||
|
|
||||||
|
|
||||||
|
def replacement_template(rep, source, span, npar):
|
||||||
|
"""Takes the replacement template and some info about the match and returns filled template
|
||||||
|
"""
|
||||||
|
n = 0
|
||||||
|
res = ''
|
||||||
|
while n < len(rep) - 1:
|
||||||
|
char = rep[n]
|
||||||
|
if char == '$':
|
||||||
|
if rep[n + 1] == '$':
|
||||||
|
res += '$'
|
||||||
|
n += 2
|
||||||
|
continue
|
||||||
|
elif rep[n + 1] == '`':
|
||||||
|
# replace with string that is BEFORE match
|
||||||
|
res += source[:span[0]]
|
||||||
|
n += 2
|
||||||
|
continue
|
||||||
|
elif rep[n + 1] == '\'':
|
||||||
|
# replace with string that is AFTER match
|
||||||
|
res += source[span[1]:]
|
||||||
|
n += 2
|
||||||
|
continue
|
||||||
|
elif rep[n + 1] in DIGS:
|
||||||
|
dig = rep[n + 1]
|
||||||
|
if n + 2 < len(rep) and rep[n + 2] in DIGS:
|
||||||
|
dig += rep[n + 2]
|
||||||
|
num = int(dig)
|
||||||
|
# we will not do any replacements if we dont have this npar or dig is 0
|
||||||
|
if not num or num > len(npar):
|
||||||
|
res += '$' + dig
|
||||||
|
else:
|
||||||
|
# None - undefined has to be replaced with ''
|
||||||
|
res += npar[num - 1] if npar[num - 1] else ''
|
||||||
|
n += 1 + len(dig)
|
||||||
|
continue
|
||||||
|
res += char
|
||||||
|
n += 1
|
||||||
|
if n < len(rep):
|
||||||
|
res += rep[-1]
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
|
||||||
|
|
||||||
|
class StringPrototype:
|
||||||
|
def toString(this, args):
|
||||||
|
if GetClass(this) != 'String':
|
||||||
|
raise MakeError('TypeError',
|
||||||
|
'String.prototype.toString is not generic')
|
||||||
|
if type(this) == unicode:
|
||||||
|
return this
|
||||||
|
assert type(this.value) == unicode
|
||||||
|
return this.value
|
||||||
|
|
||||||
|
def valueOf(this, args):
|
||||||
|
if GetClass(this) != 'String':
|
||||||
|
raise MakeError('TypeError',
|
||||||
|
'String.prototype.valueOf is not generic')
|
||||||
|
if type(this) == unicode:
|
||||||
|
return this
|
||||||
|
assert type(this.value) == unicode
|
||||||
|
return this.value
|
||||||
|
|
||||||
|
def charAt(this, args):
|
||||||
|
cok(this)
|
||||||
|
pos = to_int(get_arg(args, 0))
|
||||||
|
s = to_string(this)
|
||||||
|
if 0 <= pos < len(s):
|
||||||
|
return s[pos]
|
||||||
|
return u''
|
||||||
|
|
||||||
|
def charCodeAt(this, args):
|
||||||
|
cok(this)
|
||||||
|
pos = to_int(get_arg(args, 0))
|
||||||
|
s = to_string(this)
|
||||||
|
if 0 <= pos < len(s):
|
||||||
|
return float(ord(s[pos]))
|
||||||
|
return NaN
|
||||||
|
|
||||||
|
def concat(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this) + u''.join(map(to_string, args))
|
||||||
|
|
||||||
|
def indexOf(this, args):
|
||||||
|
cok(this)
|
||||||
|
search = to_string(get_arg(args, 0))
|
||||||
|
pos = to_int(get_arg(args, 1))
|
||||||
|
s = to_string(this)
|
||||||
|
return float(s.find(search, min(max(pos, 0), len(s))))
|
||||||
|
|
||||||
|
def lastIndexOf(this, args):
|
||||||
|
cok(this)
|
||||||
|
search = to_string(get_arg(args, 0))
|
||||||
|
pos = get_arg(args, 1)
|
||||||
|
s = to_string(this)
|
||||||
|
pos = 10**12 if is_nan(pos) else to_int(pos)
|
||||||
|
return float(s.rfind(search, 0, min(max(pos, 0) + 1, len(s))))
|
||||||
|
|
||||||
|
def localeCompare(this, args):
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
that = to_string(get_arg(args, 0))
|
||||||
|
if s < that:
|
||||||
|
return -1.
|
||||||
|
elif s > that:
|
||||||
|
return 1.
|
||||||
|
return 0.
|
||||||
|
|
||||||
|
def match(this, args):
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
regexp = get_arg(args, 0)
|
||||||
|
r = args.space.NewRegExp(
|
||||||
|
regexp, '') if GetClass(regexp) != 'RegExp' else regexp
|
||||||
|
if not r.glob:
|
||||||
|
return RegExpExec(r, s, space=args.space)
|
||||||
|
r.put('lastIndex', float(0))
|
||||||
|
found = []
|
||||||
|
previous_last_index = 0
|
||||||
|
last_match = True
|
||||||
|
while last_match:
|
||||||
|
result = RegExpExec(r, s, space=args.space)
|
||||||
|
if is_null(result):
|
||||||
|
last_match = False
|
||||||
|
else:
|
||||||
|
this_index = r.get('lastIndex')
|
||||||
|
if this_index == previous_last_index:
|
||||||
|
r.put('lastIndex', float(this_index + 1))
|
||||||
|
previous_last_index += 1
|
||||||
|
else:
|
||||||
|
previous_last_index = this_index
|
||||||
|
matchStr = result.get('0')
|
||||||
|
found.append(matchStr)
|
||||||
|
if not found:
|
||||||
|
return null
|
||||||
|
return args.space.ConstructArray(found)
|
||||||
|
|
||||||
|
def replace(this, args):
|
||||||
|
# VERY COMPLICATED. to check again.
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
searchValue = get_arg(args, 0)
|
||||||
|
replaceValue = get_arg(args, 1)
|
||||||
|
res = ''
|
||||||
|
if not is_callable(replaceValue):
|
||||||
|
replaceValue = to_string(replaceValue)
|
||||||
|
func = False
|
||||||
|
else:
|
||||||
|
func = True
|
||||||
|
# Replace all ( global )
|
||||||
|
if GetClass(searchValue) == 'RegExp' and searchValue.glob:
|
||||||
|
last = 0
|
||||||
|
for e in re.finditer(searchValue.pat, s):
|
||||||
|
res += s[last:e.span()[0]]
|
||||||
|
if func:
|
||||||
|
# prepare arguments for custom func (replaceValue)
|
||||||
|
call_args = (e.group(), ) + e.groups() + (e.span()[1], s)
|
||||||
|
# convert all types to JS before Js bytecode call...
|
||||||
|
res += to_string(
|
||||||
|
replaceValue.call(
|
||||||
|
this, ensure_js_types(call_args,
|
||||||
|
space=args.space)))
|
||||||
|
else:
|
||||||
|
res += replacement_template(replaceValue, s, e.span(),
|
||||||
|
e.groups())
|
||||||
|
last = e.span()[1]
|
||||||
|
res += s[last:]
|
||||||
|
return res
|
||||||
|
elif GetClass(searchValue) == 'RegExp':
|
||||||
|
e = re.search(searchValue.pat, s)
|
||||||
|
if e is None:
|
||||||
|
return s
|
||||||
|
span = e.span()
|
||||||
|
pars = e.groups()
|
||||||
|
match = e.group()
|
||||||
|
else:
|
||||||
|
match = to_string(searchValue)
|
||||||
|
ind = s.find(match)
|
||||||
|
if ind == -1:
|
||||||
|
return s
|
||||||
|
span = ind, ind + len(match)
|
||||||
|
pars = ()
|
||||||
|
res = s[:span[0]]
|
||||||
|
if func:
|
||||||
|
call_args = (match, ) + pars + (span[1], s)
|
||||||
|
# convert all types to JS before Js bytecode call...
|
||||||
|
res += to_string(
|
||||||
|
replaceValue.call(this,
|
||||||
|
ensure_js_types(call_args,
|
||||||
|
space=args.space)))
|
||||||
|
else:
|
||||||
|
res += replacement_template(replaceValue, s, span, pars)
|
||||||
|
res += s[span[1]:]
|
||||||
|
return res
|
||||||
|
|
||||||
|
def search(this, args):
|
||||||
|
cok(this)
|
||||||
|
string = to_string(this)
|
||||||
|
regexp = get_arg(args, 0)
|
||||||
|
if GetClass(regexp) == 'RegExp':
|
||||||
|
rx = regexp
|
||||||
|
else:
|
||||||
|
rx = args.space.NewRegExp(regexp, '')
|
||||||
|
res = re.search(rx.pat, string)
|
||||||
|
if res is not None:
|
||||||
|
return float(res.span()[0])
|
||||||
|
return -1.
|
||||||
|
|
||||||
|
def slice(this, args):
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
start = to_int(get_arg(args, 0))
|
||||||
|
length = len(s)
|
||||||
|
end = get_arg(args, 1)
|
||||||
|
end = length if is_undefined(end) else to_int(end)
|
||||||
|
#From = max(length+start, 0) if start<0 else min(length, start)
|
||||||
|
#To = max(length+end, 0) if end<0 else min(length, end)
|
||||||
|
return s[start:end]
|
||||||
|
|
||||||
|
def split(this, args):
|
||||||
|
# its a bit different from re.split!
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
separator = get_arg(args, 0)
|
||||||
|
limit = get_arg(args, 1)
|
||||||
|
lim = 2**32 - 1 if is_undefined(limit) else to_uint32(limit)
|
||||||
|
if not lim:
|
||||||
|
return args.space.ConstructArray([])
|
||||||
|
if is_undefined(separator):
|
||||||
|
return args.space.ConstructArray([s])
|
||||||
|
len_s = len(s)
|
||||||
|
res = []
|
||||||
|
R = separator if GetClass(separator) == 'RegExp' else to_string(
|
||||||
|
separator)
|
||||||
|
if not len_s:
|
||||||
|
if SplitMatch(s, 0, R) is None:
|
||||||
|
return args.space.ConstructArray([s])
|
||||||
|
return args.space.ConstructArray([])
|
||||||
|
p = q = 0
|
||||||
|
while q != len_s:
|
||||||
|
e, cap = SplitMatch(s, q, R)
|
||||||
|
if e is None or e == p:
|
||||||
|
q += 1
|
||||||
|
continue
|
||||||
|
res.append(s[p:q])
|
||||||
|
p = q = e
|
||||||
|
if len(res) == lim:
|
||||||
|
return args.space.ConstructArray(res)
|
||||||
|
for element in cap:
|
||||||
|
res.append(element)
|
||||||
|
if len(res) == lim:
|
||||||
|
return args.space.ConstructArray(res)
|
||||||
|
res.append(s[p:])
|
||||||
|
return args.space.ConstructArray(res)
|
||||||
|
|
||||||
|
def substring(this, args):
|
||||||
|
cok(this)
|
||||||
|
s = to_string(this)
|
||||||
|
start = to_int(get_arg(args, 0))
|
||||||
|
length = len(s)
|
||||||
|
end = get_arg(args, 1)
|
||||||
|
end = length if is_undefined(end) else to_int(end)
|
||||||
|
fstart = min(max(start, 0), length)
|
||||||
|
fend = min(max(end, 0), length)
|
||||||
|
return s[min(fstart, fend):max(fstart, fend)]
|
||||||
|
|
||||||
|
def substr(this, args):
|
||||||
|
cok(this)
|
||||||
|
#I hate this function and its description in specification
|
||||||
|
r1 = to_string(this)
|
||||||
|
r2 = to_int(get_arg(args, 0))
|
||||||
|
length = get_arg(args, 1)
|
||||||
|
r3 = 10**20 if is_undefined(length) else to_int(length)
|
||||||
|
r4 = len(r1)
|
||||||
|
r5 = r2 if r2 >= 0 else max(0, r2 + r4)
|
||||||
|
r6 = min(max(r3, 0), r4 - r5)
|
||||||
|
if r6 <= 0:
|
||||||
|
return ''
|
||||||
|
return r1[r5:r5 + r6]
|
||||||
|
|
||||||
|
def toLowerCase(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this).lower()
|
||||||
|
|
||||||
|
def toLocaleLowerCase(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this).lower()
|
||||||
|
|
||||||
|
def toUpperCase(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this).upper()
|
||||||
|
|
||||||
|
def toLocaleUpperCase(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this).upper()
|
||||||
|
|
||||||
|
def trim(this, args):
|
||||||
|
cok(this)
|
||||||
|
return to_string(this).strip(WHITE)
|
||||||
|
|
||||||
|
|
||||||
|
def SplitMatch(s, q, R):
|
||||||
|
# s is Py String to match, q is the py int match start and R is Js RegExp or String.
|
||||||
|
if GetClass(R) == 'RegExp':
|
||||||
|
res = R.match(s, q)
|
||||||
|
return (None, ()) if res is None else (res.span()[1], res.groups())
|
||||||
|
# R is just a string
|
||||||
|
if s[q:].startswith(R):
|
||||||
|
return q + len(R), ()
|
||||||
|
return None, ()
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
__all__ = ['require']
|
||||||
|
import subprocess, os, codecs, glob
|
||||||
|
from .evaljs import translate_js, DEFAULT_HEADER
|
||||||
|
import six
|
||||||
|
DID_INIT = False
|
||||||
|
DIRNAME = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules')
|
||||||
|
|
||||||
|
|
||||||
|
def _init():
|
||||||
|
global DID_INIT
|
||||||
|
if DID_INIT:
|
||||||
|
return
|
||||||
|
assert subprocess.call(
|
||||||
|
'node -v', shell=True, cwd=DIRNAME
|
||||||
|
) == 0, 'You must have node installed! run: brew install node'
|
||||||
|
assert subprocess.call(
|
||||||
|
'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'
|
||||||
|
% repr(DIRNAME),
|
||||||
|
shell=True,
|
||||||
|
cwd=DIRNAME) == 0, 'Could not link required node_modules'
|
||||||
|
DID_INIT = True
|
||||||
|
|
||||||
|
|
||||||
|
ADD_TO_GLOBALS_FUNC = '''
|
||||||
|
;function addToGlobals(name, obj) {
|
||||||
|
if (!Object.prototype.hasOwnProperty('_fake_exports')) {
|
||||||
|
Object.prototype._fake_exports = {};
|
||||||
|
}
|
||||||
|
Object.prototype._fake_exports[name] = obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
'''
|
||||||
|
# subprocess.call("""node -e 'require("browserify")'""", shell=True)
|
||||||
|
GET_FROM_GLOBALS_FUNC = '''
|
||||||
|
;function getFromGlobals(name) {
|
||||||
|
if (!Object.prototype.hasOwnProperty('_fake_exports')) {
|
||||||
|
throw Error("Could not find any value named "+name);
|
||||||
|
}
|
||||||
|
if (Object.prototype._fake_exports.hasOwnProperty(name)) {
|
||||||
|
return Object.prototype._fake_exports[name];
|
||||||
|
} else {
|
||||||
|
throw Error("Could not find any value named "+name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
def _get_module_py_name(module_name):
|
||||||
|
return module_name.replace('-', '_')
|
||||||
|
|
||||||
|
def _get_module_var_name(module_name):
|
||||||
|
return _get_module_py_name(module_name).rpartition('/')[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False):
|
||||||
|
assert isinstance(module_name, str), 'module_name must be a string!'
|
||||||
|
py_name = _get_module_py_name(module_name)
|
||||||
|
module_filename = '%s.py' % py_name
|
||||||
|
var_name = _get_module_var_name(module_name)
|
||||||
|
if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
|
||||||
|
module_filename)) or update:
|
||||||
|
_init()
|
||||||
|
in_file_name = 'tmp0in439341018923js2py.js'
|
||||||
|
out_file_name = 'tmp0out439341018923js2py.js'
|
||||||
|
code = ADD_TO_GLOBALS_FUNC
|
||||||
|
if include_polyfill:
|
||||||
|
code += "\n;require('babel-polyfill');\n"
|
||||||
|
code += """
|
||||||
|
var module_temp_love_python = require(%s);
|
||||||
|
addToGlobals(%s, module_temp_love_python);
|
||||||
|
""" % (repr(module_name), repr(module_name))
|
||||||
|
with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
|
||||||
|
f.write(code.encode('utf-8') if six.PY3 else code)
|
||||||
|
|
||||||
|
pkg_name = module_name.partition('/')[0]
|
||||||
|
# make sure the module is installed
|
||||||
|
assert subprocess.call(
|
||||||
|
'cd %s;npm install %s' % (repr(DIRNAME), pkg_name),
|
||||||
|
shell=True,
|
||||||
|
cwd=DIRNAME
|
||||||
|
) == 0, 'Could not install the required module: ' + pkg_name
|
||||||
|
|
||||||
|
# convert the module
|
||||||
|
assert subprocess.call(
|
||||||
|
'''node -e "(require('browserify')('./%s').bundle(function (err,data) {if (err) {console.log(err);throw new Error(err);};fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"'''
|
||||||
|
% (in_file_name, out_file_name),
|
||||||
|
shell=True,
|
||||||
|
cwd=DIRNAME,
|
||||||
|
) == 0, 'Error when converting module to the js bundle'
|
||||||
|
|
||||||
|
os.remove(os.path.join(DIRNAME, in_file_name))
|
||||||
|
with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
|
||||||
|
"utf-8") as f:
|
||||||
|
js_code = f.read()
|
||||||
|
os.remove(os.path.join(DIRNAME, out_file_name))
|
||||||
|
if len(js_code) < 50:
|
||||||
|
raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
|
||||||
|
js_code += GET_FROM_GLOBALS_FUNC
|
||||||
|
js_code += ';var %s = getFromGlobals(%s);%s' % (
|
||||||
|
var_name, repr(module_name), var_name)
|
||||||
|
print('Please wait, translating...')
|
||||||
|
py_code = translate_js(js_code)
|
||||||
|
|
||||||
|
dirname = os.path.dirname(
|
||||||
|
os.path.join(PY_NODE_MODULES_PATH, module_filename))
|
||||||
|
if not os.path.isdir(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
with open(os.path.join(PY_NODE_MODULES_PATH, module_filename),
|
||||||
|
'wb') as f:
|
||||||
|
f.write(py_code.encode('utf-8') if six.PY3 else py_code)
|
||||||
|
else:
|
||||||
|
with codecs.open(
|
||||||
|
os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
|
||||||
|
"utf-8") as f:
|
||||||
|
py_code = f.read()
|
||||||
|
return py_code
|
||||||
|
|
||||||
|
|
||||||
|
def require(module_name, include_polyfill=False, update=False, context=None):
|
||||||
|
"""
|
||||||
|
Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and
|
||||||
|
finally translates the generated JS bundle to Python via Js2Py.
|
||||||
|
Returns a pure python object that behaves like the installed module. Nice!
|
||||||
|
|
||||||
|
:param module_name: Name of the npm module to require. For example 'esprima'.
|
||||||
|
:param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed
|
||||||
|
for some modules that use unsupported features.
|
||||||
|
:param update: Whether to force update the translation. Otherwise uses a cached version if exists.
|
||||||
|
:param context: Optional context in which the translated module should be executed in. If provided, the
|
||||||
|
header (js2py imports) will be skipped as it is assumed that the context already has all the necessary imports.
|
||||||
|
:return: The JsObjectWrapper containing the translated module object. Can be used like a standard python object.
|
||||||
|
"""
|
||||||
|
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update)
|
||||||
|
# this is a bit hacky but we need to strip the default header from the generated code...
|
||||||
|
if context is not None:
|
||||||
|
if not py_code.startswith(DEFAULT_HEADER):
|
||||||
|
# new header version? retranslate...
|
||||||
|
assert not update, "Unexpected header."
|
||||||
|
py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=True)
|
||||||
|
assert py_code.startswith(DEFAULT_HEADER), "Unexpected header."
|
||||||
|
py_code = py_code[len(DEFAULT_HEADER):]
|
||||||
|
context = {} if context is None else context
|
||||||
|
exec (py_code, context)
|
||||||
|
return context['var'][_get_module_var_name(module_name)].to_py()
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# python 3 support
|
||||||
|
import six
|
||||||
|
if six.PY3:
|
||||||
|
basestring = str
|
||||||
|
long = int
|
||||||
|
xrange = range
|
||||||
|
unicode = str
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionPrototype:
|
||||||
|
def toString():
|
||||||
|
if not this.is_callable():
|
||||||
|
raise TypeError('toString is not generic!')
|
||||||
|
args = ', '.join(this.code.__code__.co_varnames[:this.argcount])
|
||||||
|
return 'function %s(%s) ' % (this.func_name, args) + this.source
|
||||||
|
|
||||||
|
def call():
|
||||||
|
arguments_ = arguments
|
||||||
|
if not len(arguments):
|
||||||
|
obj = this.Js(None)
|
||||||
|
else:
|
||||||
|
obj = arguments[0]
|
||||||
|
if len(arguments) <= 1:
|
||||||
|
args = ()
|
||||||
|
else:
|
||||||
|
args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
|
||||||
|
return this.call(obj, args)
|
||||||
|
|
||||||
|
def apply():
|
||||||
|
if not len(arguments):
|
||||||
|
obj = this.Js(None)
|
||||||
|
else:
|
||||||
|
obj = arguments[0]
|
||||||
|
if len(arguments) <= 1:
|
||||||
|
args = ()
|
||||||
|
else:
|
||||||
|
appl = arguments[1]
|
||||||
|
args = tuple([appl[e] for e in xrange(len(appl))])
|
||||||
|
return this.call(obj, args)
|
||||||
|
|
||||||
|
def bind(thisArg):
|
||||||
|
arguments_ = arguments
|
||||||
|
target = this
|
||||||
|
if not target.is_callable():
|
||||||
|
raise this.MakeError(
|
||||||
|
'Object must be callable in order to be used with bind method')
|
||||||
|
if len(arguments) <= 1:
|
||||||
|
args = ()
|
||||||
|
else:
|
||||||
|
args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
|
||||||
|
return this.PyJsBoundFunction(target, thisArg, args)
|
||||||
Generated
+13682
File diff suppressed because one or more lines are too long
+4456
File diff suppressed because it is too large
Load Diff
+5673
File diff suppressed because one or more lines are too long
+7338
File diff suppressed because one or more lines are too long
Generated
+16405
File diff suppressed because one or more lines are too long
@@ -0,0 +1,697 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from pyjsparser.pyjsparserdata import *
|
||||||
|
from .friendly_nodes import *
|
||||||
|
import random
|
||||||
|
import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
from functools import reduce
|
||||||
|
xrange = range
|
||||||
|
unicode = str
|
||||||
|
# number of characters above which expression will be split to multiple lines in order to avoid python parser stack overflow
|
||||||
|
# still experimental so I suggest to set it to 400 in order to avoid common errors
|
||||||
|
# set it to smaller value only if you have problems with parser stack overflow
|
||||||
|
LINE_LEN_LIMIT = 400 # 200 # or any other value - the larger the smaller probability of errors :)
|
||||||
|
|
||||||
|
|
||||||
|
class ForController:
|
||||||
|
def __init__(self):
|
||||||
|
self.inside = [False]
|
||||||
|
self.update = ''
|
||||||
|
|
||||||
|
def enter_for(self, update):
|
||||||
|
self.inside.append(True)
|
||||||
|
self.update = update
|
||||||
|
|
||||||
|
def leave_for(self):
|
||||||
|
self.inside.pop()
|
||||||
|
|
||||||
|
def enter_other(self):
|
||||||
|
self.inside.append(False)
|
||||||
|
|
||||||
|
def leave_other(self):
|
||||||
|
self.inside.pop()
|
||||||
|
|
||||||
|
def is_inside(self):
|
||||||
|
return self.inside[-1]
|
||||||
|
|
||||||
|
|
||||||
|
class InlineStack:
|
||||||
|
NAME = 'PyJs_%s_%d_'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reps = {}
|
||||||
|
self.names = []
|
||||||
|
|
||||||
|
def inject_inlines(self, source):
|
||||||
|
for lval in self.names: # first in first out! Its important by the way
|
||||||
|
source = inject_before_lval(source, lval, self.reps[lval])
|
||||||
|
return source
|
||||||
|
|
||||||
|
def require(self, typ):
|
||||||
|
name = self.NAME % (typ, len(self.names))
|
||||||
|
self.names.append(name)
|
||||||
|
return name
|
||||||
|
|
||||||
|
def define(self, name, val):
|
||||||
|
self.reps[name] = val
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.rel = {}
|
||||||
|
self.names = []
|
||||||
|
|
||||||
|
|
||||||
|
class ContextStack:
|
||||||
|
def __init__(self):
|
||||||
|
self.to_register = set([])
|
||||||
|
self.to_define = {}
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.to_register = set([])
|
||||||
|
self.to_define = {}
|
||||||
|
|
||||||
|
def register(self, var):
|
||||||
|
self.to_register.add(var)
|
||||||
|
|
||||||
|
def define(self, name, code):
|
||||||
|
self.to_define[name] = code
|
||||||
|
self.register(name)
|
||||||
|
|
||||||
|
def get_code(self):
|
||||||
|
code = 'var.registers([%s])\n' % ', '.join(
|
||||||
|
repr(e) for e in self.to_register)
|
||||||
|
for name, func_code in six.iteritems(self.to_define):
|
||||||
|
code += func_code
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def clean_stacks():
|
||||||
|
global Context, inline_stack
|
||||||
|
Context = ContextStack()
|
||||||
|
inline_stack = InlineStack()
|
||||||
|
|
||||||
|
|
||||||
|
def to_key(literal_or_identifier):
|
||||||
|
''' returns string representation of this object'''
|
||||||
|
if literal_or_identifier['type'] == 'Identifier':
|
||||||
|
return literal_or_identifier['name']
|
||||||
|
elif literal_or_identifier['type'] == 'Literal':
|
||||||
|
k = literal_or_identifier['value']
|
||||||
|
if isinstance(k, float):
|
||||||
|
return unicode(float_repr(k))
|
||||||
|
elif 'regex' in literal_or_identifier:
|
||||||
|
return compose_regex(k)
|
||||||
|
elif isinstance(k, bool):
|
||||||
|
return 'true' if k else 'false'
|
||||||
|
elif k is None:
|
||||||
|
return 'null'
|
||||||
|
else:
|
||||||
|
return unicode(k)
|
||||||
|
|
||||||
|
|
||||||
|
def trans(ele, standard=False):
|
||||||
|
"""Translates esprima syntax tree to python by delegating to appropriate translating node"""
|
||||||
|
try:
|
||||||
|
node = globals().get(ele['type'])
|
||||||
|
if not node:
|
||||||
|
raise NotImplementedError('%s is not supported!' % ele['type'])
|
||||||
|
if standard:
|
||||||
|
node = node.__dict__[
|
||||||
|
'standard'] if 'standard' in node.__dict__ else node
|
||||||
|
return node(**ele)
|
||||||
|
except:
|
||||||
|
#print ele
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def limited(func):
|
||||||
|
'''Decorator limiting resulting line length in order to avoid python parser stack overflow -
|
||||||
|
If expression longer than LINE_LEN_LIMIT characters then it will be moved to upper line
|
||||||
|
USE ONLY ON EXPRESSIONS!!! '''
|
||||||
|
|
||||||
|
def f(standard=False, **args):
|
||||||
|
insert_pos = len(
|
||||||
|
inline_stack.names
|
||||||
|
) # in case line is longer than limit we will have to insert the lval at current position
|
||||||
|
# this is because calling func will change inline_stack.
|
||||||
|
# we cant use inline_stack.require here because we dont know whether line overflows yet
|
||||||
|
res = func(**args)
|
||||||
|
if len(res) > LINE_LEN_LIMIT:
|
||||||
|
name = inline_stack.require('LONG')
|
||||||
|
inline_stack.names.pop()
|
||||||
|
inline_stack.names.insert(insert_pos, name)
|
||||||
|
res = 'def %s(var=var):\n return %s\n' % (name, res)
|
||||||
|
inline_stack.define(name, res)
|
||||||
|
return name + '()'
|
||||||
|
else:
|
||||||
|
return res
|
||||||
|
|
||||||
|
f.__dict__['standard'] = func
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
# ==== IDENTIFIERS AND LITERALS =======
|
||||||
|
|
||||||
|
inf = float('inf')
|
||||||
|
|
||||||
|
|
||||||
|
def Literal(type, value, raw, regex=None):
|
||||||
|
if regex: # regex
|
||||||
|
return 'JsRegExp(%s)' % repr(compose_regex(value))
|
||||||
|
elif value is None: # null
|
||||||
|
return 'var.get(u"null")'
|
||||||
|
# Todo template
|
||||||
|
# String, Bool, Float
|
||||||
|
return 'Js(%s)' % repr(value) if value != inf else 'Js(float("inf"))'
|
||||||
|
|
||||||
|
|
||||||
|
def Identifier(type, name):
|
||||||
|
return 'var.get(%s)' % repr(name)
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def MemberExpression(type, computed, object, property):
|
||||||
|
far_left = trans(object)
|
||||||
|
if computed: # obj[prop] type accessor
|
||||||
|
# may be literal which is the same in every case so we can save some time on conversion
|
||||||
|
if property['type'] == 'Literal':
|
||||||
|
prop = repr(to_key(property))
|
||||||
|
else: # worst case
|
||||||
|
prop = trans(property)
|
||||||
|
else: # always the same since not computed (obj.prop accessor)
|
||||||
|
prop = repr(to_key(property))
|
||||||
|
return far_left + '.get(%s)' % prop
|
||||||
|
|
||||||
|
|
||||||
|
def ThisExpression(type):
|
||||||
|
return 'var.get(u"this")'
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def CallExpression(type, callee, arguments):
|
||||||
|
arguments = [trans(e) for e in arguments]
|
||||||
|
if callee['type'] == 'MemberExpression':
|
||||||
|
far_left = trans(callee['object'])
|
||||||
|
if callee['computed']: # obj[prop] type accessor
|
||||||
|
# may be literal which is the same in every case so we can save some time on conversion
|
||||||
|
if callee['property']['type'] == 'Literal':
|
||||||
|
prop = repr(to_key(callee['property']))
|
||||||
|
else: # worst case
|
||||||
|
prop = trans(
|
||||||
|
callee['property']) # its not a string literal! so no repr
|
||||||
|
else: # always the same since not computed (obj.prop accessor)
|
||||||
|
prop = repr(to_key(callee['property']))
|
||||||
|
arguments.insert(0, prop)
|
||||||
|
return far_left + '.callprop(%s)' % ', '.join(arguments)
|
||||||
|
else: # standard call
|
||||||
|
return trans(callee) + '(%s)' % ', '.join(arguments)
|
||||||
|
|
||||||
|
|
||||||
|
# ========== ARRAYS ============
|
||||||
|
|
||||||
|
|
||||||
|
def ArrayExpression(type, elements): # todo fix null inside problem
|
||||||
|
return 'Js([%s])' % ', '.join(trans(e) if e else 'None' for e in elements)
|
||||||
|
|
||||||
|
|
||||||
|
# ========== OBJECTS =============
|
||||||
|
|
||||||
|
|
||||||
|
def ObjectExpression(type, properties):
|
||||||
|
name = None
|
||||||
|
elems = []
|
||||||
|
after = ''
|
||||||
|
for p in properties:
|
||||||
|
if p['kind'] == 'init':
|
||||||
|
elems.append('%s:%s' % Property(**p))
|
||||||
|
else:
|
||||||
|
if name is None:
|
||||||
|
name = inline_stack.require('Object')
|
||||||
|
if p['kind'] == 'set':
|
||||||
|
k, setter = Property(
|
||||||
|
**p
|
||||||
|
) # setter is just a lval referring to that function, it will be defined in InlineStack automatically
|
||||||
|
after += '%s.define_own_property(%s, {"set":%s, "configurable":True, "enumerable":True})\n' % (
|
||||||
|
name, k, setter)
|
||||||
|
elif p['kind'] == 'get':
|
||||||
|
k, getter = Property(**p)
|
||||||
|
after += '%s.define_own_property(%s, {"get":%s, "configurable":True, "enumerable":True})\n' % (
|
||||||
|
name, k, getter)
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Unexpected object propery kind')
|
||||||
|
definition = 'Js({%s})' % ','.join(elems)
|
||||||
|
if name is None:
|
||||||
|
return definition
|
||||||
|
body = '%s = %s\n' % (name, definition)
|
||||||
|
body += after
|
||||||
|
body += 'return %s\n' % name
|
||||||
|
code = 'def %s():\n%s' % (name, indent(body))
|
||||||
|
inline_stack.define(name, code)
|
||||||
|
return name + '()'
|
||||||
|
|
||||||
|
|
||||||
|
def Property(type, kind, key, computed, value, method, shorthand):
|
||||||
|
if shorthand or computed:
|
||||||
|
raise NotImplementedError(
|
||||||
|
'Shorthand and Computed properties not implemented!')
|
||||||
|
k = to_key(key)
|
||||||
|
if k is None:
|
||||||
|
raise SyntaxError('Invalid key in dictionary! Or bug in Js2Py')
|
||||||
|
v = trans(value)
|
||||||
|
return repr(k), v
|
||||||
|
|
||||||
|
|
||||||
|
# ========== EXPRESSIONS ============
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def UnaryExpression(type, operator, argument, prefix):
|
||||||
|
a = trans(
|
||||||
|
argument, standard=True
|
||||||
|
) # unary involve some complex operations so we cant use line shorteners here
|
||||||
|
if operator == 'delete':
|
||||||
|
if argument['type'] in ('Identifier', 'MemberExpression'):
|
||||||
|
# means that operation is valid
|
||||||
|
return js_delete(a)
|
||||||
|
return 'PyJsComma(%s, Js(True))' % a # otherwise not valid, just perform expression and return true.
|
||||||
|
elif operator == 'typeof':
|
||||||
|
return js_typeof(a)
|
||||||
|
return UNARY[operator](a)
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def BinaryExpression(type, operator, left, right):
|
||||||
|
a = trans(left)
|
||||||
|
b = trans(right)
|
||||||
|
# delegate to our friends
|
||||||
|
return BINARY[operator](a, b)
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def UpdateExpression(type, operator, argument, prefix):
|
||||||
|
a = trans(
|
||||||
|
argument, standard=True
|
||||||
|
) # also complex operation involving parsing of the result so no line length reducing here
|
||||||
|
return js_postfix(a, operator == '++', not prefix)
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def AssignmentExpression(type, operator, left, right):
|
||||||
|
operator = operator[:-1]
|
||||||
|
if left['type'] == 'Identifier':
|
||||||
|
if operator:
|
||||||
|
return 'var.put(%s, %s, %s)' % (repr(to_key(left)), trans(right),
|
||||||
|
repr(operator))
|
||||||
|
else:
|
||||||
|
return 'var.put(%s, %s)' % (repr(to_key(left)), trans(right))
|
||||||
|
elif left['type'] == 'MemberExpression':
|
||||||
|
far_left = trans(left['object'])
|
||||||
|
if left['computed']: # obj[prop] type accessor
|
||||||
|
# may be literal which is the same in every case so we can save some time on conversion
|
||||||
|
if left['property']['type'] == 'Literal':
|
||||||
|
prop = repr(to_key(left['property']))
|
||||||
|
else: # worst case
|
||||||
|
prop = trans(
|
||||||
|
left['property']) # its not a string literal! so no repr
|
||||||
|
else: # always the same since not computed (obj.prop accessor)
|
||||||
|
prop = repr(to_key(left['property']))
|
||||||
|
if operator:
|
||||||
|
return far_left + '.put(%s, %s, %s)' % (prop, trans(right),
|
||||||
|
repr(operator))
|
||||||
|
else:
|
||||||
|
return far_left + '.put(%s, %s)' % (prop, trans(right))
|
||||||
|
else:
|
||||||
|
raise SyntaxError('Invalid left hand side in assignment!')
|
||||||
|
|
||||||
|
|
||||||
|
six
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def SequenceExpression(type, expressions):
|
||||||
|
return reduce(js_comma, (trans(e) for e in expressions))
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def NewExpression(type, callee, arguments):
|
||||||
|
return trans(callee) + '.create(%s)' % ', '.join(
|
||||||
|
trans(e) for e in arguments)
|
||||||
|
|
||||||
|
|
||||||
|
@limited
|
||||||
|
def ConditionalExpression(
|
||||||
|
type, test, consequent,
|
||||||
|
alternate): # caused plenty of problems in my home-made translator :)
|
||||||
|
return '(%s if %s else %s)' % (trans(consequent), trans(test),
|
||||||
|
trans(alternate))
|
||||||
|
|
||||||
|
|
||||||
|
# =========== STATEMENTS =============
|
||||||
|
|
||||||
|
|
||||||
|
def BlockStatement(type, body):
|
||||||
|
return StatementList(
|
||||||
|
body) # never returns empty string! In the worst case returns pass\n
|
||||||
|
|
||||||
|
|
||||||
|
def ExpressionStatement(type, expression):
|
||||||
|
return trans(expression) + '\n' # end expression space with new line
|
||||||
|
|
||||||
|
|
||||||
|
def BreakStatement(type, label):
|
||||||
|
if label:
|
||||||
|
return 'raise %s("Breaked")\n' % (get_break_label(label['name']))
|
||||||
|
else:
|
||||||
|
return 'break\n'
|
||||||
|
|
||||||
|
|
||||||
|
def ContinueStatement(type, label):
|
||||||
|
if label:
|
||||||
|
return 'raise %s("Continued")\n' % (get_continue_label(label['name']))
|
||||||
|
else:
|
||||||
|
return 'continue\n'
|
||||||
|
|
||||||
|
|
||||||
|
def ReturnStatement(type, argument):
|
||||||
|
return 'return %s\n' % (trans(argument)
|
||||||
|
if argument else "var.get('undefined')")
|
||||||
|
|
||||||
|
|
||||||
|
def EmptyStatement(type):
|
||||||
|
return 'pass\n'
|
||||||
|
|
||||||
|
|
||||||
|
def DebuggerStatement(type):
|
||||||
|
return 'pass\n'
|
||||||
|
|
||||||
|
|
||||||
|
def DoWhileStatement(type, body, test):
|
||||||
|
inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n')
|
||||||
|
result = 'while 1:\n' + indent(inside)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def ForStatement(type, init, test, update, body):
|
||||||
|
update = indent(trans(update)) if update else ''
|
||||||
|
init = trans(init) if init else ''
|
||||||
|
if not init.endswith('\n'):
|
||||||
|
init += '\n'
|
||||||
|
test = trans(test) if test else '1'
|
||||||
|
if not update:
|
||||||
|
result = '#for JS loop\n%swhile %s:\n%s%s\n' % (
|
||||||
|
init, test, indent(trans(body)), update)
|
||||||
|
else:
|
||||||
|
result = '#for JS loop\n%swhile %s:\n' % (init, test)
|
||||||
|
body = 'try:\n%sfinally:\n %s\n' % (indent(trans(body)), update)
|
||||||
|
result += indent(body)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def ForInStatement(type, left, right, body, each):
|
||||||
|
res = 'for PyJsTemp in %s:\n' % trans(right)
|
||||||
|
if left['type'] == "VariableDeclaration":
|
||||||
|
addon = trans(left) # make sure variable is registered
|
||||||
|
if addon != 'pass\n':
|
||||||
|
res = addon + res # we have to execute this expression :(
|
||||||
|
# now extract the name
|
||||||
|
try:
|
||||||
|
name = left['declarations'][0]['id']['name']
|
||||||
|
except:
|
||||||
|
raise RuntimeError('Unusual ForIn loop')
|
||||||
|
elif left['type'] == 'Identifier':
|
||||||
|
name = left['name']
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Unusual ForIn loop')
|
||||||
|
res += indent('var.put(%s, PyJsTemp)\n' % repr(name) + trans(body))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def IfStatement(type, test, consequent, alternate):
|
||||||
|
# NOTE we cannot do elif because function definition inside elif statement would not be possible!
|
||||||
|
IF = 'if %s:\n' % trans(test)
|
||||||
|
IF += indent(trans(consequent))
|
||||||
|
if not alternate:
|
||||||
|
return IF
|
||||||
|
ELSE = 'else:\n' + indent(trans(alternate))
|
||||||
|
return IF + ELSE
|
||||||
|
|
||||||
|
|
||||||
|
def LabeledStatement(type, label, body):
|
||||||
|
# todo consider using smarter approach!
|
||||||
|
inside = trans(body)
|
||||||
|
defs = ''
|
||||||
|
if inside.startswith('while ') or inside.startswith(
|
||||||
|
'for ') or inside.startswith('#for'):
|
||||||
|
# we have to add contine label as well...
|
||||||
|
# 3 or 1 since #for loop type has more lines before real for.
|
||||||
|
sep = 1 if not inside.startswith('#for') else 3
|
||||||
|
cont_label = get_continue_label(label['name'])
|
||||||
|
temp = inside.split('\n')
|
||||||
|
injected = 'try:\n' + '\n'.join(temp[sep:])
|
||||||
|
injected += 'except %s:\n pass\n' % cont_label
|
||||||
|
inside = '\n'.join(temp[:sep]) + '\n' + indent(injected)
|
||||||
|
defs += 'class %s(Exception): pass\n' % cont_label
|
||||||
|
break_label = get_break_label(label['name'])
|
||||||
|
inside = 'try:\n%sexcept %s:\n pass\n' % (indent(inside), break_label)
|
||||||
|
defs += 'class %s(Exception): pass\n' % break_label
|
||||||
|
return defs + inside
|
||||||
|
|
||||||
|
|
||||||
|
def StatementList(lis):
|
||||||
|
if lis: # ensure we don't return empty string because it may ruin indentation!
|
||||||
|
code = ''.join(trans(e) for e in lis)
|
||||||
|
return code if code else 'pass\n'
|
||||||
|
else:
|
||||||
|
return 'pass\n'
|
||||||
|
|
||||||
|
|
||||||
|
def PyimportStatement(type, imp):
|
||||||
|
lib = imp['name']
|
||||||
|
jlib = 'PyImport_%s' % lib
|
||||||
|
code = 'import %s as %s\n' % (lib, jlib)
|
||||||
|
#check whether valid lib name...
|
||||||
|
try:
|
||||||
|
compile(code, '', 'exec')
|
||||||
|
except:
|
||||||
|
raise SyntaxError(
|
||||||
|
'Invalid Python module name (%s) in pyimport statement' % lib)
|
||||||
|
# var.pyimport will handle module conversion to PyJs object
|
||||||
|
code += 'var.pyimport(%s, %s)\n' % (repr(lib), jlib)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def SwitchStatement(type, discriminant, cases):
|
||||||
|
#TODO there will be a problem with continue in a switch statement.... FIX IT
|
||||||
|
code = 'while 1:\n' + indent('SWITCHED = False\nCONDITION = (%s)\n')
|
||||||
|
code = code % trans(discriminant)
|
||||||
|
for case in cases:
|
||||||
|
case_code = None
|
||||||
|
if case['test']: # case (x):
|
||||||
|
case_code = 'if SWITCHED or PyJsStrictEq(CONDITION, %s):\n' % (
|
||||||
|
trans(case['test']))
|
||||||
|
else: # default:
|
||||||
|
case_code = 'if True:\n'
|
||||||
|
case_code += indent('SWITCHED = True\n')
|
||||||
|
case_code += indent(StatementList(case['consequent']))
|
||||||
|
# one more indent for whole
|
||||||
|
code += indent(case_code)
|
||||||
|
# prevent infinite loop and sort out nested switch...
|
||||||
|
code += indent('SWITCHED = True\nbreak\n')
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def ThrowStatement(type, argument):
|
||||||
|
return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans(
|
||||||
|
argument)
|
||||||
|
|
||||||
|
|
||||||
|
def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer):
|
||||||
|
result = 'try:\n%s' % indent(trans(block))
|
||||||
|
# complicated catch statement...
|
||||||
|
if handler:
|
||||||
|
identifier = handler['param']['name']
|
||||||
|
holder = 'PyJsHolder_%s_%d' % (to_hex(identifier),
|
||||||
|
random.randrange(1e8))
|
||||||
|
identifier = repr(identifier)
|
||||||
|
result += 'except PyJsException as PyJsTempException:\n'
|
||||||
|
# fill in except ( catch ) block and remember to recover holder variable to its previous state
|
||||||
|
result += indent(
|
||||||
|
TRY_CATCH.replace('HOLDER',
|
||||||
|
holder).replace('NAME', identifier).replace(
|
||||||
|
'BLOCK', indent(trans(handler['body']))))
|
||||||
|
# translate finally statement if present
|
||||||
|
if finalizer:
|
||||||
|
result += 'finally:\n%s' % indent(trans(finalizer))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def LexicalDeclaration(type, declarations, kind):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'let and const not implemented yet but they will be soon! Check github for updates.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def VariableDeclarator(type, id, init):
|
||||||
|
name = id['name']
|
||||||
|
# register the name if not already registered
|
||||||
|
Context.register(name)
|
||||||
|
if init:
|
||||||
|
return 'var.put(%s, %s)\n' % (repr(name), trans(init))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def VariableDeclaration(type, declarations, kind):
|
||||||
|
code = ''.join(trans(d) for d in declarations)
|
||||||
|
return code if code else 'pass\n'
|
||||||
|
|
||||||
|
|
||||||
|
def WhileStatement(type, test, body):
|
||||||
|
result = 'while %s:\n' % trans(test) + indent(trans(body))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def WithStatement(type, object, body):
|
||||||
|
raise NotImplementedError('With statement not implemented!')
|
||||||
|
|
||||||
|
|
||||||
|
def Program(type, body):
|
||||||
|
inline_stack.reset()
|
||||||
|
code = ''.join(trans(e) for e in body)
|
||||||
|
# here add hoisted elements (register variables and define functions)
|
||||||
|
code = Context.get_code() + code
|
||||||
|
# replace all inline variables
|
||||||
|
code = inline_stack.inject_inlines(code)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
# ======== FUNCTIONS ============
|
||||||
|
|
||||||
|
|
||||||
|
def FunctionDeclaration(type, id, params, defaults, body, generator,
|
||||||
|
expression):
|
||||||
|
if generator:
|
||||||
|
raise NotImplementedError('Generators not supported')
|
||||||
|
if defaults:
|
||||||
|
raise NotImplementedError('Defaults not supported')
|
||||||
|
if not id:
|
||||||
|
return FunctionExpression(type, id, params, defaults, body, generator,
|
||||||
|
expression) + '\n'
|
||||||
|
JsName = id['name']
|
||||||
|
PyName = 'PyJsHoisted_%s_' % JsName
|
||||||
|
PyName = PyName if is_valid_py_name(PyName) else 'PyJsHoistedNonPyName'
|
||||||
|
# this is quite complicated
|
||||||
|
global Context
|
||||||
|
previous_context = Context
|
||||||
|
# change context to the context of this function
|
||||||
|
Context = ContextStack()
|
||||||
|
# translate body within current context
|
||||||
|
code = trans(body)
|
||||||
|
# get arg names
|
||||||
|
vars = [v['name'] for v in params]
|
||||||
|
# args are automaticaly registered variables
|
||||||
|
Context.to_register.update(vars)
|
||||||
|
# add all hoisted elements inside function
|
||||||
|
code = Context.get_code() + code
|
||||||
|
# check whether args are valid python names:
|
||||||
|
used_vars = []
|
||||||
|
for v in vars:
|
||||||
|
if is_valid_py_name(v):
|
||||||
|
used_vars.append(v)
|
||||||
|
else: # invalid arg in python, for example $, replace with alternatice arg
|
||||||
|
used_vars.append('PyJsArg_%s_' % to_hex(v))
|
||||||
|
header = '@Js\n'
|
||||||
|
header += 'def %s(%sthis, arguments, var=var):\n' % (
|
||||||
|
PyName, ', '.join(used_vars) + (', ' if vars else ''))
|
||||||
|
# transfer names from Py scope to Js scope
|
||||||
|
arg_map = dict(zip(vars, used_vars))
|
||||||
|
arg_map.update({'this': 'this', 'arguments': 'arguments'})
|
||||||
|
arg_conv = 'var = Scope({%s}, var)\n' % ', '.join(
|
||||||
|
repr(k) + ':' + v for k, v in six.iteritems(arg_map))
|
||||||
|
# and finally set the name of the function to its real name:
|
||||||
|
footer = '%s.func_name = %s\n' % (PyName, repr(JsName))
|
||||||
|
footer += 'var.put(%s, %s)\n' % (repr(JsName), PyName)
|
||||||
|
whole_code = header + indent(arg_conv + code) + footer
|
||||||
|
# restore context
|
||||||
|
Context = previous_context
|
||||||
|
# define in upper context
|
||||||
|
Context.define(JsName, whole_code)
|
||||||
|
return 'pass\n'
|
||||||
|
|
||||||
|
|
||||||
|
def FunctionExpression(type, id, params, defaults, body, generator,
|
||||||
|
expression):
|
||||||
|
if generator:
|
||||||
|
raise NotImplementedError('Generators not supported')
|
||||||
|
if defaults:
|
||||||
|
raise NotImplementedError('Defaults not supported')
|
||||||
|
JsName = id['name'] if id else 'anonymous'
|
||||||
|
if not is_valid_py_name(JsName):
|
||||||
|
ScriptName = 'InlineNonPyName'
|
||||||
|
else:
|
||||||
|
ScriptName = JsName
|
||||||
|
PyName = inline_stack.require(ScriptName) # this is unique
|
||||||
|
|
||||||
|
# again quite complicated
|
||||||
|
global Context
|
||||||
|
previous_context = Context
|
||||||
|
# change context to the context of this function
|
||||||
|
Context = ContextStack()
|
||||||
|
# translate body within current context
|
||||||
|
code = trans(body)
|
||||||
|
# get arg names
|
||||||
|
vars = [v['name'] for v in params]
|
||||||
|
# args are automaticaly registered variables
|
||||||
|
Context.to_register.update(vars)
|
||||||
|
# add all hoisted elements inside function
|
||||||
|
code = Context.get_code() + code
|
||||||
|
# check whether args are valid python names:
|
||||||
|
used_vars = []
|
||||||
|
for v in vars:
|
||||||
|
if is_valid_py_name(v):
|
||||||
|
used_vars.append(v)
|
||||||
|
else: # invalid arg in python, for example $, replace with alternatice arg
|
||||||
|
used_vars.append('PyJsArg_%s_' % to_hex(v))
|
||||||
|
header = '@Js\n'
|
||||||
|
header += 'def %s(%sthis, arguments, var=var):\n' % (
|
||||||
|
PyName, ', '.join(used_vars) + (', ' if vars else ''))
|
||||||
|
# transfer names from Py scope to Js scope
|
||||||
|
arg_map = dict(zip(vars, used_vars))
|
||||||
|
arg_map.update({'this': 'this', 'arguments': 'arguments'})
|
||||||
|
if id: # make self available from inside...
|
||||||
|
if id['name'] not in arg_map:
|
||||||
|
arg_map[id['name']] = PyName
|
||||||
|
arg_conv = 'var = Scope({%s}, var)\n' % ', '.join(
|
||||||
|
repr(k) + ':' + v for k, v in six.iteritems(arg_map))
|
||||||
|
# and finally set the name of the function to its real name:
|
||||||
|
footer = '%s._set_name(%s)\n' % (PyName, repr(JsName))
|
||||||
|
whole_code = header + indent(arg_conv + code) + footer
|
||||||
|
# restore context
|
||||||
|
Context = previous_context
|
||||||
|
# define in upper context
|
||||||
|
inline_stack.define(PyName, whole_code)
|
||||||
|
return PyName
|
||||||
|
|
||||||
|
|
||||||
|
LogicalExpression = BinaryExpression
|
||||||
|
PostfixExpression = UpdateExpression
|
||||||
|
|
||||||
|
clean_stacks()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import codecs
|
||||||
|
import time
|
||||||
|
import pyjsparser
|
||||||
|
|
||||||
|
c = None #'''`ijfdij`'''
|
||||||
|
if not c:
|
||||||
|
with codecs.open("esp.js", "r", "utf-8") as f:
|
||||||
|
c = f.read()
|
||||||
|
|
||||||
|
print('Started')
|
||||||
|
t = time.time()
|
||||||
|
res = trans(pyjsparser.PyJsParser().parse(c))
|
||||||
|
dt = time.time() - t + 0.000000001
|
||||||
|
print('Translated everyting in', round(dt, 5), 'seconds.')
|
||||||
|
print('Thats %d characters per second' % int(len(c) / dt))
|
||||||
|
with open('res.py', 'w') as f:
|
||||||
|
f.write(res)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user