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