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

View File

@@ -5,6 +5,7 @@ import re
from .translators.friendly_nodes import REGEXP_CONVERTER
from .utils.injector import fix_js_args
from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType
from math import floor, log10
import traceback
try:
import numpy
@@ -603,15 +604,7 @@ class PyJs(object):
elif typ == 'Boolean':
return Js('true') if self.value else Js('false')
elif typ == 'Number': #or self.Class=='Number':
if self.is_nan():
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
return Js(unicode(js_dtoa(self.value)))
elif typ == 'String':
return self
else: #object
@@ -1046,7 +1039,7 @@ def PyJsComma(a, b):
return b
from .internals.simplex import JsException as PyJsException
from .internals.simplex import JsException as PyJsException, js_dtoa
import pyjsparser
pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg)

View File

@@ -116,36 +116,52 @@ def eval_js(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.
>>> js = EvalJs()
>>> js.execute('var a = 10;function f(x) {return x*x};')
>>> js.f(9)
>>> ctx = EvalJs()
>>> ctx.execute('var a = 10;function f(x) {return x*x};')
>>> ctx.f(9)
81
>>> js.a
>>> ctx.a
10
context is a python dict or object that contains python variables that should be available to JavaScript
For example:
>>> js = EvalJs({'a': 30})
>>> js.execute('var x = a')
>>> js.x
>>> 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={}):
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__

View File

@@ -7,7 +7,7 @@ from ..byte_trans import ByteCodeGenerator, Code
def Function(this, args):
# convert arguments to python list of strings
a = map(to_string, tuple(args))
a = list(map(to_string, tuple(args)))
_body = u';'
_args = ()
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('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!
@@ -71,5 +72,5 @@ def _eval(this, args):
def log(this, args):
print ' '.join(map(to_string, args))
print(' '.join(map(to_string, args)))
return undefined

View File

@@ -4,7 +4,7 @@ from __future__ import unicode_literals
import re
from ..conversions import *
from ..func_utils import *
from jsregexp import RegExpExec
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"

View File

@@ -1,6 +1,6 @@
__all__ = ['require']
import subprocess, os, codecs, glob
from .evaljs import translate_js
from .evaljs import translate_js, DEFAULT_HEADER
import six
DID_INIT = False
DIRNAME = os.path.dirname(os.path.abspath(__file__))
@@ -15,7 +15,7 @@ def _init():
'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'
'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'
@@ -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!'
py_name = module_name.replace('-', '_')
py_name = _get_module_py_name(module_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,
module_filename)) or update:
_init()
@@ -77,7 +83,7 @@ def require(module_name, include_polyfill=False, update=False):
# convert the module
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),
shell=True,
cwd=DIRNAME,
@@ -88,7 +94,8 @@ def require(module_name, include_polyfill=False, update=False):
"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)
@@ -107,7 +114,32 @@ def require(module_name, include_polyfill=False, update=False):
os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
"utf-8") as f:
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)
return context['var'][var_name].to_py()
return context['var'][_get_module_var_name(module_name)].to_py()

View File

@@ -6,8 +6,6 @@ if six.PY3:
xrange = range
unicode = str
# todo fix apply and bind
class FunctionPrototype:
def toString():
@@ -41,6 +39,7 @@ class FunctionPrototype:
return this.call(obj, args)
def bind(thisArg):
arguments_ = arguments
target = this
if not target.is_callable():
raise this.MakeError(
@@ -48,5 +47,5 @@ class FunctionPrototype:
if len(arguments) <= 1:
args = ()
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)

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)

View File

