[jsinterp] Implementing String split
This commit is contained in:
parent
327bb2dd86
commit
f9f030a005
@ -39,8 +39,6 @@ tests = [
|
|||||||
])
|
])
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
# FIXME built-in functions are not yet implemented
|
|
||||||
'exclude': ('jsinterp2',),
|
|
||||||
'code': 'function x(a) { return a.split(""); }',
|
'code': 'function x(a) { return a.split(""); }',
|
||||||
'asserts': [{'value': ["a", "b", "c"], 'call': ('x', "abc")}],
|
'asserts': [{'value': ["a", "b", "c"], 'call': ('x', "abc")}],
|
||||||
'ast': [
|
'ast': [
|
||||||
|
@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
skip = {
|
skip = {
|
||||||
'jsinterp': 'String literals are not supported',
|
'jsinterp': 'String literals are not supported',
|
||||||
'interpret': 'Builtins are not yet implemented',
|
|
||||||
'parse': 'Test is not yet implemented: missing ast'
|
'parse': 'Test is not yet implemented: missing ast'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ __doc__ = """see: `js2tests`"""
|
|||||||
defs = gettestcases()
|
defs = gettestcases()
|
||||||
# set level to logging.DEBUG to see messages about missing assertions
|
# set level to logging.DEBUG to see messages about missing assertions
|
||||||
# set level to logging.DEBUG to see messages about code tests are running
|
# set level to logging.DEBUG to see messages about code tests are running
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||||
log = logging.getLogger('TestJSInterpreter')
|
log = logging.getLogger('TestJSInterpreter')
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ __doc__ = """see: `js2tests`"""
|
|||||||
defs = gettestcases()
|
defs = gettestcases()
|
||||||
# set level to logging.DEBUG to see messages about missing assertions
|
# set level to logging.DEBUG to see messages about missing assertions
|
||||||
# set level to logging.DEBUG to see messages about code tests are running
|
# set level to logging.DEBUG to see messages about code tests are running
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||||
log = logging.getLogger('TestJSInterpreter2')
|
log = logging.getLogger('TestJSInterpreter2')
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,5 +53,9 @@ global_obj = jsobject.JSObject.construct(
|
|||||||
'Array': jsarray.JSArray(),
|
'Array': jsarray.JSArray(),
|
||||||
'Function': jsfunction.JSFunction(),
|
'Function': jsfunction.JSFunction(),
|
||||||
'String': jsstring.JSString(),
|
'String': jsstring.JSString(),
|
||||||
'Number': jsnumber.JSNumber()
|
'Number': jsnumber.JSNumber(),
|
||||||
|
'false': jsboolean.false,
|
||||||
|
'true': jsboolean.true,
|
||||||
|
'null': base.null,
|
||||||
|
'undefined': base.undefined
|
||||||
})
|
})
|
||||||
|
@ -24,10 +24,10 @@ class JSProtoBase(JSBase):
|
|||||||
super(JSProtoBase, self).__init__('')
|
super(JSProtoBase, self).__init__('')
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
while cls.__base__ is not JSProtoBase:
|
while cls.__base__ is not JSProtoBase:
|
||||||
cls = cls.__base__
|
|
||||||
props = cls.own.copy()
|
props = cls.own.copy()
|
||||||
props.update(self.props)
|
props.update(self.props)
|
||||||
self.props = props
|
self.props = props
|
||||||
|
cls = cls.__base__
|
||||||
self.value = {}
|
self.value = {}
|
||||||
|
|
||||||
def get_prop(self, prop):
|
def get_prop(self, prop):
|
||||||
@ -63,3 +63,8 @@ native_number = (int, float)
|
|||||||
native_object = dict
|
native_object = dict
|
||||||
native_array = (list, tuple)
|
native_array = (list, tuple)
|
||||||
native_function = FunctionType
|
native_function = FunctionType
|
||||||
|
|
||||||
|
|
||||||
|
def isprimitive(value):
|
||||||
|
return (isinstance(value, (native_bool, native_string, native_number, native_object, native_array, native_function))
|
||||||
|
or value is None)
|
||||||
|
@ -60,7 +60,9 @@ class JSStringPrototype(JSObjectPrototype):
|
|||||||
return 'string slice'
|
return 'string slice'
|
||||||
|
|
||||||
def _split(self, sep):
|
def _split(self, sep):
|
||||||
return 'string split'
|
if sep == '':
|
||||||
|
return list(self.value)
|
||||||
|
return self.value.split(sep)
|
||||||
|
|
||||||
def _substring(self, start, end):
|
def _substring(self, start, end):
|
||||||
return 'string substring'
|
return 'string substring'
|
||||||
|
@ -6,6 +6,10 @@ from ..compat import compat_str
|
|||||||
from ..utils import ExtractorError
|
from ..utils import ExtractorError
|
||||||
from .jsparser import Parser
|
from .jsparser import Parser
|
||||||
from .jsgrammar import Token, token_keys
|
from .jsgrammar import Token, token_keys
|
||||||
|
from .jsbuilt_ins import global_obj
|
||||||
|
from .jsbuilt_ins.base import isprimitive
|
||||||
|
from .jsbuilt_ins.internals import to_string
|
||||||
|
from .jsbuilt_ins.utils import to_js
|
||||||
|
|
||||||
|
|
||||||
class Context(object):
|
class Context(object):
|
||||||
@ -31,7 +35,7 @@ class Reference(object):
|
|||||||
if deep:
|
if deep:
|
||||||
if isinstance(self._value, (list, tuple)):
|
if isinstance(self._value, (list, tuple)):
|
||||||
# TODO test nested arrays
|
# TODO test nested arrays
|
||||||
value = [elem.getvalue() for elem in self._value]
|
value = [elem if isprimitive(elem) else elem.getvalue() for elem in self._value]
|
||||||
elif isinstance(self._value, dict):
|
elif isinstance(self._value, dict):
|
||||||
value = {}
|
value = {}
|
||||||
for key, prop in self._value.items():
|
for key, prop in self._value.items():
|
||||||
@ -60,8 +64,6 @@ class Reference(object):
|
|||||||
class JSInterpreter(object):
|
class JSInterpreter(object):
|
||||||
# TODO support json
|
# TODO support json
|
||||||
|
|
||||||
undefined = object()
|
|
||||||
|
|
||||||
def __init__(self, code, variables=None):
|
def __init__(self, code, variables=None):
|
||||||
super(JSInterpreter, self).__init__()
|
super(JSInterpreter, self).__init__()
|
||||||
self.code = code
|
self.code = code
|
||||||
@ -116,7 +118,8 @@ class JSInterpreter(object):
|
|||||||
ref = s.getvalue()
|
ref = s.getvalue()
|
||||||
elif name is Token.VAR:
|
elif name is Token.VAR:
|
||||||
for name, value in stmt[1]:
|
for name, value in stmt[1]:
|
||||||
value = self.interpret_expression(value).getvalue() if value is not None else self.undefined
|
value = (self.interpret_expression(value).getvalue() if value is not None else
|
||||||
|
global_obj.get_prop('undefined'))
|
||||||
self.this[name] = Reference(value, (self.this, name))
|
self.this[name] = Reference(value, (self.this, name))
|
||||||
elif name is Token.EXPR:
|
elif name is Token.EXPR:
|
||||||
for expr in stmt[1]:
|
for expr in stmt[1]:
|
||||||
@ -158,7 +161,7 @@ class JSInterpreter(object):
|
|||||||
if lid[0] is Token.ID and args is None and tail is None:
|
if lid[0] is Token.ID and args is None and tail is None:
|
||||||
key = lid[1]
|
key = lid[1]
|
||||||
if key is not None:
|
if key is not None:
|
||||||
u = Reference(self.undefined, (self.this, key))
|
u = Reference(global_obj.get_prop('undefined'), (self.this, key))
|
||||||
leftref = self.this[key] = u
|
leftref = self.this[key] = u
|
||||||
else:
|
else:
|
||||||
raise ExtractorError('Invalid left-hand side in assignment')
|
raise ExtractorError('Invalid left-hand side in assignment')
|
||||||
@ -209,16 +212,31 @@ class JSInterpreter(object):
|
|||||||
if args is not None:
|
if args is not None:
|
||||||
# TODO interpret NewExpression
|
# TODO interpret NewExpression
|
||||||
pass
|
pass
|
||||||
|
source = None
|
||||||
while tail is not None:
|
while tail is not None:
|
||||||
tail_name, tail_value, tail = tail
|
tail_name, tail_value, tail = tail
|
||||||
if tail_name is Token.FIELD:
|
if tail_name is Token.FIELD:
|
||||||
target = target.getvalue()[tail_value]
|
source = to_js(target.getvalue())
|
||||||
|
target = source.get_prop(tail_value)
|
||||||
elif tail_name is Token.ELEM:
|
elif tail_name is Token.ELEM:
|
||||||
index = self.interpret_expression(tail_value).getvalue()
|
prop = self.interpret_expression(tail_value).getvalue()
|
||||||
target = target.getvalue()[index]
|
target = to_js(target.getvalue()).get_prop(to_string(to_js(prop)))
|
||||||
elif tail_name is Token.CALL:
|
elif tail_name is Token.CALL:
|
||||||
args = (self.interpret_expression(arg).getvalue() for arg in tail_value)
|
args = (self.interpret_expression(arg).getvalue() for arg in tail_value)
|
||||||
target = Reference(target.getvalue()(*args))
|
if isprimitive(target):
|
||||||
|
if source is None:
|
||||||
|
target = target(*args)
|
||||||
|
else:
|
||||||
|
target = target(source, *args)
|
||||||
|
else:
|
||||||
|
if source is None:
|
||||||
|
target = target.getvalue()(*args)
|
||||||
|
else:
|
||||||
|
target = target.getvalue()(source, *args)
|
||||||
|
if isprimitive(target):
|
||||||
|
target = Reference(target)
|
||||||
|
else:
|
||||||
|
target = Reference(target.getvalue())
|
||||||
ref = target
|
ref = target
|
||||||
|
|
||||||
elif name is Token.ID:
|
elif name is Token.ID:
|
||||||
|
@ -440,7 +440,7 @@ class Parser(object):
|
|||||||
# Rhino has check for args length
|
# Rhino has check for args length
|
||||||
# Rhino has experimental syntax allowing an object literal to follow a new expression
|
# Rhino has experimental syntax allowing an object literal to follow a new expression
|
||||||
else:
|
else:
|
||||||
target = self._primary_expression(stack_top)
|
target = self._primary_expression(stack_top - 1)
|
||||||
args = None
|
args = None
|
||||||
|
|
||||||
return (Token.MEMBER, target, args, self._member_tail(stack_top - 1))
|
return (Token.MEMBER, target, args, self._member_tail(stack_top - 1))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user