[jsinterp] Adding interpreter support for pre- and postfix expressions
This commit is contained in:
parent
0e4dd1ac77
commit
d7443e1233
@ -5,6 +5,7 @@ from . import (
|
||||
morespace,
|
||||
strange_chars,
|
||||
operators,
|
||||
unary,
|
||||
array_access,
|
||||
parens,
|
||||
assignments,
|
||||
@ -29,15 +30,15 @@ from . import (
|
||||
)
|
||||
|
||||
|
||||
modules = [basic, calc, empty_return, morespace, strange_chars, operators, array_access, parens, assignments, comments,
|
||||
precedence, call, getfield, branch, switch, for_loop, for_empty, for_in, do_loop, while_loop, label,
|
||||
func_expr, object_literal, try_statement, with_statement, debug, unshift]
|
||||
modules = [basic, calc, empty_return, morespace, strange_chars, operators, unary, array_access, parens, assignments,
|
||||
comments, precedence, call, getfield, branch, switch, for_loop, for_empty, for_in, do_loop, while_loop,
|
||||
label, func_expr, object_literal, try_statement, with_statement, debug, unshift]
|
||||
|
||||
|
||||
def gettestcases():
|
||||
for module in modules:
|
||||
if hasattr(module, 'tests'):
|
||||
case = {'name': module.__name__[len(__name__) + 1:], 'subtests': []}
|
||||
case = {'name': module.__name__[len(__name__) + 1:], 'subtests': [], 'skip': {}}
|
||||
for test in getattr(module, 'tests'):
|
||||
if 'code' in test:
|
||||
case['subtests'].append(test)
|
||||
|
@ -35,7 +35,7 @@ tests = [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
(Token.POSTFIX, _UNARY_OPERATORS['++'][1])
|
||||
]), None)
|
||||
])
|
||||
])),
|
||||
|
@ -30,7 +30,7 @@ tests = [
|
||||
]), None)]),
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'h'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
(Token.PREFIX, _UNARY_OPERATORS['++'][1])
|
||||
]), None)]),
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
|
@ -28,7 +28,7 @@ tests = [
|
||||
]), None)]),
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'h'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
(Token.PREFIX, _UNARY_OPERATORS['++'][1])
|
||||
]), None)]),
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
|
@ -39,16 +39,5 @@ tests = [
|
||||
(Token.OP, _OPERATORS['>>'][1])
|
||||
]), None)
|
||||
]))]
|
||||
}, {
|
||||
'code': 'return -5 + +3;',
|
||||
'asserts': [{'value': -2}]
|
||||
}, {
|
||||
'code': 'return -5 + ++a;',
|
||||
'globals': {'a': -3},
|
||||
'asserts': [{'value': -7}]
|
||||
}, {
|
||||
'code': 'function f() {return -5 + a++;}',
|
||||
'globals': {'a': -3},
|
||||
'asserts': [{'value': -8, 'call': ('f',)}, {'value': -7, 'call': ('f',)}]
|
||||
}
|
||||
]
|
||||
|
@ -42,7 +42,7 @@ tests = [
|
||||
[
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
(Token.POSTFIX, _UNARY_OPERATORS['++'][1])
|
||||
]), None)])
|
||||
]),
|
||||
((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
@ -50,7 +50,7 @@ tests = [
|
||||
[
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['--'][1])
|
||||
(Token.POSTFIX, _UNARY_OPERATORS['--'][1])
|
||||
]), None)]),
|
||||
(Token.BREAK, None)
|
||||
]),
|
||||
|
17
test/jstests/unary.py
Normal file
17
test/jstests/unary.py
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
skip = {'p': True}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'return -5 + +3;',
|
||||
'asserts': [{'value': -2}]
|
||||
}, {
|
||||
'code': 'function f() {return -5 + ++a;}',
|
||||
'globals': {'a': -3},
|
||||
'asserts': [{'value': -7, 'call': ('f',)}, {'value': -6, 'call': ('f',)}]
|
||||
}, {
|
||||
'code': 'function f() {return -5 + a++;}',
|
||||
'globals': {'a': -3},
|
||||
'asserts': [{'value': -8, 'call': ('f',)}, {'value': -7, 'call': ('f',)}]
|
||||
}
|
||||
]
|
@ -35,7 +35,7 @@ tests = [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
(Token.POSTFIX, _UNARY_OPERATORS['++'][1])
|
||||
]), None)
|
||||
])
|
||||
])),
|
||||
|
@ -33,7 +33,7 @@ def generator(test_case):
|
||||
else:
|
||||
self.assertEqual(jsi.run(), a['value'])
|
||||
|
||||
if 'skip' not in test_case or 'i' not in test_case['skip']:
|
||||
if 'i' not in test_case['skip']:
|
||||
reason = False
|
||||
else:
|
||||
reason = test_case['skip']['i']
|
||||
|
@ -44,7 +44,7 @@ def generator(test_case):
|
||||
if 'ast' in a:
|
||||
self.assertEqual(traverse(parsed), traverse(a['ast']))
|
||||
|
||||
if 'skip' not in test_case or 'p' not in test_case['skip']:
|
||||
if 'p' not in test_case['skip']:
|
||||
reason = False
|
||||
else:
|
||||
reason = test_case['skip']['p']
|
||||
@ -54,6 +54,7 @@ def generator(test_case):
|
||||
|
||||
# And add them to TestJSInterpreter
|
||||
for n, tc in enumerate(defs):
|
||||
if 'p' not in tc['skip'] or tc['skip']['p'] is not True:
|
||||
test_method = generator(tc)
|
||||
tname = 'test_' + str(tc['name'])
|
||||
i = 1
|
||||
|
@ -9,7 +9,7 @@ _token_keys = ('COPEN', 'CCLOSE', 'POPEN', 'PCLOSE', 'SOPEN', 'SCLOSE',
|
||||
'AND', 'OR', 'PLUS', 'NEG', 'INC', 'DEC', 'NOT', 'BNOT', 'DEL', 'VOID', 'TYPE',
|
||||
'LT', 'GT', 'LE', 'GE', 'EQ', 'NE', 'SEQ', 'SNE', 'IN', 'INSTANCEOF',
|
||||
'BOR', 'BXOR', 'BAND', 'RSHIFT', 'LSHIFT', 'URSHIFT', 'SUB', 'ADD', 'MOD', 'DIV', 'MUL',
|
||||
'OP', 'AOP', 'UOP', 'LOP', 'REL',
|
||||
'OP', 'AOP', 'UOP', 'LOP', 'REL', 'PREFIX', 'POSTFIX',
|
||||
'COMMENT', 'TOKEN', 'PUNCT',
|
||||
'NULL', 'BOOL', 'ID', 'STR', 'INT', 'FLOAT', 'REGEX', 'OBJECT',
|
||||
'REFLAGS', 'REBODY',
|
||||
|
@ -33,6 +33,7 @@ class Reference(object):
|
||||
if not hasattr(parent, '__setitem__'):
|
||||
raise ExtractorError('Unknown reference')
|
||||
parent.__setitem__(key, Reference(value, (parent, key)))
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
if self._parent is not None:
|
||||
@ -748,6 +749,8 @@ class JSInterpreter(object):
|
||||
if peek_id is Token.UOP:
|
||||
name, op = peek_value
|
||||
had_inc = name in (Token.INC, Token.DEC)
|
||||
if had_inc:
|
||||
peek_id = Token.PREFIX
|
||||
while stack and stack[-1][0] > 16:
|
||||
_, stack_id, stack_op = stack.pop()
|
||||
out.append((stack_id, stack_op))
|
||||
@ -770,6 +773,7 @@ class JSInterpreter(object):
|
||||
raise ExtractorError('''Can't have prefix and postfix operator at the same time at %d''' % peek_pos)
|
||||
name, op = peek_value
|
||||
if name in (Token.INC, Token.DEC):
|
||||
peek_id = Token.POSTFIX
|
||||
prec = 17
|
||||
else:
|
||||
raise ExtractorError('Unexpected operator at %d' % peek_pos)
|
||||
@ -880,6 +884,7 @@ class JSInterpreter(object):
|
||||
|
||||
elif name is Token.OPEXPR:
|
||||
stack = []
|
||||
postfix = []
|
||||
rpn = expr[1][:]
|
||||
# FIXME support pre- and postfix operators
|
||||
while rpn:
|
||||
@ -893,10 +898,17 @@ class JSInterpreter(object):
|
||||
elif token[0] is Token.UOP:
|
||||
right = stack.pop()
|
||||
stack.append(Reference(token[1](right.getvalue())))
|
||||
elif token[0] is Token.PREFIX:
|
||||
right = stack.pop()
|
||||
stack.append(Reference(right.putvalue(token[1](right.getvalue()))))
|
||||
elif token[0] is Token.POSTFIX:
|
||||
postfix.append((stack[-1], token[1]))
|
||||
else:
|
||||
stack.append(self.interpret_expression(token))
|
||||
result = stack.pop()
|
||||
if not stack:
|
||||
for operand, op in postfix:
|
||||
operand.putvalue(op(operand.getvalue()))
|
||||
ref = result
|
||||
else:
|
||||
raise ExtractorError('Expression has too many values')
|
||||
|
Loading…
x
Reference in New Issue
Block a user