[test] Adding jstests test suite
This commit is contained in:
parent
cd0bb42f4e
commit
ab37e2b811
46
test/jstests/__init__.py
Normal file
46
test/jstests/__init__.py
Normal file
@ -0,0 +1,46 @@
|
||||
from . import (
|
||||
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, 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': []}
|
||||
for test in getattr(module, 'tests'):
|
||||
if 'code' in test:
|
||||
case['subtests'].append(test)
|
||||
if hasattr(module, 'skip'):
|
||||
case['skip'] = getattr(module, 'skip')
|
||||
yield case
|
76
test/jstests/array_access.py
Normal file
76
test/jstests/array_access.py
Normal file
@ -0,0 +1,76 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS
|
||||
|
||||
tests = [
|
||||
{'code': 'var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;',
|
||||
'asserts': [{'value': [5, 2, 7]}],
|
||||
'ast': [(Token.VAR,
|
||||
zip(['x'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ARRAY, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 2), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 3), None, None)]), None)
|
||||
]), None, None),
|
||||
]),
|
||||
None)
|
||||
])
|
||||
),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'),
|
||||
None,
|
||||
(Token.ELEM,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]),
|
||||
None)
|
||||
]),
|
||||
None))
|
||||
]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 4), None, None)]), None)
|
||||
)
|
||||
]),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'),
|
||||
None,
|
||||
(Token.ELEM, (Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]),
|
||||
None)
|
||||
]), None))
|
||||
]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 5), None, None)]), None))
|
||||
]),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'),
|
||||
None,
|
||||
(Token.ELEM, (Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]),
|
||||
None)
|
||||
]), None))
|
||||
]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 7), None, None)]), None))
|
||||
]),
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None)
|
||||
])
|
||||
)]
|
||||
}
|
||||
]
|
42
test/jstests/assignments.py
Normal file
42
test/jstests/assignments.py
Normal file
@ -0,0 +1,42 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS, _ASSIGN_OPERATORS
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'var x = 20; x = 30 + 1; return x;',
|
||||
'asserts': [{'value': 31}],
|
||||
'ast': [
|
||||
(Token.VAR, zip(
|
||||
['x'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 20), None, None)]),
|
||||
None)]
|
||||
)),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]),
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 30), None, None),
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])]),
|
||||
None))
|
||||
]),
|
||||
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None)
|
||||
]), None)
|
||||
]))
|
||||
]
|
||||
}, {
|
||||
'code': 'var x = 20; x += 30 + 1; return x;',
|
||||
'asserts': [{'value': 51}],
|
||||
}, {
|
||||
'code': 'var x = 20; x -= 30 + 1; return x;',
|
||||
'asserts': [{'value': -11}],
|
||||
}
|
||||
]
|
24
test/jstests/basic.py
Normal file
24
test/jstests/basic.py
Normal file
@ -0,0 +1,24 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'return 42;',
|
||||
'asserts': [{'value': 42}],
|
||||
'ast': [(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 42), None, None)]),
|
||||
None)
|
||||
]))]
|
||||
},
|
||||
{
|
||||
'code': ';',
|
||||
'asserts': [{'value': None}],
|
||||
'ast': [None]
|
||||
},
|
||||
{
|
||||
'code': 'var x5 = function(){return 42;}',
|
||||
'asserts': [{'value': 42, 'call': ('x5',)}]
|
||||
}
|
||||
]
|
35
test/jstests/branch.py
Normal file
35
test/jstests/branch.py
Normal file
@ -0,0 +1,35 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _RELATIONS
|
||||
|
||||
skip = {'i': 'Interpreting if statement not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function a(x) {
|
||||
if (x > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': True, 'call': ('a', 1)}, {'value': False, 'call': ('a', 0)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'a',
|
||||
['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.IF,
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.MEMBER, (Token.INT, 0), None, None),
|
||||
(Token.REL, _RELATIONS['>'][1])
|
||||
]), None)]),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.BOOL, True), None, None)]), None)])),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.BOOL, False), None, None)]), None)])))
|
||||
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
24
test/jstests/calc.py
Normal file
24
test/jstests/calc.py
Normal file
@ -0,0 +1,24 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
tests = [
|
||||
{'code': 'return 2*a+1;',
|
||||
'globals': {'a': 3},
|
||||
'asserts': [{'value': 7}],
|
||||
'ast': [(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
# Reverse Polish Notation!
|
||||
(Token.MEMBER, (Token.INT, 2), None, None),
|
||||
(Token.MEMBER, (Token.ID, 'a'), None, None),
|
||||
(Token.OP, _OPERATORS['*'][1]),
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]),
|
||||
None)
|
||||
])
|
||||
)]
|
||||
}
|
||||
]
|
111
test/jstests/call.py
Normal file
111
test/jstests/call.py
Normal file
@ -0,0 +1,111 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting function call not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function x() { return 2; }
|
||||
function y(a) { return x() + a; }
|
||||
function z() { return y(3); }
|
||||
''',
|
||||
'asserts': [{'value': 5, 'call': ('z',)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'x',
|
||||
[],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), None)
|
||||
])
|
||||
)
|
||||
])),
|
||||
(Token.FUNC, 'y',
|
||||
['a'],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, (Token.CALL, [], None)),
|
||||
(Token.MEMBER, (Token.ID, 'a'), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]), None)
|
||||
])
|
||||
)
|
||||
])),
|
||||
(Token.FUNC, 'z',
|
||||
[],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'y'), None, (Token.CALL, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 3), None, None)]), None)
|
||||
], None))
|
||||
]), None)
|
||||
])
|
||||
)
|
||||
]))
|
||||
]
|
||||
}, {
|
||||
'code': 'function x(a) { return a.split(""); }',
|
||||
'asserts': [{'value': ["a", "b", "c"], 'call': ('x',)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'x',
|
||||
['a'],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'a'), None,
|
||||
(Token.FIELD, 'split',
|
||||
(Token.CALL, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.STR, ''), None, None)]), None)
|
||||
], None))
|
||||
)]),
|
||||
None)
|
||||
])
|
||||
)
|
||||
]))
|
||||
]
|
||||
}, {
|
||||
'code': '''
|
||||
function a(x) { return x; }
|
||||
function b(x) { return x; }
|
||||
function c() { return [a, b][0](0); }
|
||||
''',
|
||||
'asserts': [{'value': 0}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'a', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None)
|
||||
])
|
||||
)
|
||||
])),
|
||||
(Token.FUNC, 'b', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None)
|
||||
])
|
||||
)
|
||||
])),
|
||||
(Token.FUNC, 'c', [],
|
||||
(Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ARRAY, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'a'), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'b'), None, None)]), None)
|
||||
]), None, (Token.ELEM, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None)
|
||||
]), (Token.CALL, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None)
|
||||
], None)))
|
||||
]), None)
|
||||
])
|
||||
)
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
69
test/jstests/comments.py
Normal file
69
test/jstests/comments.py
Normal file
@ -0,0 +1,69 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
var x = /* 1 + */ 2;
|
||||
var y = /* 30
|
||||
* 40 */ 50;
|
||||
return x + y;''',
|
||||
'asserts': [{'value': 52}],
|
||||
'ast': [
|
||||
(Token.VAR, zip(
|
||||
['x'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]),
|
||||
None)]
|
||||
)),
|
||||
(Token.VAR, zip(
|
||||
['y'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 50), None, None)]),
|
||||
None)]
|
||||
)),
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.MEMBER, (Token.ID, 'y'), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]), None)
|
||||
]))
|
||||
]
|
||||
}, {
|
||||
'code': '''
|
||||
var x = "/*";
|
||||
var y = 1 /* comment */ + 2;
|
||||
return y;
|
||||
''',
|
||||
'asserts': [{'value': 3}],
|
||||
'ast': [
|
||||
(Token.VAR, zip(
|
||||
['x'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.STR, '/*'), None, None)]),
|
||||
None)]
|
||||
)),
|
||||
(Token.VAR, zip(
|
||||
['y'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.MEMBER, (Token.INT, 2), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]),
|
||||
None)]
|
||||
)),
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'y'), None, None)]),
|
||||
None)
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
12
test/jstests/debug.py
Normal file
12
test/jstests/debug.py
Normal file
@ -0,0 +1,12 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
skip = {'i': 'Interpreting debugger statement not yet implemented',
|
||||
'p': 'Test not yet implemented: missing code and ast'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '',
|
||||
'asserts': [{'value': 0}],
|
||||
'ast': []
|
||||
}
|
||||
]
|
47
test/jstests/do_loop.py
Normal file
47
test/jstests/do_loop.py
Normal file
@ -0,0 +1,47 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
|
||||
|
||||
skip = {'i': 'Interpreting do loop not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f(x){
|
||||
i = 1;
|
||||
do{
|
||||
i++;
|
||||
} while (i < x);
|
||||
return i;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 5, 'call': 5}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'f', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, _ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None))
|
||||
]),
|
||||
(Token.DO,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.REL, _RELATIONS['<'][1])
|
||||
]), None)
|
||||
]),
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
]), None)
|
||||
])
|
||||
])),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None)]), None)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
21
test/jstests/empty_return.py
Normal file
21
test/jstests/empty_return.py
Normal file
@ -0,0 +1,21 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
tests = [
|
||||
{'code': 'return; y()',
|
||||
'asserts': [{'value': None}],
|
||||
'ast': [
|
||||
(Token.RETURN, None),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER,
|
||||
(Token.ID, 'y'),
|
||||
None,
|
||||
(Token.CALL, [], None)
|
||||
)
|
||||
]),
|
||||
None)
|
||||
])]
|
||||
}
|
||||
]
|
47
test/jstests/for_empty.py
Normal file
47
test/jstests/for_empty.py
Normal file
@ -0,0 +1,47 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
|
||||
|
||||
skip = {'i': 'Interpreting for empty loop not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f(x){
|
||||
var h = 0;
|
||||
for (; h <= x; ++h) {
|
||||
a = h;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 5, 'call': ('f', 5)}],
|
||||
'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)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
40
test/jstests/for_in.py
Normal file
40
test/jstests/for_in.py
Normal file
@ -0,0 +1,40 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting for in loop not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f(z){
|
||||
for (h in z) {
|
||||
a = h;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 'c', 'call': ('f', ['a', 'b', 'c'])}],
|
||||
'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)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
45
test/jstests/for_loop.py
Normal file
45
test/jstests/for_loop.py
Normal file
@ -0,0 +1,45 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
|
||||
|
||||
skip = {'i': 'Interpreting for loop not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f(x){
|
||||
for (var h = 0; h <= x; ++h) {
|
||||
a = h;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 5, 'call': ('f', 5)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'f', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.FOR,
|
||||
(Token.VAR, zip(['h'], [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), 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)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
58
test/jstests/func_expr.py
Normal file
58
test/jstests/func_expr.py
Normal file
@ -0,0 +1,58 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting function expression not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f() {
|
||||
var add = (function () {
|
||||
var counter = 0;
|
||||
return function () {return counter += 1;};
|
||||
})();
|
||||
add();
|
||||
add();
|
||||
return add();
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 3, 'call': ('f',)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'f', [],
|
||||
(Token.BLOCK, [
|
||||
(Token.VAR, zip(['add'], [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [
|
||||
(Token.VAR, zip(
|
||||
['counter'],
|
||||
[(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 0), None, None)
|
||||
]), None)]
|
||||
)),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, _ASSIGN_OPERATORS['+='][1], (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'counter'), None, None)
|
||||
]), (Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None)
|
||||
]), None))
|
||||
]))
|
||||
])), None, None)
|
||||
]), None)]))
|
||||
])), None, None),
|
||||
]), None)]), None, (Token.CALL, [], None))
|
||||
]), None)])),
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None))
|
||||
]), None)]),
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None))
|
||||
]), None)]),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None))
|
||||
]), None)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
25
test/jstests/getfield.py
Normal file
25
test/jstests/getfield.py
Normal file
@ -0,0 +1,25 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
skip = {'i': 'Interpreting get field not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'return a.var;',
|
||||
'asserts': [{'value': 3}],
|
||||
'globals': {'a': {'var': 3}},
|
||||
'ast': [
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER,
|
||||
(Token.ID, 'a'),
|
||||
None,
|
||||
(Token.FIELD, 'var', None)),
|
||||
]),
|
||||
None)
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
12
test/jstests/label.py
Normal file
12
test/jstests/label.py
Normal file
@ -0,0 +1,12 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
skip = {'i': 'Interpreting label not yet implemented',
|
||||
'p': 'Test not yet implemented: missing code and ast'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '',
|
||||
'asserts': [{'value': 0}],
|
||||
'ast': []
|
||||
}
|
||||
]
|
33
test/jstests/morespace.py
Normal file
33
test/jstests/morespace.py
Normal file
@ -0,0 +1,33 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting set field not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'x = 2 ; return x;',
|
||||
'asserts': [{'value': 2}],
|
||||
'ast': [
|
||||
(Token.EXPR,
|
||||
[(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]),
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]),
|
||||
None)
|
||||
)]
|
||||
),
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]),
|
||||
None)
|
||||
])
|
||||
)]
|
||||
}, {
|
||||
'code': 'function x (a) { return 2 * a + 1 ; }',
|
||||
'asserts': [{'value': 7, 'call': ('x', 3)}]
|
||||
}
|
||||
]
|
59
test/jstests/object_literal.py
Normal file
59
test/jstests/object_literal.py
Normal file
@ -0,0 +1,59 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting object literals not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f() {
|
||||
var o = {
|
||||
a: 7,
|
||||
get b() { return this.a + 1; },
|
||||
set c(x) { this.a = x / 2; }
|
||||
};
|
||||
return o;
|
||||
}
|
||||
''',
|
||||
'ast': [
|
||||
(Token.FUNC, 'f', [],
|
||||
(Token.BLOCK, [
|
||||
(Token.VAR,
|
||||
zip(['o'],
|
||||
[(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.OBJECT, [
|
||||
('a', (Token.PROPVALUE, (Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 7), None, None)
|
||||
]), None))),
|
||||
('b', (Token.PROPGET, (Token.BLOCK, [
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None)),
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]), None)]))
|
||||
]))),
|
||||
('c', (Token.PROPSET, 'x', (Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None))
|
||||
]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.MEMBER, (Token.INT, 2), None, None),
|
||||
(Token.OP, _OPERATORS['/'][1])
|
||||
]), None))
|
||||
])
|
||||
])))
|
||||
]),
|
||||
None, None)
|
||||
]), None)]
|
||||
)
|
||||
),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'o'), None, None)]), None)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
43
test/jstests/operators.py
Normal file
43
test/jstests/operators.py
Normal file
@ -0,0 +1,43 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'return 1 << 5;',
|
||||
'asserts': [{'value': 32}],
|
||||
'ast': [
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.MEMBER, (Token.INT, 5), None, None),
|
||||
(Token.OP, _OPERATORS['<<'][1])
|
||||
]), None)
|
||||
]))]
|
||||
}, {
|
||||
'code': 'return 19 & 21;',
|
||||
'asserts': [{'value': 17}],
|
||||
'ast': [
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 19), None, None),
|
||||
(Token.MEMBER, (Token.INT, 21), None, None),
|
||||
(Token.OP, _OPERATORS['&'][1])
|
||||
]), None)
|
||||
]))
|
||||
]
|
||||
}, {
|
||||
'code': 'return 11 >> 2;',
|
||||
'asserts': [{'value': 2}],
|
||||
'ast': [
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 11), None, None),
|
||||
(Token.MEMBER, (Token.INT, 2), None, None),
|
||||
(Token.OP, _OPERATORS['>>'][1])
|
||||
]), None)
|
||||
]))]
|
||||
}
|
||||
]
|
73
test/jstests/parens.py
Normal file
73
test/jstests/parens.py
Normal file
@ -0,0 +1,73 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'return (1 + 2) * 3;',
|
||||
'asserts': [{'value': 9}],
|
||||
'ast': [
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.MEMBER, (Token.INT, 2), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]), None)
|
||||
]), None, None),
|
||||
(Token.MEMBER, (Token.INT, 3), None, None),
|
||||
(Token.OP, _OPERATORS['*'][1])
|
||||
]), None)
|
||||
]))]
|
||||
}, {
|
||||
'code': 'return (1) + (2) * ((( (( (((((3)))))) )) ));',
|
||||
'asserts': [{'value': 7}],
|
||||
'ast': [
|
||||
|
||||
(Token.RETURN, (Token.EXPR, [
|
||||
(Token.ASSIGN, None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 1), None, None)
|
||||
]), None)]), None, None),
|
||||
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 2), None, None)
|
||||
]), None)]), None, None),
|
||||
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER,
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 3), None, None)
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None)
|
||||
]), None)]), None, None),
|
||||
|
||||
(Token.OP, _OPERATORS['*'][1]),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]), None)
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
80
test/jstests/precedence.py
Normal file
80
test/jstests/precedence.py
Normal file
@ -0,0 +1,80 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting get field not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
var a = [10, 20, 30, 40, 50];
|
||||
var b = 6;
|
||||
a[0]=a[b%a.length];
|
||||
return a;
|
||||
''',
|
||||
'asserts': [{'value': [20, 20, 30, 40, 50]}],
|
||||
'ast': [
|
||||
(Token.VAR,
|
||||
zip(['a'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ARRAY, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 10), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 20), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 30), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 40), None, None)]), None),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 50), None, None)]), None)
|
||||
]), None, None),
|
||||
]),
|
||||
None)
|
||||
])
|
||||
),
|
||||
(Token.VAR,
|
||||
zip(['b'],
|
||||
[(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 6), None, None)]), None)]
|
||||
)
|
||||
),
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'a'),
|
||||
None,
|
||||
(Token.ELEM,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]),
|
||||
None)
|
||||
]),
|
||||
None))
|
||||
]),
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'a'),
|
||||
None,
|
||||
(Token.ELEM, (Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'b'), None, None),
|
||||
(Token.MEMBER, (Token.ID, 'a'), None, (Token.FIELD, 'length', None)),
|
||||
(Token.OP, _OPERATORS['%'][1])
|
||||
]), None)]),
|
||||
None))
|
||||
]),
|
||||
None)
|
||||
)
|
||||
]),
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), None)
|
||||
])
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
31
test/jstests/strange_chars.py
Normal file
31
test/jstests/strange_chars.py
Normal file
@ -0,0 +1,31 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _OPERATORS
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': 'var $_axY2 = $_xY1 + 1; return $_axY2;',
|
||||
'globals': {'$_xY1': 20},
|
||||
'asserts': [{'value': 21}],
|
||||
'ast': [
|
||||
(Token.VAR,
|
||||
zip(['$_axY2'],
|
||||
[(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, '$_xY1'), None, None),
|
||||
(Token.MEMBER, (Token.INT, 1), None, None),
|
||||
(Token.OP, _OPERATORS['+'][1])
|
||||
]),
|
||||
None)
|
||||
])
|
||||
),
|
||||
(Token.RETURN,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
None,
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, '$_axY2'), None, None)]),
|
||||
None)]
|
||||
)
|
||||
)]
|
||||
}
|
||||
]
|
73
test/jstests/switch.py
Normal file
73
test/jstests/switch.py
Normal file
@ -0,0 +1,73 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS
|
||||
|
||||
skip = {'i': 'Interpreting switch statement not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function a(x) {
|
||||
switch (x) {
|
||||
case 6:
|
||||
break;
|
||||
case 5:
|
||||
x++;
|
||||
case 8:
|
||||
x--;
|
||||
break;
|
||||
default:
|
||||
x = 0;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 4, 'call': ('a', 0)},
|
||||
{'value': 5, 'call': ('a', 5)},
|
||||
{'value': 6, 'call': ('a', 6)},
|
||||
{'value': 8, 'call': ('a', 7)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'a', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.SWITCH, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None)
|
||||
]), None)]),
|
||||
[
|
||||
((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 6), None, None)]), None)]),
|
||||
[
|
||||
(Token.BREAK, None)
|
||||
]),
|
||||
((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 5), None, None)]), None)]),
|
||||
[
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
]), None)])
|
||||
]),
|
||||
((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.INT, 8), None, None)]), None)]),
|
||||
[
|
||||
(Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['--'][1])
|
||||
]), None)]),
|
||||
(Token.BREAK, None)
|
||||
]),
|
||||
(None,
|
||||
[
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN,
|
||||
_ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None)
|
||||
)
|
||||
])
|
||||
])
|
||||
]
|
||||
),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None)]), None)]))
|
||||
]))]
|
||||
}
|
||||
]
|
12
test/jstests/try_statement.py
Normal file
12
test/jstests/try_statement.py
Normal file
@ -0,0 +1,12 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
skip = {'i': 'Interpreting try statement not yet implemented',
|
||||
'p': 'Test not yet implemented: missing code and ast'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '',
|
||||
'asserts': [{'value': 0}],
|
||||
'ast': []
|
||||
}
|
||||
]
|
30
test/jstests/unshift.py
Normal file
30
test/jstests/unshift.py
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
skip = {'p': 'Signed integers not yet supported'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
var MAX_LENGTH = 0xffffffff;
|
||||
|
||||
var a = {};
|
||||
a.length = MAX_LENGTH + 1;
|
||||
assertEq([].unshift.call(a), MAX_LENGTH);
|
||||
assertEq(a.length, MAX_LENGTH);
|
||||
|
||||
function testGetSet(len, expected) {
|
||||
var newlen;
|
||||
var a = { get length() { return len; }, set length(v) { newlen = v; } };
|
||||
var res = [].unshift.call(a);
|
||||
assertEq(res, expected);
|
||||
assertEq(newlen, expected);
|
||||
}
|
||||
|
||||
testGetSet(0, 0);
|
||||
testGetSet(10, 10);
|
||||
testGetSet("1", 1);
|
||||
testGetSet(null, 0);
|
||||
testGetSet(MAX_LENGTH + 2, MAX_LENGTH);
|
||||
testGetSet(-5, 0);
|
||||
'''
|
||||
}
|
||||
]
|
47
test/jstests/while_loop.py
Normal file
47
test/jstests/while_loop.py
Normal file
@ -0,0 +1,47 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
|
||||
|
||||
skip = {'i': 'Interpreting while loop not yet implemented'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '''
|
||||
function f(x){
|
||||
i = 1;
|
||||
while (i < x) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
''',
|
||||
'asserts': [{'value': 5, 'call': ('f', 5)}],
|
||||
'ast': [
|
||||
(Token.FUNC, 'f', ['x'],
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, _ASSIGN_OPERATORS['='][1],
|
||||
(Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]),
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None))
|
||||
]),
|
||||
(Token.WHILE,
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.MEMBER, (Token.ID, 'x'), None, None),
|
||||
(Token.REL, _RELATIONS['<'][1])
|
||||
]), None)
|
||||
]),
|
||||
(Token.BLOCK, [
|
||||
(Token.EXPR, [
|
||||
(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None),
|
||||
(Token.UOP, _UNARY_OPERATORS['++'][1])
|
||||
]), None)
|
||||
])
|
||||
])),
|
||||
(Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
|
||||
(Token.MEMBER, (Token.ID, 'i'), None, None)]), None)]))
|
||||
]))
|
||||
]
|
||||
}
|
||||
]
|
12
test/jstests/with_statement.py
Normal file
12
test/jstests/with_statement.py
Normal file
@ -0,0 +1,12 @@
|
||||
from youtube_dl.jsinterp.jsgrammar import Token
|
||||
|
||||
skip = {'i': 'Interpreting with statement not yet implemented',
|
||||
'p': 'Test not yet implemented: missing code and ast'}
|
||||
|
||||
tests = [
|
||||
{
|
||||
'code': '',
|
||||
'asserts': [{'value': 0}],
|
||||
'ast': []
|
||||
}
|
||||
]
|
@ -2,9 +2,9 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Allow direct execution
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
@ -12,127 +12,44 @@ else:
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from youtube_dl.jsinterp import JSInterpreter
|
||||
from test.jstests import gettestcases
|
||||
|
||||
defs = gettestcases()
|
||||
|
||||
|
||||
class TestJSInterpreter(unittest.TestCase):
|
||||
def test_basic(self):
|
||||
jsi = JSInterpreter('function x(){;}')
|
||||
self.assertEqual(jsi.call_function('x'), None)
|
||||
def setUp(self):
|
||||
self.defs = defs
|
||||
|
||||
jsi = JSInterpreter('function x3(){return 42;}')
|
||||
self.assertEqual(jsi.call_function('x3'), 42)
|
||||
|
||||
jsi = JSInterpreter('var x5 = function(){return 42;}')
|
||||
self.assertEqual(jsi.call_function('x5'), 42)
|
||||
def generator(test_case):
|
||||
def test_template(self):
|
||||
for test in test_case['subtests']:
|
||||
jsi = JSInterpreter(test['code'], variables=None if 'globals' not in test else test['globals'])
|
||||
if 'asserts' in test:
|
||||
for a in test['asserts']:
|
||||
if 'call' in a:
|
||||
self.assertEqual(jsi.call_function(*a['call']), a['value'])
|
||||
else:
|
||||
self.assertEqual(jsi.run(), a['value'])
|
||||
|
||||
def test_calc(self):
|
||||
jsi = JSInterpreter('function x4(a){return 2*a+1;}')
|
||||
self.assertEqual(jsi.call_function('x4', 3), 7)
|
||||
if 'skip' not in test_case or 'i' not in test_case['skip']:
|
||||
reason = False
|
||||
else:
|
||||
reason = test_case['skip']['i']
|
||||
|
||||
def test_empty_return(self):
|
||||
jsi = JSInterpreter('function f(){return; y()}')
|
||||
self.assertEqual(jsi.call_function('f'), None)
|
||||
return test_template if not reason else unittest.skip(reason)(test_template)
|
||||
|
||||
@unittest.skip('Interpreting set field not yet implemented')
|
||||
def test_morespace(self):
|
||||
jsi = JSInterpreter('function x (a) { return 2 * a + 1 ; }')
|
||||
self.assertEqual(jsi.call_function('x', 3), 7)
|
||||
|
||||
jsi = JSInterpreter('function f () { x = 2 ; return x; }')
|
||||
self.assertEqual(jsi.call_function('f'), 2)
|
||||
|
||||
def test_strange_chars(self):
|
||||
jsi = JSInterpreter('function $_xY1 ($_axY1) { var $_axY2 = $_axY1 + 1; return $_axY2; }')
|
||||
self.assertEqual(jsi.call_function('$_xY1', 20), 21)
|
||||
|
||||
# TODO test prefix and postfix operators
|
||||
|
||||
def test_operators(self):
|
||||
jsi = JSInterpreter('function f(){return 1 << 5;}')
|
||||
self.assertEqual(jsi.call_function('f'), 32)
|
||||
|
||||
jsi = JSInterpreter('function f(){return 19 & 21;}')
|
||||
self.assertEqual(jsi.call_function('f'), 17)
|
||||
|
||||
jsi = JSInterpreter('function f(){return 11 >> 2;}')
|
||||
self.assertEqual(jsi.call_function('f'), 2)
|
||||
|
||||
def test_array_access(self):
|
||||
jsi = JSInterpreter('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;}')
|
||||
self.assertEqual(jsi.call_function('f'), [5, 2, 7])
|
||||
|
||||
def test_parens(self):
|
||||
jsi = JSInterpreter('function f(){return (1) + (2) * ((( (( (((((3)))))) )) ));}')
|
||||
self.assertEqual(jsi.call_function('f'), 7)
|
||||
|
||||
jsi = JSInterpreter('function f(){return (1 + 2) * 3;}')
|
||||
self.assertEqual(jsi.call_function('f'), 9)
|
||||
|
||||
def test_assignments(self):
|
||||
jsi = JSInterpreter('function f(){var x = 20; x = 30 + 1; return x;}')
|
||||
self.assertEqual(jsi.call_function('f'), 31)
|
||||
|
||||
jsi = JSInterpreter('function f(){var x = 20; x += 30 + 1; return x;}')
|
||||
self.assertEqual(jsi.call_function('f'), 51)
|
||||
|
||||
jsi = JSInterpreter('function f(){var x = 20; x -= 30 + 1; return x;}')
|
||||
self.assertEqual(jsi.call_function('f'), -11)
|
||||
|
||||
def test_comments(self):
|
||||
jsi = JSInterpreter('''
|
||||
function x() {
|
||||
var x = /* 1 + */ 2;
|
||||
var y = /* 30
|
||||
* 40 */ 50;
|
||||
return x + y;
|
||||
}
|
||||
''')
|
||||
self.assertEqual(jsi.call_function('x'), 52)
|
||||
|
||||
jsi = JSInterpreter('''
|
||||
function f() {
|
||||
var x = "/*";
|
||||
var y = 1 /* comment */ + 2;
|
||||
return y;
|
||||
}
|
||||
''')
|
||||
self.assertEqual(jsi.call_function('f'), 3)
|
||||
|
||||
@unittest.skip('Interpreting get field not yet implemented')
|
||||
def test_precedence(self):
|
||||
jsi = JSInterpreter('''
|
||||
function x() {
|
||||
var a = [10, 20, 30, 40, 50];
|
||||
var b = 6;
|
||||
a[0]=a[b%a.length];
|
||||
return a;
|
||||
}''')
|
||||
self.assertEqual(jsi.call_function('x'), [20, 20, 30, 40, 50])
|
||||
|
||||
@unittest.skip('Interpreting function call not yet implemented')
|
||||
def test_call(self):
|
||||
jsi = JSInterpreter('''
|
||||
function x() { return 2; }
|
||||
function y(a) { return x() + a; }
|
||||
function z() { return y(3); }
|
||||
''')
|
||||
self.assertEqual(jsi.call_function('z'), 5)
|
||||
jsi = JSInterpreter('function x(a) { return a.split(""); }', variables={'a': 'abc'})
|
||||
self.assertEqual(jsi.call_function('x'), ["a", "b", "c"])
|
||||
|
||||
@unittest.skip('Interpreting function call not yet implemented')
|
||||
def test_complex_call(self):
|
||||
jsi = JSInterpreter('''
|
||||
function a(x) { return x; }
|
||||
function b(x) { return x; }
|
||||
function c() { return [a, b][0](0); }
|
||||
''')
|
||||
self.assertEqual(jsi.call_function('c'), 0)
|
||||
|
||||
@unittest.skip('Interpreting get field not yet implemented')
|
||||
def test_getfield(self):
|
||||
jsi = JSInterpreter('function c() { return a.var; }', variables={'a': {'var': 3}})
|
||||
self.assertEqual(jsi.call_function('c'), 3)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
# And add them to TestJSInterpreter
|
||||
for n, tc in enumerate(defs):
|
||||
if any('asserts' in test for test in tc['subtests']):
|
||||
test_method = generator(tc)
|
||||
tname = 'test_' + str(tc['name'])
|
||||
i = 1
|
||||
while hasattr(TestJSInterpreter, tname):
|
||||
tname = 'test_%s_%d' % (tc['name'], i)
|
||||
i += 1
|
||||
test_method.__name__ = str(tname)
|
||||
setattr(TestJSInterpreter, test_method.__name__, test_method)
|
||||
del test_method
|
||||
|
65
test/test_jsinterp_parse.py
Normal file
65
test/test_jsinterp_parse.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from youtube_dl.jsinterp import JSInterpreter
|
||||
from test.jstests import gettestcases
|
||||
|
||||
|
||||
def traverse(node, tree_types=(list, tuple)):
|
||||
if type(node) == zip:
|
||||
node = list(copy.deepcopy(node))
|
||||
if isinstance(node, tree_types):
|
||||
tree = []
|
||||
for value in node:
|
||||
tree.append(traverse(value, tree_types))
|
||||
return tree
|
||||
else:
|
||||
return node
|
||||
|
||||
|
||||
defs = gettestcases()
|
||||
|
||||
|
||||
class TestJSInterpreterParse(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.defs = defs
|
||||
|
||||
|
||||
def generator(test_case):
|
||||
def test_template(self):
|
||||
for a in test_case['subtests']:
|
||||
jsi = JSInterpreter(a['code'], variables=None if 'globals' not in a else a['globals'])
|
||||
parsed = list(jsi.statements())
|
||||
if 'ast' in a:
|
||||
self.assertEqual(traverse(parsed), traverse(a['ast']))
|
||||
|
||||
if 'skip' not in test_case or 'p' not in test_case['skip']:
|
||||
reason = False
|
||||
else:
|
||||
reason = test_case['skip']['p']
|
||||
|
||||
return test_template if not reason else unittest.skip(reason)(test_template)
|
||||
|
||||
|
||||
# And add them to TestJSInterpreter
|
||||
for n, tc in enumerate(defs):
|
||||
test_method = generator(tc)
|
||||
tname = 'test_' + str(tc['name'])
|
||||
i = 1
|
||||
while hasattr(TestJSInterpreterParse, tname):
|
||||
tname = 'test_%s_%d' % (tc['name'], i)
|
||||
i += 1
|
||||
test_method.__name__ = str(tname)
|
||||
setattr(TestJSInterpreterParse, test_method.__name__, test_method)
|
||||
del test_method
|
File diff suppressed because it is too large
Load Diff
@ -48,6 +48,7 @@ _SINGLE_QUOTED_RE = r"""'(?:(?:\\'|\n)|[^'\n])*'"""
|
||||
_DOUBLE_QUOTED_RE = r'''"(?:(?:\\"|\n)|[^"\n])*"'''
|
||||
_STRING_RE = r'(?:%s)|(?:%s)' % (_SINGLE_QUOTED_RE, _DOUBLE_QUOTED_RE)
|
||||
|
||||
# FIXME signed values
|
||||
_INTEGER_RE = r'(?:%(hex)s)|(?:%(dec)s)|(?:%(oct)s)' % {'hex': __HEXADECIMAL_RE, 'dec': __DECIMAL_RE, 'oct': __OCTAL_RE}
|
||||
_FLOAT_RE = r'(?:(?:%(dec)s\.[0-9]*)|(?:\.[0-9]+))(?:[eE][+-]?[0-9]+)?' % {'dec': __DECIMAL_RE}
|
||||
|
||||
|
@ -55,7 +55,7 @@ class JSInterpreter(object):
|
||||
for k, v in dict(variables).items():
|
||||
# XXX validate identifiers
|
||||
self.global_vars[k] = Reference(v, (self.global_vars, k))
|
||||
self._context = Context(self.global_vars)
|
||||
self._context = Context()
|
||||
self._context_stack = []
|
||||
|
||||
def statements(self, code=None, pos=0, stack_size=100):
|
||||
@ -920,7 +920,8 @@ class JSInterpreter(object):
|
||||
|
||||
elif name is Token.ID:
|
||||
# XXX error handling (unknown id)
|
||||
ref = self._context.local_vars[expr[1]] if expr[1] in self._context.local_vars else self.global_vars[expr[1]]
|
||||
ref = (self._context.local_vars[expr[1]] if expr[1] in self._context.local_vars else
|
||||
self.global_vars[expr[1]])
|
||||
|
||||
# literal
|
||||
elif name in _token_keys:
|
||||
@ -939,6 +940,18 @@ class JSInterpreter(object):
|
||||
|
||||
return ref
|
||||
|
||||
def run(self, cx=None):
|
||||
if cx is not None:
|
||||
self.push_context(cx)
|
||||
res = None
|
||||
for stmt in self.statements():
|
||||
res = self.interpret_statement(stmt)
|
||||
if self._context.ended:
|
||||
if cx is not None:
|
||||
self.pop_context()
|
||||
break
|
||||
return res
|
||||
|
||||
def extract_object(self, objname):
|
||||
obj = {}
|
||||
obj_m = re.search(
|
||||
|
@ -124,7 +124,10 @@ class TokenStream(object):
|
||||
elif token_id is Token.STR:
|
||||
yield (token_id, token_value[1:-1], pos)
|
||||
elif token_id is Token.INT:
|
||||
yield (token_id, int(token_value), pos)
|
||||
# FIXME signed values
|
||||
root = ((16 if len(token_value) > 2 and token_value[1] in 'xX' else 8)
|
||||
if token_value.startswith('0') else 10)
|
||||
yield (token_id, int(token_value, root), pos)
|
||||
elif token_id is Token.FLOAT:
|
||||
yield (token_id, float(token_value), pos)
|
||||
elif token_id is Token.REGEX:
|
||||
@ -142,6 +145,8 @@ class TokenStream(object):
|
||||
else:
|
||||
raise ExtractorError('Unexpected token at %d' % pos)
|
||||
pos = feed_m.end()
|
||||
elif pos >= len(self.code):
|
||||
self.ended = True
|
||||
else:
|
||||
raise ExtractorError('Unrecognised sequence at %d' % pos)
|
||||
raise StopIteration
|
||||
|
Loading…
x
Reference in New Issue
Block a user