[jsinterp] Fixing types and operators

- Adds `jsbuilt_ins.nan` and `jsbuilt_ins.infinity`
- Adds arithmetic operator overload to
  `jsbuilt_ins.jsnumber.JSNumberPrototype`
- Adds equality operator overload to `jsinterp.Reference`
- Adds better strict equality and typeof operator in `tstream`
This commit is contained in:
sulyi 2018-06-11 07:47:53 +02:00
parent a33b47e485
commit 93c0bb53a6
4 changed files with 92 additions and 16 deletions

View File

@ -10,6 +10,7 @@ from . import jsnumber
from .base import null, undefined from .base import null, undefined
from .jsboolean import false, true from .jsboolean import false, true
from .jsnumber import infinity, nan
def _eval(code): def _eval(code):

View File

@ -16,6 +16,34 @@ class JSNumberPrototype(JSObjectPrototype):
self.value = value self.value = value
self.own = {} self.own = {}
def __add__(self, other):
if isinstance(other, JSNumberPrototype):
other = other.value
return JSNumberPrototype(self.value + other)
def __sub__(self, other):
if isinstance(other, JSNumberPrototype):
other = other.value
return JSNumberPrototype(self.value - other)
def __mul__(self, other):
if isinstance(other, JSNumberPrototype):
other = other.value
return JSNumberPrototype(self.value * other)
def __div__(self, other):
if isinstance(other, JSNumberPrototype):
other = other.value
return JSNumberPrototype(self.value / other)
def __neg__(self):
return JSNumberPrototype(-self.value)
def __pos__(self):
return JSNumberPrototype(+self.value)
# __invert__?
@staticmethod @staticmethod
def _constructor(value=None): def _constructor(value=None):
return JSNumber.construct(value) return JSNumber.construct(value)
@ -53,6 +81,10 @@ class JSNumberPrototype(JSObjectPrototype):
} }
nan = JSNumberPrototype(float('nan'))
infinity = JSNumberPrototype(float('inf'))
class JSNumber(JSObject): class JSNumber(JSObject):
@staticmethod @staticmethod
def call(value=None): def call(value=None):
@ -68,7 +100,7 @@ class JSNumber(JSObject):
'prototype': JSNumberPrototype(), 'prototype': JSNumberPrototype(),
'MAX_VALUE': 1.7976931348623157 * 10 ** 308, 'MAX_VALUE': 1.7976931348623157 * 10 ** 308,
'MIN_VALUE': 5 * 10 ** (-324), 'MIN_VALUE': 5 * 10 ** (-324),
'NAN': float('nan'), 'NAN': nan,
'NEGATIVE_INFINITY': float('-inf'), 'NEGATIVE_INFINITY': infinity * -1,
'POSITIVE_INFINITY': float('inf'), 'POSITIVE_INFINITY': infinity,
} }

View File

@ -25,10 +25,13 @@ class Context(object):
class Reference(object): class Reference(object):
def __init__(self, value, parent=None): def __init__(self, value, parent_key=None):
super(Reference, self).__init__() super(Reference, self).__init__()
self._value = value self._value = value
self._parent = parent if parent_key is not None:
self._parent, self._name = parent_key
else:
self._parent = self._name = None
def getvalue(self, deep=False): def getvalue(self, deep=False):
value = self._value value = self._value
@ -46,10 +49,9 @@ class Reference(object):
def putvalue(self, value): def putvalue(self, value):
if self._parent is None: if self._parent is None:
raise ExtractorError('Trying to set a read-only reference') raise ExtractorError('Trying to set a read-only reference')
parent, key = self._parent if not hasattr(self._parent, '__setitem__'):
if not hasattr(parent, '__setitem__'):
raise ExtractorError('Unknown reference') raise ExtractorError('Unknown reference')
parent.__setitem__(key, Reference(value, (parent, key))) self._parent.__setitem__(self._name, Reference(value, (self._parent, self._name)))
self._value = value self._value = value
return value return value
@ -60,6 +62,14 @@ class Reference(object):
str(self._value), parent.__class__.__name__, id(parent), key) str(self._value), parent.__class__.__name__, id(parent), key)
return '<Reference value: %s, parent: %s>' % (self._value, None) return '<Reference value: %s, parent: %s>' % (self._value, None)
def __eq__(self, other):
if isinstance(other, Reference):
return self._parent is other._parent and self._name == other._name
return False
def __ne__(self, other):
return not self.__eq__(other)
class JSInterpreter(object): class JSInterpreter(object):
# TODO support json # TODO support json

