[jsinterp] Adding ast to for empty and for in parser test

This commit is contained in:
sulyi 2016-12-12 23:52:10 +01:00
parent b397ea2bdd
commit cd0bb42f4e
4 changed files with 66 additions and 15 deletions

View File

@ -835,24 +835,47 @@ class TestJSInterpreterParser(unittest.TestCase):
] ]
self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast)))
@unittest.skip('Test not yet implemented: missing ast')
def test_for_empty(self): def test_for_empty(self):
# ASAP for empty statement test
jsi = JSInterpreter(''' jsi = JSInterpreter('''
function f(x){ function f(x){
var h = 0 var h = 0;
for (; h <= x; ++h) { for (; h <= x; ++h) {
a = h; a = h;
} }
return a; return a;
} }
''') ''')
ast = [] ast = [
(Token.FUNC, 'f', ['x'],
(Token.BLOCK, [
(Token.VAR, zip(['h'], [
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None)
])),
(Token.FOR,
None,
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'h'), None, None),
(Token.MEMBER, (Token.ID, 'x'), None, None),
(Token.REL, _RELATIONS['<='][1])
]), None)]),
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'h'), None, None),
(Token.UOP, _UNARY_OPERATORS['++'][1])
]), None)]),
(Token.BLOCK, [
(Token.EXPR, [
(Token.ASSIGN, _ASSIGN_OPERATORS['='][1],
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]),
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None))
])
])),
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'a'), None, None)]), None)]))
]))
]
self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast)))
@unittest.skip('Test not yet implemented: missing ast')
def test_for_in(self): def test_for_in(self):
# ASAP for in statement test
jsi = JSInterpreter(''' jsi = JSInterpreter('''
function f(z){ function f(z){
for (h in z) { for (h in z) {
@ -861,7 +884,28 @@ class TestJSInterpreterParser(unittest.TestCase):
return a; return a;
} }
''') ''')
ast = [] ast = [
(Token.FUNC, 'f', ['z'],
(Token.BLOCK, [
(Token.FOR,
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'h'), None, None)
]), None)]),
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'z'), None, None)
]), None)]),
None,
(Token.BLOCK, [
(Token.EXPR, [
(Token.ASSIGN, _ASSIGN_OPERATORS['='][1],
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]),
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None))
])
])),
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'a'), None, None)]), None)]))
]))
]
self.assertEqual(list(jsi.statements()), ast) self.assertEqual(list(jsi.statements()), ast)
def test_do(self): def test_do(self):

View File

@ -30,9 +30,10 @@ __ESC_HEX_RE = r'x[0-9a-fA-F]{2}'
# NOTE order is fixed due to regex matching, does not represent any precedence # NOTE order is fixed due to regex matching, does not represent any precedence
# NOTE unary operator 'delete', 'void', 'instanceof' and relation 'in' and 'instanceof' do not handled this way
_logical_operator = ['||', '&&'] _logical_operator = ['||', '&&']
_relation = ['===', '!==', '==', '!=', '<=', '>=', '<', '>'] _relation = ['===', '!==', '==', '!=', '<=', '>=', '<', '>']
_unary_operator = ['++', '--', '!', '~', 'delete', 'void', 'typeof'] _unary_operator = ['++', '--', '!', '~']
_operator = ['|', '^', '&', '>>>', '>>', '<<', '-', '+', '%', '/', '*'] _operator = ['|', '^', '&', '>>>', '>>', '<<', '-', '+', '%', '/', '*']
_assign_operator = [op + '=' for op in _operator] _assign_operator = [op + '=' for op in _operator]
_assign_operator.append('=') _assign_operator.append('=')

View File