@@ -155,7 +155,7 @@ def limited(func):
inf = float('inf')
def Literal(type, value, raw, regex=None, comments=None):
def Literal(type, value, raw, regex=None):
if regex: # regex
return 'JsRegExp(%s)' % repr(compose_regex(value))
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"))'
def Identifier(type, name, comments=None):
def Identifier(type, name):
return 'var.get(%s)' % repr(name)
@limited
def MemberExpression(type, computed, object, property, comments=None):
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
@@ -183,12 +183,12 @@ def MemberExpression(type, computed, object, property, comments=None):
return far_left + '.get(%s)' % prop
def ThisExpression(type, comments=None):
def ThisExpression(type):
return 'var.get(u"this")'
@limited
def CallExpression(type, callee, arguments, comments=None):
def CallExpression(type, callee, arguments):
arguments = [trans(e) for e in arguments]
if callee['type'] == 'MemberExpression':
far_left = trans(callee['object'])
@@ -210,38 +210,47 @@ def CallExpression(type, callee, arguments, comments=None):
# ========== 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)
# ========== OBJECTS =============
def ObjectExpression(type, properties, comments=None):
name = inline_stack.require('Object')
def ObjectExpression(type, properties):
name = None
elems = []
after = ''
for p in properties:
if p['kind'] == 'init':
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:
raise RuntimeError('Unexpected object propery kind')
obj = '%s = Js({%s})\n' % (name, ','.join(elems))
inline_stack.define(name, obj + after)
return name
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, comments=None):
def Property(type, kind, key, computed, value, method, shorthand):
if shorthand or computed:
raise NotImplementedError(
'Shorthand and Computed properties not implemented!')
@@ -256,7 +265,7 @@ def Property(type, kind, key, computed, value, method, shorthand, comments=None)
@limited
def UnaryExpression(type, operator, argument, prefix, comments=None):
def UnaryExpression(type, operator, argument, prefix):
a = trans(
argument, standard=True
) # 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
def BinaryExpression(type, operator, left, right, comments=None):
def BinaryExpression(type, operator, left, right):
a = trans(left)
b = trans(right)
# delegate to our friends
@@ -279,7 +288,7 @@ def BinaryExpression(type, operator, left, right, comments=None):
@limited
def UpdateExpression(type, operator, argument, prefix, comments=None):
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
@@ -287,7 +296,7 @@ def UpdateExpression(type, operator, argument, prefix, comments=None):
@limited
def AssignmentExpression(type, operator, left, right, comments=None):
def AssignmentExpression(type, operator, left, right):
operator = operator[:-1]
if left['type'] == 'Identifier':
if operator:
@@ -319,12 +328,12 @@ six
@limited
def SequenceExpression(type, expressions, comments=None):
def SequenceExpression(type, expressions):
return reduce(js_comma, (trans(e) for e in expressions))
@limited
def NewExpression(type, callee, arguments, comments=None):
def NewExpression(type, callee, arguments):
return trans(callee) + '.create(%s)' % ', '.join(
trans(e) for e in arguments)
@@ -332,7 +341,7 @@ def NewExpression(type, callee, arguments, comments=None):
@limited
def ConditionalExpression(
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),
trans(alternate))
@@ -340,49 +349,49 @@ def ConditionalExpression(
# =========== STATEMENTS =============
def BlockStatement(type, body, comments=None):
def BlockStatement(type, body):
return StatementList(
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
def BreakStatement(type, label, comments=None):
def BreakStatement(type, label):
if label:
return 'raise %s("Breaked")\n' % (get_break_label(label['name']))
else:
return 'break\n'
def ContinueStatement(type, label, comments=None):
def ContinueStatement(type, label):
if label:
return 'raise %s("Continued")\n' % (get_continue_label(label['name']))
else:
return 'continue\n'
def ReturnStatement(type, argument, comments=None):
def ReturnStatement(type, argument):
return 'return %s\n' % (trans(argument)
if argument else "var.get('undefined')")
def EmptyStatement(type, comments=None):
def EmptyStatement(type):
return 'pass\n'
def DebuggerStatement(type, comments=None):
def DebuggerStatement(type):
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')
result = 'while 1:\n' + indent(inside)
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 ''
init = trans(init) if init else ''
if not init.endswith('\n'):
@@ -398,7 +407,7 @@ def ForStatement(type, init, test, update, body, comments=None):
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)
if left['type'] == "VariableDeclaration":
addon = trans(left) # make sure variable is registered
@@ -417,7 +426,7 @@ def ForInStatement(type, left, right, body, each, comments=None):
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!
IF = 'if %s:\n' % trans(test)
IF += indent(trans(consequent))
@@ -427,7 +436,7 @@ def IfStatement(type, test, consequent, alternate, comments=None):
return IF + ELSE
def LabeledStatement(type, label, body, comments=None):
def LabeledStatement(type, label, body):
# todo consider using smarter approach!
inside = trans(body)
defs = ''
@@ -448,7 +457,7 @@ def LabeledStatement(type, label, body, comments=None):
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!
code = ''.join(trans(e) for e in lis)
return code if code else 'pass\n'
@@ -456,7 +465,7 @@ def StatementList(lis, comments=None):
return 'pass\n'
def PyimportStatement(type, imp, comments=None):
def PyimportStatement(type, imp):
lib = imp['name']
jlib = 'PyImport_%s' % lib
code = 'import %s as %s\n' % (lib, jlib)
@@ -471,7 +480,7 @@ def PyimportStatement(type, imp, comments=None):
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
code = 'while 1:\n' + indent('SWITCHED = False\nCONDITION = (%s)\n')
code = code % trans(discriminant)
@@ -491,12 +500,12 @@ def SwitchStatement(type, discriminant, cases, comments=None):
return code
def ThrowStatement(type, argument, comments=None):
def ThrowStatement(type, argument):
return 'PyJsTempException = JsToPyException(%s)\nraise PyJsTempException\n' % trans(
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))
# complicated catch statement...
if handler:
@@ -516,13 +525,13 @@ def TryStatement(type, block, handler, handlers, guardedHandlers, finalizer, com
return result
def LexicalDeclaration(type, declarations, kind, comments=None):
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, comments=None):
def VariableDeclarator(type, id, init):
name = id['name']
# register the name if not already registered
Context.register(name)
@@ -531,21 +540,21 @@ def VariableDeclarator(type, id, init, comments=None):
return ''
def VariableDeclaration(type, declarations, kind, comments=None):
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, comments=None):
def WhileStatement(type, test, body):
result = 'while %s:\n' % trans(test) + indent(trans(body))
return result
def WithStatement(type, object, body, comments=None):
def WithStatement(type, object, body):
raise NotImplementedError('With statement not implemented!')
def Program(type, body, comments=None):
def Program(type, body):
inline_stack.reset()
code = ''.join(trans(e) for e in body)
# 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,
expression, comments=None):
expression):
if generator:
raise NotImplementedError('Generators not supported')
if defaults:
@@ -610,7 +619,7 @@ def FunctionDeclaration(type, id, params, defaults, body, generator,
def FunctionExpression(type, id, params, defaults, body, generator,
expression, comments=None):
expression):
if generator:
raise NotImplementedError('Generators not supported')
if defaults:

View File

@@ -115,7 +115,16 @@ def append_arguments(code_obj, new_locals):
code_obj.co_freevars, code_obj.co_cellvars)
# 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):

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

3041
lib/pyjsparser/parser.py Normal file

File diff suppressed because it is too large Load Diff

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
lib/pyjsparser/std_nodes.py Normal file
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())