View File

@ -16,6 +16,35 @@ from .jsgrammar import (
UNARY_OPERATORS_RE, UNARY_OPERATORS_RE,
TokenTypes TokenTypes
) )
from .jsbuilt_ins import false, true, nan
from .jsbuilt_ins.internals import jstype, undefined_type, null_type, number_type, boolean_type, string_type
def convert_to_unary(token_value):
return {TokenTypes.ADD: _UNARY_OPERATORS['+'], TokenTypes.SUB: _UNARY_OPERATORS['-']}[token_value[0]]
def strict_equal(x, y):
from .jsinterp import Reference
if jstype(x) != jstype(y):
return False
if jstype(x) in (undefined_type, null_type):
return True
if jstype(x) is number_type:
if x is nan or y is nan:
return False
if x.value == y.value:
return True
return False
if jstype(x):
return x.value == y.value
if jstype(x) is boolean_type:
return (x is true and y is true) or (x is false and y is false)
if isinstance(x, Reference):
return isinstance(y, Reference) and x == y
return False
_PUNCTUATIONS = { _PUNCTUATIONS = {
'{': TokenTypes.COPEN, '{': TokenTypes.COPEN,
@ -35,8 +64,8 @@ _LOGICAL_OPERATORS = {
'||': (TokenTypes.OR, lambda cur, right: cur or right) '||': (TokenTypes.OR, lambda cur, right: cur or right)
} }
_UNARY_OPERATORS = { _UNARY_OPERATORS = {
'+': (TokenTypes.PLUS, lambda cur: cur), '+': (TokenTypes.PLUS, operator.pos),
'-': (TokenTypes.NEG, lambda cur: cur * -1), '-': (TokenTypes.NEG, operator.neg),
'++': (TokenTypes.INC, lambda cur: cur + 1), '++': (TokenTypes.INC, lambda cur: cur + 1),
'--': (TokenTypes.DEC, lambda cur: cur - 1), '--': (TokenTypes.DEC, lambda cur: cur - 1),
'!': (TokenTypes.NOT, operator.not_), '!': (TokenTypes.NOT, operator.not_),
@ -44,7 +73,7 @@ _UNARY_OPERATORS = {
# XXX define these operators # XXX define these operators
'delete': (TokenTypes.DEL, None), 'delete': (TokenTypes.DEL, None),
'void': (TokenTypes.VOID, None), 'void': (TokenTypes.VOID, None),
'typeof': (TokenTypes.TYPE, lambda cur: type(cur)) 'typeof': (TokenTypes.TYPE, lambda cur: _type_strings[jstype(cur)])
} }
_RELATIONS = { _RELATIONS = {
'<': (TokenTypes.LT, operator.lt), '<': (TokenTypes.LT, operator.lt),
@ -54,8 +83,8 @@ _RELATIONS = {
# XXX check python and JavaScript equality difference # XXX check python and JavaScript equality difference
'==': (TokenTypes.EQ, operator.eq), '==': (TokenTypes.EQ, operator.eq),
'!=': (TokenTypes.NE, operator.ne), '!=': (TokenTypes.NE, operator.ne),
'===': (TokenTypes.SEQ, lambda cur, right: cur == right and type(cur) == type(right)), '===': (TokenTypes.SEQ, strict_equal),
'!==': (TokenTypes.SNE, lambda cur, right: not cur == right or not type(cur) == type(right)), '!==': (TokenTypes.SNE, lambda cur, right: not strict_equal(cur, right)),
'in': (TokenTypes.IN, operator.contains), 'in': (TokenTypes.IN, operator.contains),
'instanceof': (TokenTypes.INSTANCEOF, lambda cur, right: isinstance(cur, right)) 'instanceof': (TokenTypes.INSTANCEOF, lambda cur, right: isinstance(cur, right))
} }
@ -100,9 +129,13 @@ _input_element = re.compile(r'\s*(?:%(comment)s|%(token)s|%(lop)s|%(uop)s|%(aop)
_line_terminator = re.compile(LINETERMINATORSEQ_RE) _line_terminator = re.compile(LINETERMINATORSEQ_RE)
_type_strings = {
def convert_to_unary(token_value): undefined_type: 'undefinied',
return {TokenTypes.ADD: _UNARY_OPERATORS['+'], TokenTypes.SUB: _UNARY_OPERATORS['-']}[token_value[0]] null_type: 'null',
boolean_type: 'boolean',
number_type: 'number',
string_type: 'string'
}
class Token(object): class Token(object):