|
|
@ -1,5 +1,6 @@
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from functools import update_wrapper
|
|
|
|
import itertools
|
|
|
|
import itertools
|
|
|
|
import json
|
|
|
|
import json
|
|
|
|
import math
|
|
|
|
import math
|
|
|
@ -23,11 +24,23 @@ from .compat import (
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def wraps_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_and_rename_wrapper(w):
|
|
|
|
|
|
|
|
f = update_wrapper(w, op)
|
|
|
|
|
|
|
|
# fn names are str in both Py 2/3
|
|
|
|
|
|
|
|
f.__name__ = str('JS_') + f.__name__
|
|
|
|
|
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return update_and_rename_wrapper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _js_bit_op(op):
|
|
|
|
def _js_bit_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
def zeroise(x):
|
|
|
|
def zeroise(x):
|
|
|
|
return 0 if x in (None, JS_Undefined) else x
|
|
|
|
return 0 if x in (None, JS_Undefined) else x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@wraps_op(op)
|
|
|
|
def wrapped(a, b):
|
|
|
|
def wrapped(a, b):
|
|
|
|
return op(zeroise(a), zeroise(b)) & 0xffffffff
|
|
|
|
return op(zeroise(a), zeroise(b)) & 0xffffffff
|
|
|
|
|
|
|
|
|
|
|
@ -36,6 +49,7 @@ def _js_bit_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
def _js_arith_op(op):
|
|
|
|
def _js_arith_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@wraps_op(op)
|
|
|
|
def wrapped(a, b):
|
|
|
|
def wrapped(a, b):
|
|
|
|
if JS_Undefined in (a, b):
|
|
|
|
if JS_Undefined in (a, b):
|
|
|
|
return float('nan')
|
|
|
|
return float('nan')
|
|
|
@ -66,6 +80,7 @@ def _js_exp(a, b):
|
|
|
|
|
|
|
|
|
|
|
|
def _js_eq_op(op):
|
|
|
|
def _js_eq_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@wraps_op(op)
|
|
|
|
def wrapped(a, b):
|
|
|
|
def wrapped(a, b):
|
|
|
|
if set((a, b)) <= set((None, JS_Undefined)):
|
|
|
|
if set((a, b)) <= set((None, JS_Undefined)):
|
|
|
|
return op(a, a)
|
|
|
|
return op(a, a)
|
|
|
@ -76,6 +91,7 @@ def _js_eq_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
def _js_comp_op(op):
|
|
|
|
def _js_comp_op(op):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@wraps_op(op)
|
|
|
|
def wrapped(a, b):
|
|
|
|
def wrapped(a, b):
|
|
|
|
if JS_Undefined in (a, b):
|
|
|
|
if JS_Undefined in (a, b):
|
|
|
|
return False
|
|
|
|
return False
|
|
|
@ -356,6 +372,7 @@ class JSInterpreter(object):
|
|
|
|
return right_val
|
|
|
|
return right_val
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|
|
|
|
# print('Eval:', opfunc.__name__, left_val, right_val)
|
|
|
|
return opfunc(left_val, right_val)
|
|
|
|
return opfunc(left_val, right_val)
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
raise self.Exception('Failed to evaluate {left_val!r:.50} {op} {right_val!r:.50}'.format(**locals()), expr, cause=e)
|
|
|
|
raise self.Exception('Failed to evaluate {left_val!r:.50} {op} {right_val!r:.50}'.format(**locals()), expr, cause=e)
|
|
|
@ -395,6 +412,7 @@ class JSInterpreter(object):
|
|
|
|
raise self.Exception('Recursion limit reached')
|
|
|
|
raise self.Exception('Recursion limit reached')
|
|
|
|
allow_recursion -= 1
|
|
|
|
allow_recursion -= 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# print('At: ' + stmt[:60])
|
|
|
|
should_return = False
|
|
|
|
should_return = False
|
|
|
|
# fails on (eg) if (...) stmt1; else stmt2;
|
|
|
|
# fails on (eg) if (...) stmt1; else stmt2;
|
|
|
|
sub_statements = list(self._separate(stmt, ';')) or ['']
|
|
|
|
sub_statements = list(self._separate(stmt, ';')) or ['']
|
|
|
@ -702,9 +720,24 @@ class JSInterpreter(object):
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
right_expr = separated.pop()
|
|
|
|
right_expr = separated.pop()
|
|
|
|
while op == '-' and len(separated) > 1 and not separated[-1].strip():
|
|
|
|
# handle operators that are both unary and binary, minimal BODMAS
|
|
|
|
right_expr = '-' + right_expr
|
|
|
|
if op in ('+', '-'):
|
|
|
|
|
|
|
|
undone = 0
|
|
|
|
|
|
|
|
while len(separated) > 1 and not separated[-1].strip():
|
|
|
|
|
|
|
|
undone += 1
|
|
|
|
separated.pop()
|
|
|
|
separated.pop()
|
|
|
|
|
|
|
|
if op == '-' and undone % 2 != 0:
|
|
|
|
|
|
|
|
right_expr = op + right_expr
|
|
|
|
|
|
|
|
left_val = separated[-1]
|
|
|
|
|
|
|
|
for dm_op in ('*', '%', '/', '**'):
|
|
|
|
|
|
|
|
bodmas = tuple(self._separate(left_val, dm_op, skip_delims=skip_delim))
|
|
|
|
|
|
|
|
if len(bodmas) > 1 and not bodmas[-1].strip():
|
|
|
|
|
|
|
|
expr = op.join(separated) + op + right_expr
|
|
|
|
|
|
|
|
right_expr = None
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
if right_expr is None:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
left_val = self.interpret_expression(op.join(separated), local_vars, allow_recursion)
|
|
|
|
left_val = self.interpret_expression(op.join(separated), local_vars, allow_recursion)
|
|
|
|
return self._operator(op, left_val, right_expr, expr, local_vars, allow_recursion), should_return
|
|
|
|
return self._operator(op, left_val, right_expr, expr, local_vars, allow_recursion), should_return
|
|
|
|
|
|
|
|
|
|
|
@ -955,6 +988,7 @@ class JSInterpreter(object):
|
|
|
|
def build_function(self, argnames, code, *global_stack):
|
|
|
|
def build_function(self, argnames, code, *global_stack):
|
|
|
|
global_stack = list(global_stack) or [{}]
|
|
|
|
global_stack = list(global_stack) or [{}]
|
|
|
|
argnames = tuple(argnames)
|
|
|
|
argnames = tuple(argnames)
|
|
|
|
|
|
|
|
# import pdb; pdb.set_trace()
|
|
|
|
|
|
|
|
|
|
|
|
def resf(args, kwargs={}, allow_recursion=100):
|
|
|
|
def resf(args, kwargs={}, allow_recursion=100):
|
|
|
|
global_stack[0].update(
|
|
|
|
global_stack[0].update(
|
|
|
|