@ -102,7 +102,6 @@ class JSInterpreter(object):
elif token_id is Token.ID: elif token_id is Token.ID:
if token_value == 'var': if token_value == 'var':
# XXX refactor (create dedicated method for handling variable declaration list)
token_stream.pop() token_stream.pop()
variables = [] variables = []
init = [] init = []
@ -252,17 +251,18 @@ class JSInterpreter(object):
if token_id is Token.END: if token_id is Token.END:
init = None init = None
elif token_id is Token.ID and token_value == 'var': elif token_id is Token.ID and token_value == 'var':
# XXX change it on refactoring variable declaration list
init = self._statement(token_stream, stack_top - 1) init = self._statement(token_stream, stack_top - 1)
else: else:
init = self._expression(token_stream, stack_top - 1) init = self._expression(token_stream, stack_top - 1)
self._context.no_in = True self._context.no_in = True
token_id, token_value, token_pos = token_stream.pop() token_id, token_value, token_pos = token_stream.pop()
if token_id is Token.IN: if token_id is Token.ID and token_value == 'in':
cond = self._expression(token_stream, stack_top - 1) cond = self._expression(token_stream, stack_top - 1)
# FIXME further processing might be needed for interpretation # FIXME further processing of operator 'in' needed for interpretation
incr = None incr = None
# NOTE ES6 has of operator # NOTE ES6 has 'of' operator
elif token_id is Token.END: elif token_id is Token.END:
token_id, token_value, token_pos = token_stream.peek() token_id, token_value, token_pos = token_stream.peek()
cond = None if token_id is Token.END else self._expression(token_stream, stack_top - 1) cond = None if token_id is Token.END else self._expression(token_stream, stack_top - 1)
@ -881,6 +881,8 @@ class JSInterpreter(object):
rpn = expr[1][:] rpn = expr[1][:]
while rpn: while rpn:
token = rpn.pop(0) token = rpn.pop(0)
# XXX add unary operator 'delete', 'void', 'instanceof'
# XXX relation 'in' 'instanceof'
if token[0] in (Token.OP, Token.AOP, Token.UOP, Token.LOP, Token.REL): if token[0] in (Token.OP, Token.AOP, Token.UOP, Token.LOP, Token.REL):
right = stack.pop() right = stack.pop()
left = stack.pop() left = stack.pop()

View File

@ -48,12 +48,14 @@ _RELATIONS = {
'>': (Token.GT, operator.gt), '>': (Token.GT, operator.gt),
'<=': (Token.LE, operator.le), '<=': (Token.LE, operator.le),
'>=': (Token.GE, operator.ge), '>=': (Token.GE, operator.ge),
# XXX add instanceof and in operators
# XXX check python and JavaScript equality difference # XXX check python and JavaScript equality difference
'==': (Token.EQ, operator.eq), '==': (Token.EQ, operator.eq),
'!=': (Token.NE, operator.ne), '!=': (Token.NE, operator.ne),
'===': (Token.SEQ, lambda cur, right: cur == right and type(cur) == type(right)), '===': (Token.SEQ, lambda cur, right: cur == right and type(cur) == type(right)),
'!==': (Token.SNE, lambda cur, right: not cur == right or not type(cur) == type(right)) '!==': (Token.SNE, lambda cur, right: not cur == right or not type(cur) == type(right)),
# XXX define instanceof and in operators
'in': (Token.IN, None),
'instanceof': (Token.INSTANCEOF, None)
} }
_OPERATORS = { _OPERATORS = {
'|': (Token.BOR, operator.or_), '|': (Token.BOR, operator.or_),
@ -132,7 +134,9 @@ class TokenStream(object):
elif token_id is Token.ID: elif token_id is Token.ID:
yield (token_id, token_value, pos) yield (token_id, token_value, pos)
elif token_id in _operator_lookup: elif token_id in _operator_lookup:
yield (token_id, _operator_lookup[token_id][token_value], pos) yield (token_id if token_value != 'in' else Token.IN,
_operator_lookup[token_id][token_value],
pos)
elif token_id is Token.PUNCT: elif token_id is Token.PUNCT:
yield (_PUNCTUATIONS[token_value], token_value, pos) yield (_PUNCTUATIONS[token_value], token_value, pos)
else: else: