rimessa libreria pyjsparser e aggiornata js2py

This commit is contained in:
marco
2019-12-29 11:41:27 +01:00
parent 1ab7044d22
commit 5ce0723c27
13 changed files with 3972 additions and 98 deletions
+3 -10
View File
@@ -5,6 +5,7 @@ import re
from .translators.friendly_nodes import REGEXP_CONVERTER from .translators.friendly_nodes import REGEXP_CONVERTER
from .utils.injector import fix_js_args from .utils.injector import fix_js_args
from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType
from math import floor, log10
import traceback import traceback
try: try:
import numpy import numpy
@@ -603,15 +604,7 @@ class PyJs(object):
elif typ == 'Boolean': elif typ == 'Boolean':
return Js('true') if self.value else Js('false') return Js('true') if self.value else Js('false')
elif typ == 'Number': #or self.Class=='Number': elif typ == 'Number': #or self.Class=='Number':
if self.is_nan(): return Js(unicode(js_dtoa(self.value)))
return Js('NaN')
elif self.is_infinity():
sign = '-' if self.value < 0 else ''
return Js(sign + 'Infinity')
elif isinstance(self.value,
long) or self.value.is_integer(): # dont print .0
return Js(unicode(int(self.value)))
return Js(unicode(self.value)) # accurate enough
elif typ == 'String': elif typ == 'String':
return self return self
else: #object else: #object
@@ -1046,7 +1039,7 @@ def PyJsComma(a, b):
return b return b
from .internals.simplex import JsException as PyJsException from .internals.simplex import JsException as PyJsException, js_dtoa
import pyjsparser import pyjsparser
pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg) pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg)
+24 -8
View File
@@ -116,36 +116,52 @@ def eval_js(js):
def eval_js6(js): def eval_js6(js):
"""Just like eval_js but with experimental support for js6 via babel."""
return eval_js(js6_to_js5(js)) return eval_js(js6_to_js5(js))
def translate_js6(js): def translate_js6(js):
"""Just like translate_js but with experimental support for js6 via babel."""
return translate_js(js6_to_js5(js)) return translate_js(js6_to_js5(js))
class EvalJs(object): class EvalJs(object):
"""This class supports continuous execution of javascript under same context. """This class supports continuous execution of javascript under same context.
>>> js = EvalJs() >>> ctx = EvalJs()
>>> js.execute('var a = 10;function f(x) {return x*x};') >>> ctx.execute('var a = 10;function f(x) {return x*x};')
>>> js.f(9) >>> ctx.f(9)
81 81
>>> js.a >>> ctx.a
10 10
context is a python dict or object that contains python variables that should be available to JavaScript context is a python dict or object that contains python variables that should be available to JavaScript
For example: For example:
>>> js = EvalJs({'a': 30}) >>> ctx = EvalJs({'a': 30})
>>> js.execute('var x = a') >>> ctx.execute('var x = a')
>>> js.x >>> ctx.x
30 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!""" You can run interactive javascript console with console method!"""
def __init__(self, context={}): def __init__(self, context={}, enable_require=False):
self.__dict__['_context'] = {} self.__dict__['_context'] = {}
exec (DEFAULT_HEADER, self._context) exec (DEFAULT_HEADER, self._context)
self.__dict__['_var'] = self._context['var'].to_python() 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): if not isinstance(context, dict):
try: try:
context = context.__dict__ context = context.__dict__
@@ -7,7 +7,7 @@ from ..byte_trans import ByteCodeGenerator, Code
def Function(this, args): def Function(this, args):
# convert arguments to python list of strings # convert arguments to python list of strings
a = map(to_string, tuple(args)) a = list(map(to_string, tuple(args)))
_body = u';' _body = u';'
_args = () _args = ()
if len(a): if len(a):
@@ -42,6 +42,7 @@ def executable_code(code_str, space, global_context=True):
space.byte_generator.emit('LABEL', skip) space.byte_generator.emit('LABEL', skip)
space.byte_generator.emit('NOP') space.byte_generator.emit('NOP')
space.byte_generator.restore_state() space.byte_generator.restore_state()
space.byte_generator.exe.compile( space.byte_generator.exe.compile(
start_loc=old_tape_len start_loc=old_tape_len
) # dont read the code from the beginning, dont be stupid! ) # dont read the code from the beginning, dont be stupid!
@@ -71,5 +72,5 @@ def _eval(this, args):
def log(this, args): def log(this, args):
print ' '.join(map(to_string, args)) print(' '.join(map(to_string, args)))
return undefined return undefined
+1 -1
View File
@@ -4,7 +4,7 @@ from __future__ import unicode_literals
import re import re
from ..conversions import * from ..conversions import *
from ..func_utils import * from ..func_utils import *
from jsregexp import RegExpExec from .jsregexp import RegExpExec
DIGS = set(u'0123456789') 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" 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"
+41 -9
View File
@@ -1,6 +1,6 @@
__all__ = ['require'] __all__ = ['require']
import subprocess, os, codecs, glob import subprocess, os, codecs, glob
from .evaljs import translate_js from .evaljs import translate_js, DEFAULT_HEADER
import six import six
DID_INIT = False DID_INIT = False
DIRNAME = os.path.dirname(os.path.abspath(__file__)) DIRNAME = os.path.dirname(os.path.abspath(__file__))
@@ -15,7 +15,7 @@ def _init():
'node -v', shell=True, cwd=DIRNAME 'node -v', shell=True, cwd=DIRNAME
) == 0, 'You must have node installed! run: brew install node' ) == 0, 'You must have node installed! run: brew install node'
assert subprocess.call( assert subprocess.call(
'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify' 'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'
% repr(DIRNAME), % repr(DIRNAME),
shell=True, shell=True,
cwd=DIRNAME) == 0, 'Could not link required node_modules' cwd=DIRNAME) == 0, 'Could not link required node_modules'
@@ -46,12 +46,18 @@ GET_FROM_GLOBALS_FUNC = '''
''' '''
def _get_module_py_name(module_name):
return module_name.replace('-', '_')
def require(module_name, include_polyfill=False, update=False): 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!' assert isinstance(module_name, str), 'module_name must be a string!'
py_name = module_name.replace('-', '_') py_name = _get_module_py_name(module_name)
module_filename = '%s.py' % py_name module_filename = '%s.py' % py_name
var_name = py_name.rpartition('/')[-1] var_name = _get_module_var_name(module_name)
if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH, if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
module_filename)) or update: module_filename)) or update:
_init() _init()
@@ -77,7 +83,7 @@ def require(module_name, include_polyfill=False, update=False):
# convert the module # convert the module
assert subprocess.call( assert subprocess.call(
'''node -e "(require('browserify')('./%s').bundle(function (err,data) {fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"''' '''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), % (in_file_name, out_file_name),
shell=True, shell=True,
cwd=DIRNAME, cwd=DIRNAME,
@@ -88,7 +94,8 @@ def require(module_name, include_polyfill=False, update=False):
"utf-8") as f: "utf-8") as f:
js_code = f.read() js_code = f.read()
os.remove(os.path.join(DIRNAME, out_file_name)) 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 += GET_FROM_GLOBALS_FUNC
js_code += ';var %s = getFromGlobals(%s);%s' % ( js_code += ';var %s = getFromGlobals(%s);%s' % (
var_name, repr(module_name), var_name) var_name, repr(module_name), var_name)
@@ -107,7 +114,32 @@ def require(module_name, include_polyfill=False, update=False):
os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
"utf-8") as f: "utf-8") as f:
py_code = f.read() py_code = f.read()
return py_code
context = {}
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) exec (py_code, context)
return context['var'][var_name].to_py() return context['var'][_get_module_var_name(module_name)].to_py()
+2 -3
View File
@@ -6,8 +6,6 @@ if six.PY3:
xrange = range xrange = range
unicode = str unicode = str
# todo fix apply and bind
class FunctionPrototype: class FunctionPrototype:
def toString(): def toString():
@@ -41,6 +39,7 @@ class FunctionPrototype:
return this.call(obj, args) return this.call(obj, args)
def bind(thisArg): def bind(thisArg):
arguments_ = arguments
target = this target = this
if not target.is_callable(): if not target.is_callable():
raise this.MakeError( raise this.MakeError(
@@ -48,5 +47,5 @@ class FunctionPrototype:
if len(arguments) <= 1: if len(arguments) <= 1:
args = () args = ()
else: else:
args = tuple([arguments[e] for e in xrange(1, len(arguments))]) args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
return this.PyJsBoundFunction(target, thisArg, args) return this.PyJsBoundFunction(target, thisArg, args)
-9
View File
@@ -1,9 +0,0 @@
from internals import byte_trans
from internals import seval
import pyjsparser
x = r'''
function g() {var h123 = 11; return [function g1() {return h123}, new Function('return h123')]}
g()[1]()
'''
print seval.eval_js_vm(x)
+64 -55
View File
@@ -155,7 +155,7 @@ def limited(func):
inf = float('inf') inf = float('inf')
def Literal(type, value, raw, regex=None, comments=None): def Literal(type, value, raw, regex=None):
if regex: # regex if regex: # regex
return 'JsRegExp(%s)' % repr(compose_regex(value)) return 'JsRegExp(%s)' % repr(compose_regex(value))
elif value is None: # null elif value is None: # null
@@ -165,12 +165,12 @@ def Literal(type, value, raw, regex=None, comments=None):
return 'Js(%s)' % repr(value) if value != inf else 'Js(float("inf"))' return 'Js(%s)' % repr(value) if value != inf else 'Js(float("inf"))'
def Identifier(type, name, comments=None): def Identifier(type, name):
return 'var.get(%s)' % repr(name) return 'var.get(%s)' % repr(name)
@limited @limited
def MemberExpression(type, computed, object, property, comments=None): def MemberExpression(type, computed, object, property):
far_left = trans(object) far_left = trans(object)
if computed: # obj[prop] type accessor if computed: # obj[prop] type accessor
# may be literal which is the same in every case so we can save some time on conversion # may be literal which is the same in every case so we can save some time on conversion
@@ -183,12 +183,12 @@ def MemberExpression(type, computed, object, property, comments=None):
return far_left + '.get(%s)' % prop return far_left + '.get(%s)' % prop
def ThisExpression(type, comments=None): def ThisExpression(type):
return 'var.get(u"this")' return 'var.get(u"this")'
@limited @limited
def CallExpression(type, callee, arguments, comments=None): def CallExpression(type, callee, arguments):
arguments = [trans(e) for e in arguments] arguments = [trans(e) for e in arguments]
if callee['type'] == 'MemberExpression': if callee['type'] == 'MemberExpression':
far_left = trans(callee['object']) far_left = trans(callee['object'])
@@ -210,38 +210,47 @@ def CallExpression(type, callee, arguments, comments=None):
# ========== ARRAYS ============ # ========== ARRAYS ============
def ArrayExpression(type, elements, comments=None): # todo fix null inside problem def ArrayExpression(type, elements): # todo fix null inside problem
return 'Js([%s])' % ', '.join(trans(e) if e else 'None' for e in elements) return 'Js([%s])' % ', '.join(trans(e) if e else 'None' for e in elements)
# ========== OBJECTS ============= # ========== OBJECTS =============
def ObjectExpression(type, properties, comments=None): def ObjectExpression(type, properties):
name = inline_stack.require('Object') name = None
elems = [] elems = []
after = '' after = ''
for p in properties: for p in properties:
if p['kind'] == 'init': if p['kind'] == 'init':
elems.append('%s:%s' % Property(**p)) elems.append('%s:%s' % Property(**p))
elif 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: else:
raise RuntimeError('Unexpected object propery kind') if name is None:
obj = '%s = Js({%s})\n' % (name, ','.join(elems)) name = inline_stack.require('Object')
inline_stack.define(name, obj + after) if p['kind'] == 'set':
return name 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, comments=None): def Property(type, kind, key, computed, value, method, shorthand):
if shorthand or computed: if shorthand or computed:
raise NotImplementedError( raise NotImplementedError(
'Shorthand and Computed properties not implemented!') 'Shorthand and Computed properties not implemented!')
@@ -256,7 +265,7 @@ def Property(type, kind, key, computed, value, method, shorthand, comments=None)
@limited @limited
def UnaryExpression(type, operator, argument, prefix, comments=None): def UnaryExpression(type, operator, argument, prefix):
a = trans( a = trans(
argument, standard=True argument, standard=True
) # unary involve some complex operations so we cant use line shorteners here ) # unary involve some complex operations so we cant use line shorteners here
@@ -271,7 +280,7 @@ def UnaryExpression(type, operator, argument, prefix, comments=None):
@limited @limited
def BinaryExpression(type, operator, left, right, comments=None): def BinaryExpression(type, operator, left, right):
a = trans(left) a = trans(left)
b = trans(right) b = trans(right)
# delegate to our friends # delegate to our friends
@@ -279,7 +288,7 @@ def BinaryExpression(type, operator, left, right, comments=None):
@limited @limited
def UpdateExpression(type, operator, argument, prefix, comments=None): def UpdateExpression(type, operator, argument, prefix):
a = trans( a = trans(
argument, standard=True argument, standard=True
) # also complex operation involving parsing of the result so no line length reducing here ) # also complex operation involving parsing of the result so no line length reducing here
@@ -287,7 +296,7 @@ def UpdateExpression(type, operator, argument, prefix, comments=None):
@limited @limited
def AssignmentExpression(type, operator, left, right, comments=None): def AssignmentExpression(type, operator, left, right):
operator = operator[:-1] operator = operator[:-1]
if left['type'] == 'Identifier': if left['type'] == 'Identifier':
if operator: if operator:
@@ -319,12 +328,12 @@ six
@limited @limited
def SequenceExpression(type, expressions, comments=None): def SequenceExpression(type, expressions):
return reduce(js_comma, (trans(e) for e in expressions)) return reduce(js_comma, (trans(e) for e in expressions))
@limited @limited
def NewExpression(type, callee, arguments, comments=None): def NewExpression(type, callee, arguments):
return trans(callee) + '.create(%s)' % ', '.join( return trans(callee) + '.create(%s)' % ', '.join(
trans(e) for e in arguments) trans(e) for e in arguments)
@@ -332,7 +341,7 @@ def NewExpression(type, callee, arguments, comments=None):
@limited @limited
def ConditionalExpression( def ConditionalExpression(
type, test, consequent, type, test, consequent,
alternate, comments=None): # caused plenty of problems in my home-made translator :) alternate): # caused plenty of problems in my home-made translator :)
return '(%s if %s else %s)' % (trans(consequent), trans(test), return '(%s if %s else %s)' % (trans(consequent), trans(test),
trans(alternate)) trans(alternate))
@@ -340,49 +349,49 @@ def ConditionalExpression(
# =========== STATEMENTS ============= # =========== STATEMENTS =============
def BlockStatement(type, body, comments=None): def BlockStatement(type, body):
return StatementList( return StatementList(
body) # never returns empty string! In the worst case returns pass\n body) # never returns empty string! In the worst case returns pass\n
def ExpressionStatement(type, expression, comments=None): def ExpressionStatement(type, expression):
return trans(expression) + '\n' # end expression space with new line return trans(expression) + '\n' # end expression space with new line
def BreakStatement(type, label, comments=None): def BreakStatement(type, label):
if label: if label:
return 'raise %s("Breaked")\n' % (get_break_label(label['name'])) return 'raise %s("Breaked")\n' % (get_break_label(label['name']))
else: else:
return 'break\n' return 'break\n'
def ContinueStatement(type, label, comments=None): def ContinueStatement(type, label):
if label: if label:
return 'raise %s("Continued")\n' % (get_continue_label(label['name'])) return 'raise %s("Continued")\n' % (get_continue_label(label['name']))
else: else:
return 'continue\n' return 'continue\n'
def ReturnStatement(type, argument, comments=None): def ReturnStatement(type, argument):
return 'return %s\n' % (trans(argument) return 'return %s\n' % (trans(argument)
if argument else "var.get('undefined')") if argument else "var.get('undefined')")
def EmptyStatement(type, comments=None): def EmptyStatement(type):
return 'pass\n' return 'pass\n'
def DebuggerStatement(type, comments=None): def DebuggerStatement(type):
return 'pass\n' return 'pass\n'
def DoWhileStatement(type, body, test, comments=None): def DoWhileStatement(type, body, test):
inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n') inside = trans(body) + 'if not %s:\n' % trans(test) + indent('break\n')
result = 'while 1:\n' + indent(inside) result = 'while 1:\n' + indent(inside)
return result return result
def ForStatement(type, init, test, update, body, comments=None): def ForStatement(type, init, test, update, body):
update = indent(trans(update)) if update else '' update = indent(trans(update)) if update else ''
init = trans(init) if init else '' init = trans(init) if init else ''
if not init.endswith('\n'): if not init.endswith('\n'):
@@ -398,7 +407,7 @@ def ForStatement(type, init, test, update, body, comments=None):
return result return result
def ForInStatement(type, left, right, body, each, comments=None): def ForInStatement(type, left, right, body, each):
res = 'for PyJsTemp in %s:\n' % trans(right) res = 'for PyJsTemp in %s:\n' % trans(right)
if left['type'] == "VariableDeclaration": if left['type'] == "VariableDeclaration":
addon = trans(left) # make sure variable is registered addon = trans(left) # make sure variable is registered
@@ -417,7 +426,7 @@ def ForInStatement(type, left, right, body, each, comments=None):
return res return res
def IfStatement(type, test, consequent, alternate, comments=None): def IfStatement(type, test, consequent, alternate):
# NOTE we cannot do elif because function definition inside elif statement would not be possible! # NOTE we cannot do elif because function definition inside elif statement would not be possible!
IF = 'if %s:\n' % trans(test) IF = 'if %s:\n' % trans(test)
IF += indent(trans(consequent)) IF += indent(trans(consequent))
@@ -427,7 +436,7 @@ def IfStatement(type, test, consequent, alternate, comments=None):
return IF + ELSE return IF + ELSE
def LabeledStatement(type, label, body, comments=None): def LabeledStatement(type, label, body):
# todo consider using smarter approach! # todo consider using smarter approach!
inside = trans(body) inside = trans(body)
defs = '' defs = ''
@@ -448,7 +457,7 @@ def LabeledStatement(type, label, body, comments=None):
return defs + inside return defs + inside
def StatementList(lis, comments=None): def StatementList(lis):
if lis: # ensure we don't return empty string because it may ruin indentation! if lis: # ensure we don't return empty string because it may ruin indentation!
code = ''.join(trans(e) for e in lis) code = ''.join(trans(e) for e in lis)
return code if code else 'pass\n' return code if code else 'pass\n'
@@ -456,7 +465,7 @@ def StatementList(lis, comments=None):
return 'pass\n' return 'pass\n'
def PyimportStatement(type, imp, comments=None): def PyimportStatement(type, imp):
lib = imp['name'] lib = imp['name']
jlib = 'PyImport_%s' % lib jlib = 'PyImport_%s' % lib
code = 'import %s as %s\n' % (lib, jlib) code = 'import %s as %s\n' % (lib, jlib)
@@ -471,7 +480,7 @@ def PyimportStatement(type, imp, comments=None):
return code return code
def SwitchStatement(type, discriminant, cases, comments=None): def SwitchStatement(type, discriminant, cases):
#TODO there will be a problem with continue in a switch statement.... FIX IT #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 = 'while 1:\n' + indent('SWITCHED = False\nCONDITION = (%s)\n')
code = code % trans(discriminant) code = code % trans(discriminant)
@@ -491,12 +500,12 @@ def SwitchStatement(type, discriminant, cases, comments=None):
return code return code
def ThrowStatement(type, argument, comments=None): def ThrowStatement(type, argument):
return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans( return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans(
argument) argument)
def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer, comments=None): def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer):
result = 'try:\n%s' % indent(trans(block)) result = 'try:\n%s' % indent(trans(block))
# complicated catch statement... # complicated catch statement...
if handler: if handler:
@@ -516,13 +525,13 @@ def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer, com
return result return result
def LexicalDeclaration(type, declarations, kind, comments=None): def LexicalDeclaration(type, declarations, kind):
raise NotImplementedError( raise NotImplementedError(
'let and const not implemented yet but they will be soon! Check github for updates.' 'let and const not implemented yet but they will be soon! Check github for updates.'
) )
def VariableDeclarator(type, id, init, comments=None): def VariableDeclarator(type, id, init):
name = id['name'] name = id['name']
# register the name if not already registered # register the name if not already registered
Context.register(name) Context.register(name)
@@ -531,21 +540,21 @@ def VariableDeclarator(type, id, init, comments=None):
return '' return ''
def VariableDeclaration(type, declarations, kind, comments=None): def VariableDeclaration(type, declarations, kind):
code = ''.join(trans(d) for d in declarations) code = ''.join(trans(d) for d in declarations)
return code if code else 'pass\n' return code if code else 'pass\n'
def WhileStatement(type, test, body, comments=None): def WhileStatement(type, test, body):
result = 'while %s:\n' % trans(test) + indent(trans(body)) result = 'while %s:\n' % trans(test) + indent(trans(body))
return result return result
def WithStatement(type, object, body, comments=None): def WithStatement(type, object, body):
raise NotImplementedError('With statement not implemented!') raise NotImplementedError('With statement not implemented!')
def Program(type, body, comments=None): def Program(type, body):
inline_stack.reset() inline_stack.reset()
code = ''.join(trans(e) for e in body) code = ''.join(trans(e) for e in body)
# here add hoisted elements (register variables and define functions) # here add hoisted elements (register variables and define functions)
@@ -559,7 +568,7 @@ def Program(type, body, comments=None):
def FunctionDeclaration(type, id, params, defaults, body, generator, def FunctionDeclaration(type, id, params, defaults, body, generator,
expression, comments=None): expression):
if generator: if generator:
raise NotImplementedError('Generators not supported') raise NotImplementedError('Generators not supported')
if defaults: if defaults:
@@ -610,7 +619,7 @@ def FunctionDeclaration(type, id, params, defaults, body, generator,
def FunctionExpression(type, id, params, defaults, body, generator, def FunctionExpression(type, id, params, defaults, body, generator,
expression, comments=None): expression):
if generator: if generator:
raise NotImplementedError('Generators not supported') raise NotImplementedError('Generators not supported')
if defaults: if defaults:
+10 -1
View File
@@ -115,7 +115,16 @@ def append_arguments(code_obj, new_locals):
code_obj.co_freevars, code_obj.co_cellvars) code_obj.co_freevars, code_obj.co_cellvars)
# Done modifying codestring - make the code object # Done modifying codestring - make the code object
return types.CodeType(*args) if hasattr(code_obj, "replace"):
# Python 3.8+
return code_obj.replace(
co_argcount=co_argcount + new_locals_len,
co_nlocals=code_obj.co_nlocals + new_locals_len,
co_code=code,
co_names=names,
co_varnames=varnames)
else:
return types.CodeType(*args)
def instructions(code_obj): def instructions(code_obj):
+5
View File
@@ -0,0 +1,5 @@
__all__ = ['PyJsParser', 'parse', 'JsSyntaxError', 'pyjsparserdata']
__author__ = 'Piotr Dabkowski'
__version__ = '2.2.0'
from .parser import PyJsParser, parse, JsSyntaxError
from . import pyjsparserdata
File diff suppressed because it is too large Load Diff
+407
View File
@@ -0,0 +1,407 @@
# 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
from __future__ import unicode_literals
import sys
import unicodedata
from collections import defaultdict
PY3 = sys.version_info >= (3, 0)
if PY3:
unichr = chr
xrange = range
unicode = str
token = {
'BooleanLiteral': 1,
'EOF': 2,
'Identifier': 3,
'Keyword': 4,
'NullLiteral': 5,
'NumericLiteral': 6,
'Punctuator': 7,
'StringLiteral': 8,
'RegularExpression': 9,
'Template': 10
}
TokenName = dict((v, k) for k, v in token.items())
FnExprTokens = [
'(',
'{',
'[',
'in',
'typeof',
'instanceof',
'new',
'return',
'case',
'delete',
'throw',
'void',
# assignment operators
'=',
'+=',
'-=',
'*=',
'/=',
'%=',
'<<=',
'>>=',
'>>>=',
'&=',
'|=',
'^=',
',',
# binary/unary operators
'+',
'-',
'*',
'/',
'%',
'++',
'--',
'<<',
'>>',
'>>>',
'&',
'|',
'^',
'!',
'~',
'&&',
'||',
'?',
':',
'===',
'==',
'>=',
'<=',
'<',
'>',
'!=',
'!=='
]
syntax = set(
('AssignmentExpression', 'AssignmentPattern', 'ArrayExpression',
'ArrayPattern', 'ArrowFunctionExpression', 'BlockStatement',
'BinaryExpression', 'BreakStatement', 'CallExpression', 'CatchClause',
'ClassBody', 'ClassDeclaration', 'ClassExpression',
'ConditionalExpression', 'ContinueStatement', 'DoWhileStatement',
'DebuggerStatement', 'EmptyStatement', 'ExportAllDeclaration',
'ExportDefaultDeclaration', 'ExportNamedDeclaration', 'ExportSpecifier',
'ExpressionStatement', 'ForStatement', 'ForInStatement',
'FunctionDeclaration', 'FunctionExpression', 'Identifier', 'IfStatement',
'ImportDeclaration', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier',
'ImportSpecifier', 'Literal', 'LabeledStatement', 'LogicalExpression',
'MemberExpression', 'MethodDefinition', 'NewExpression',
'ObjectExpression', 'ObjectPattern', 'Program', 'Property', 'RestElement',
'ReturnStatement', 'SequenceExpression', 'SpreadElement', 'Super',
'SwitchCase', 'SwitchStatement', 'TaggedTemplateExpression',
'TemplateElement', 'TemplateLiteral', 'ThisExpression', 'ThrowStatement',
'TryStatement', 'UnaryExpression', 'UpdateExpression',
'VariableDeclaration', 'VariableDeclarator', 'WhileStatement',
'WithStatement'))
supported_syntax = set(
('AssignmentExpression', 'ArrayExpression', 'BlockStatement',
'BinaryExpression', 'BreakStatement', 'CallExpression', 'CatchClause',
'ConditionalExpression', 'ContinueStatement', 'DoWhileStatement',
'DebuggerStatement', 'EmptyStatement', 'ExpressionStatement',
'ForStatement', 'ForInStatement', 'FunctionDeclaration',
'FunctionExpression', 'Identifier', 'IfStatement', 'Literal',
'LabeledStatement', 'LogicalExpression', 'MemberExpression',
'MethodDefinition', 'NewExpression', 'ObjectExpression', 'Program',
'Property', 'ReturnStatement', 'SequenceExpression', 'SwitchCase',
'SwitchStatement', 'ThisExpression', 'ThrowStatement', 'TryStatement',
'UnaryExpression', 'UpdateExpression', 'VariableDeclaration',
'VariableDeclarator', 'WhileStatement', 'WithStatement'))
# Error messages should be identical to V8.
messages = {
'UnexpectedToken':
'Unexpected token %s',
'UnexpectedNumber':
'Unexpected number',
'UnexpectedString':
'Unexpected string',
'UnexpectedIdentifier':
'Unexpected identifier',
'UnexpectedReserved':
'Unexpected reserved word',
'UnexpectedTemplate':
'Unexpected quasi %s',
'UnexpectedEOS':
'Unexpected end of input',
'NewlineAfterThrow':
'Illegal newline after throw',
'InvalidRegExp':
'Invalid regular expression',
'UnterminatedRegExp':
'Invalid regular expression: missing /',
'InvalidLHSInAssignment':
'Invalid left-hand side in assignment',
'InvalidLHSInForIn':
'Invalid left-hand side in for-in',
'MultipleDefaultsInSwitch':
'More than one default clause in switch statement',
'NoCatchOrFinally':
'Missing catch or finally after try',
'UnknownLabel':
'Undefined label \'%s\'',
'Redeclaration':
'%s \'%s\' has already been declared',
'IllegalContinue':
'Illegal continue statement',
'IllegalBreak':
'Illegal break statement',
'IllegalReturn':
'Illegal return statement',
'StrictModeWith':
'Strict mode code may not include a with statement',
'StrictCatchVariable':
'Catch variable may not be eval or arguments in strict mode',
'StrictVarName':
'Variable name may not be eval or arguments in strict mode',
'StrictParamName':
'Parameter name eval or arguments is not allowed in strict mode',
'StrictParamDupe':
'Strict mode function may not have duplicate parameter names',
'StrictFunctionName':
'Function name may not be eval or arguments in strict mode',
'StrictOctalLiteral':
'Octal literals are not allowed in strict mode.',
'StrictDelete':
'Delete of an unqualified identifier in strict mode.',
'StrictLHSAssignment':
'Assignment to eval or arguments is not allowed in strict mode',
'StrictLHSPostfix':
'Postfix increment/decrement may not have eval or arguments operand in strict mode',
'StrictLHSPrefix':
'Prefix increment/decrement may not have eval or arguments operand in strict mode',
'StrictReservedWord':
'Use of future reserved word in strict mode',
'TemplateOctalLiteral':
'Octal literals are not allowed in template strings.',
'ParameterAfterRestParameter':
'Rest parameter must be last formal parameter',
'DefaultRestParameter':
'Unexpected token =',
'ObjectPatternAsRestParameter':
'Unexpected token {',
'DuplicateProtoProperty':
'Duplicate __proto__ fields are not allowed in object literals',
'ConstructorSpecialMethod':
'Class constructor may not be an accessor',
'DuplicateConstructor':
'A class may only have one constructor',
'StaticPrototype':
'Classes may not have static property named prototype',
'MissingFromClause':
'Unexpected token',
'NoAsAfterImportNamespace':
'Unexpected token',
'InvalidModuleSpecifier':
'Unexpected token',
'IllegalImportDeclaration':
'Unexpected token',
'IllegalExportDeclaration':
'Unexpected token'
}
PRECEDENCE = {
'||': 1,
'&&': 2,
'|': 3,
'^': 4,
'&': 5,
'==': 6,
'!=': 6,
'===': 6,
'!==': 6,
'<': 7,
'>': 7,
'<=': 7,
'>=': 7,
'instanceof': 7,
'in': 7,
'<<': 8,
'>>': 8,
'>>>': 8,
'+': 9,
'-': 9,
'*': 11,
'/': 11,
'%': 11
}
class Token:
pass
class Syntax:
pass
class Messages:
pass
class PlaceHolders:
ArrowParameterPlaceHolder = 'ArrowParameterPlaceHolder'
for k, v in token.items():
setattr(Token, k, v)
for e in syntax:
setattr(Syntax, e, e)
for k, v in messages.items():
setattr(Messages, k, v)
#http://stackoverflow.com/questions/14245893/efficiently-list-all-characters-in-a-given-unicode-category
BOM = u'\uFEFF'
ZWJ = u'\u200D'
ZWNJ = u'\u200C'
TAB = u'\u0009'
VT = u'\u000B'
FF = u'\u000C'
SP = u'\u0020'
NBSP = u'\u00A0'
LF = u'\u000A'
CR = u'\u000D'
LS = u'\u2028'
PS = u'\u2029'
LETTER_CATEGORIES = set(['Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl'])
COMBINING_MARK_CATEGORIES = set(['Mn', 'Mc'])
DIGIT_CATEGORIES = set(['Nd'])
CONNECTOR_PUNCTUATION_CATEGORIES = set(['Pc'])
IDENTIFIER_START_CATEGORIES = LETTER_CATEGORIES.copy() # and some fucking unicode escape sequence
IDENTIFIER_PART_CATEGORIES = IDENTIFIER_START_CATEGORIES.union(COMBINING_MARK_CATEGORIES).union(DIGIT_CATEGORIES)\
.union(CONNECTOR_PUNCTUATION_CATEGORIES)
EXTRA_IDENTIFIER_START_CHARS = set(('$','_', '\\'))
EXTRA_IDENTIFIER_PART_CHARS = EXTRA_IDENTIFIER_START_CHARS.union(set((ZWJ, ZWNJ)))
WHITE_SPACE = set((0x20, 0x09, 0x0B, 0x0C, 0xA0, 0x1680, 0x180E, 0x2000,
0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF))
LINE_TERMINATORS = set((0x0A, 0x0D, 0x2028, 0x2029))
def isIdentifierStart(ch):
uch = (ch if isinstance(ch, unicode) else unichr(ch))
return unicodedata.category(uch) in IDENTIFIER_START_CATEGORIES or uch in EXTRA_IDENTIFIER_START_CHARS
def isIdentifierPart(ch):
uch = (ch if isinstance(ch, unicode) else unichr(ch))
return unicodedata.category(uch) in IDENTIFIER_PART_CATEGORIES or uch in EXTRA_IDENTIFIER_PART_CHARS
def isValidIdentifier(name):
if not name or isKeyword(name):
return False
check = isIdentifierStart
for e in name:
if not check(e):
return False
check = isIdentifierPart
return True
def isWhiteSpace(ch):
return (ord(ch) if isinstance(ch, unicode) else ch) in WHITE_SPACE
def isLineTerminator(ch):
return (ord(ch) if isinstance(ch, unicode) else ch) in LINE_TERMINATORS
OCTAL = set(('0', '1', '2', '3', '4', '5', '6', '7'))
DEC = set(('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'))
HEX = set('0123456789abcdefABCDEF')
HEX_CONV = dict(('0123456789abcdef' [n], n) for n in xrange(16))
for i, e in enumerate('ABCDEF', 10):
HEX_CONV[e] = i
def isDecimalDigit(ch):
return (ch if isinstance(ch, unicode) else unichr(ch)) in DEC
def isHexDigit(ch):
return (ch if isinstance(ch, unicode) else unichr(ch)) in HEX
def isOctalDigit(ch):
return (ch if isinstance(ch, unicode) else unichr(ch)) in OCTAL
def isFutureReservedWord(w):
return w in ('enum', 'export', 'import', 'super')
RESERVED_WORD = set(('implements', 'interface', 'package', 'private',
'protected', 'public', 'static', 'yield', 'let'))
def isStrictModeReservedWord(w):
return w in RESERVED_WORD
def isRestrictedWord(w):
return w in ('eval', 'arguments')
KEYWORDS = set(
('if', 'in', 'do', 'var', 'for', 'new', 'try', 'let', 'this', 'else',
'case', 'void', 'with', 'enum', 'while', 'break', 'catch', 'throw',
'const', 'yield', 'class', 'super', 'return', 'typeof', 'delete',
'switch', 'export', 'import', 'default', 'finally', 'extends', 'function',
'continue', 'debugger', 'instanceof', 'pyimport'))
def isKeyword(w):
# 'const' is specialized as Keyword in V8.
# 'yield' and 'let' are for compatibility with SpiderMonkey and ES.next.
# Some others are from future reserved words.
return w in KEYWORDS
class JsSyntaxError(Exception):
pass
if __name__ == '__main__':
assert isLineTerminator('\n')
assert isLineTerminator(0x0A)
assert isIdentifierStart('$')
assert isIdentifierStart(100)
assert isWhiteSpace(' ')
+371
View File
@@ -0,0 +1,371 @@
from .pyjsparserdata import *
class Ecma51NotSupported(Exception):
def __init__(self, feature):
super(Ecma51NotSupported,
self).__init__("%s is not supported by ECMA 5.1." % feature)
self.feature = feature
def get_feature(self):
return self.feature
class BaseNode:
def finish(self):
pass
def finishArrayExpression(self, elements):
self.type = Syntax.ArrayExpression
self.elements = elements
self.finish()
return self
def finishArrayPattern(self, elements):
self.type = Syntax.ArrayPattern
self.elements = elements
self.finish()
return self
def finishAssignmentExpression(self, operator, left, right):
self.type = Syntax.AssignmentExpression
self.operator = operator
self.left = left
self.right = right
self.finish()
return self
def finishAssignmentPattern(self, left, right):
self.type = Syntax.AssignmentPattern
self.left = left
self.right = right
self.finish()
return self
def finishBinaryExpression(self, operator, left, right):
self.type = Syntax.LogicalExpression if (
operator == '||' or operator == '&&') else Syntax.BinaryExpression
self.operator = operator
self.left = left
self.right = right
self.finish()
return self
def finishBlockStatement(self, body):
self.type = Syntax.BlockStatement
self.body = body
self.finish()
return self
def finishBreakStatement(self, label):
self.type = Syntax.BreakStatement
self.label = label
self.finish()
return self
def finishCallExpression(self, callee, args):
self.type = Syntax.CallExpression
self.callee = callee
self.arguments = args
self.finish()
return self
def finishCatchClause(self, param, body):
self.type = Syntax.CatchClause
self.param = param
self.body = body
self.finish()
return self
def finishConditionalExpression(self, test, consequent, alternate):
self.type = Syntax.ConditionalExpression
self.test = test
self.consequent = consequent
self.alternate = alternate
self.finish()
return self
def finishContinueStatement(self, label):
self.type = Syntax.ContinueStatement
self.label = label
self.finish()
return self
def finishDebuggerStatement(self, ):
self.type = Syntax.DebuggerStatement
self.finish()
return self
def finishDoWhileStatement(self, body, test):
self.type = Syntax.DoWhileStatement
self.body = body
self.test = test
self.finish()
return self
def finishEmptyStatement(self, ):
self.type = Syntax.EmptyStatement
self.finish()
return self
def finishExpressionStatement(self, expression):
self.type = Syntax.ExpressionStatement
self.expression = expression
self.finish()
return self
def finishForStatement(self, init, test, update, body):
self.type = Syntax.ForStatement
self.init = init
self.test = test
self.update = update
self.body = body
self.finish()
return self
def finishForInStatement(self, left, right, body):
self.type = Syntax.ForInStatement
self.left = left
self.right = right
self.body = body
self.each = False
self.finish()
return self
def finishFunctionDeclaration(self, id, params, defaults, body):
self.type = Syntax.FunctionDeclaration
self.id = id
self.params = params
self.defaults = defaults
self.body = body
self.generator = False
self.expression = False
self.finish()
return self
def finishFunctionExpression(self, id, params, defaults, body):
self.type = Syntax.FunctionExpression
self.id = id
self.params = params
self.defaults = defaults
self.body = body
self.generator = False
self.expression = False
self.finish()
return self
def finishIdentifier(self, name):
self.type = Syntax.Identifier
self.name = name
self.finish()
return self
def finishIfStatement(self, test, consequent, alternate):
self.type = Syntax.IfStatement
self.test = test
self.consequent = consequent
self.alternate = alternate
self.finish()
return self
def finishLabeledStatement(self, label, body):
self.type = Syntax.LabeledStatement
self.label = label
self.body = body
self.finish()
return self
def finishLiteral(self, token):
self.type = Syntax.Literal
self.value = token['value']
self.raw = token['raw']
if token.get('regex'):
self.regex = token['regex']
self.finish()
return self
def finishMemberExpression(self, accessor, object, property):
self.type = Syntax.MemberExpression
self.computed = accessor == '['
self.object = object
self.property = property
self.finish()
return self
def finishNewExpression(self, callee, args):
self.type = Syntax.NewExpression
self.callee = callee
self.arguments = args
self.finish()
return self
def finishObjectExpression(self, properties):
self.type = Syntax.ObjectExpression
self.properties = properties
self.finish()
return self
def finishObjectPattern(self, properties):
self.type = Syntax.ObjectPattern
self.properties = properties
self.finish()
return self
def finishPostfixExpression(self, operator, argument):
self.type = Syntax.UpdateExpression
self.operator = operator
self.argument = argument
self.prefix = False
self.finish()
return self
def finishProgram(self, body):
self.type = Syntax.Program
self.body = body
self.finish()
return self
def finishPyimport(self, imp):
self.type = 'PyimportStatement'
self.imp = imp
self.finish()
return self
def finishProperty(self, kind, key, computed, value, method, shorthand):
self.type = Syntax.Property
self.key = key
self.computed = computed
self.value = value
self.kind = kind
self.method = method
self.shorthand = shorthand
self.finish()
return self
def finishReturnStatement(self, argument):
self.type = Syntax.ReturnStatement
self.argument = argument
self.finish()
return self
def finishSequenceExpression(self, expressions):
self.type = Syntax.SequenceExpression
self.expressions = expressions
self.finish()
return self
def finishSwitchCase(self, test, consequent):
self.type = Syntax.SwitchCase
self.test = test
self.consequent = consequent
self.finish()
return self
def finishSwitchStatement(self, discriminant, cases):
self.type = Syntax.SwitchStatement
self.discriminant = discriminant
self.cases = cases
self.finish()
return self
def finishThisExpression(self, ):
self.type = Syntax.ThisExpression
self.finish()
return self
def finishThrowStatement(self, argument):
self.type = Syntax.ThrowStatement
self.argument = argument
self.finish()
return self
def finishTryStatement(self, block, handler, finalizer):
self.type = Syntax.TryStatement
self.block = block
self.guardedHandlers = []
self.handlers = [handler] if handler else []
self.handler = handler
self.finalizer = finalizer
self.finish()
return self
def finishUnaryExpression(self, operator, argument):
self.type = Syntax.UpdateExpression if (
operator == '++' or operator == '--') else Syntax.UnaryExpression
self.operator = operator
self.argument = argument
self.prefix = True
self.finish()
return self
def finishVariableDeclaration(self, declarations):
self.type = Syntax.VariableDeclaration
self.declarations = declarations
self.kind = 'var'
self.finish()
return self
def finishLexicalDeclaration(self, declarations, kind):
self.type = Syntax.VariableDeclaration
self.declarations = declarations
self.kind = kind
self.finish()
return self
def finishVariableDeclarator(self, id, init):
self.type = Syntax.VariableDeclarator
self.id = id
self.init = init
self.finish()
return self
def finishWhileStatement(self, test, body):
self.type = Syntax.WhileStatement
self.test = test
self.body = body
self.finish()
return self
def finishWithStatement(self, object, body):
self.type = Syntax.WithStatement
self.object = object
self.body = body
self.finish()
return self
def __getattr__(self, item):
if item in self.__dict__:
return self.__dict__[item]
if item.startswith('finish'):
feature = item[6:]
raise Ecma51NotSupported(feature)
else:
raise AttributeError(item)
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, key, value):
setattr(self, key, value)
def to_dict(self):
return node_to_dict(self)
class Node(BaseNode):
pass
class WrappingNode(BaseNode):
def __init__(self, startToken=None):
pass
def node_to_dict(node): # extremely important for translation speed
if isinstance(node, list):
return [node_to_dict(e) for e in node]
elif isinstance(node, dict):
return dict((k, node_to_dict(v)) for k, v in node.items())
elif not isinstance(node, BaseNode):
return node
return dict((k, node_to_dict(v)) for k, v in node.__dict__.items())