[jsinterp] Test suit update

- Fixes (at least changes) global variable referencing in `jsinterp2`
- Adds test suite based testcase for `jsinterp`
- Adds per assertion based skip
- Renames `test_jsinterp` (hopefully temporally) to `test_jsinterp_orig`
- Adds function declaration to testcases (code & ast)
This commit is contained in:
sulyi 2018-06-02 02:01:40 +02:00
parent 70ac98a924
commit 1f40e3ef63
33 changed files with 740 additions and 429 deletions

View File

@ -4,75 +4,79 @@ from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.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)
{'code': 'function f() { var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x; }',
'asserts': [{'value': [5, 2, 7], 'call': ('f',)}],
'ast': [
(Token.FUNC, 'f', [], [
(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.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 4), 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)
])
)
]),
(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)
])
)]
])
]
}
]

View File

@ -5,40 +5,42 @@ from youtube_dl.jsinterp2.tstream import _OPERATORS, _ASSIGN_OPERATORS
tests = [
{
'code': 'var x = 20; x = 30 + 1; return x;',
'asserts': [{'value': 31}],
'code': 'function f() { var x = 20; x = 30 + 1; return x; }',
'asserts': [{'value': 31, 'call': ('f',)}],
'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.FUNC, 'f', [], [
(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)
]))
(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': 'function f() { var x = 20; x += 30 + 1; return x;}',
'asserts': [{'value': 51, 'call': ('f',)}],
}, {
'code': 'var x = 20; x -= 30 + 1; return x;',
'asserts': [{'value': -11}],
'code': 'function f() { var x = 20; x -= 30 + 1; return x;}',
'asserts': [{'value': -11, 'call': ('f',)}],
}
]

View File

@ -4,23 +4,29 @@ from youtube_dl.jsinterp2.jsgrammar import Token
tests = [
{
'code': 'return 42;',
'asserts': [{'value': 42}],
'ast': [(Token.RETURN,
'code': 'function f() { return 42; }',
'asserts': [{'value': 42, 'call': ('f',)}],
'ast': [
(Token.FUNC, 'f', [], [
(Token.RETURN,
(Token.EXPR, [
(Token.ASSIGN,
None,
(Token.OPEXPR, [(Token.MEMBER, (Token.INT, 42), None, None)]),
None)
]))]
]))
])
]
},
{
'code': ';',
'asserts': [{'value': None}],
'ast': [None]
'code': 'function x() {;}',
'asserts': [{'value': None, 'call': ('x',)}],
'ast': [(Token.FUNC, 'x', [], [None])]
},
{
'code': 'var x5 = function(){return 42;}',
# FIXME: function expresiion needs to be implemented
'exclude': ('jsinterp2',),
'code': 'var x5 = function x5(){return 42;}',
'asserts': [{'value': 42, 'call': ('x5',)}]
}
]

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _RELATIONS
skip = {'interpret': 'Interpreting if statement not yet implemented'}
skip = {
'jsinterp': 'Branching is not supported',
'interpret': 'Interpreting if statement not yet implemented'
}
tests = [
{

View File

@ -4,10 +4,11 @@ from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _OPERATORS
tests = [
{'code': 'return 2*a+1;',
'globals': {'a': 3},
'asserts': [{'value': 7}],
'ast': [(Token.RETURN,
{'code': 'function x4(a){return 2*a+1;}',
'asserts': [{'value': 7, 'call': ('x4', 3)}],
'ast': [
(Token.FUNC, 'x4', ['a'], [
(Token.RETURN,
(Token.EXPR, [
(Token.ASSIGN,
None,
@ -21,6 +22,8 @@ tests = [
]),
None)
])
)]
)
])
]
}
]

View File

@ -9,9 +9,8 @@ tests = [
function x() { return 2; }
function y(a) { return x() + a; }
function z() { return y(3); }
z();
''',
'asserts': [{'value': 5}],
'asserts': [{'value': 5, 'call': ('z',)}],
'ast': [
(Token.FUNC, 'x', [], [
(Token.RETURN, (Token.EXPR, [
@ -37,17 +36,13 @@ tests = [
]), None)
])
)
]),
(Token.EXPR, [
(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'z'), None, (Token.CALL, [], None))
]), None)
])
]
}, {
'code': 'function x(a) { return a.split(""); }',
# FIXME built-in functions not yet implemented
# 'asserts': [{'value': ["a", "b", "c"], 'call': ('x',"abc")}],
'exclude': ('jsinterp2',),
'code': 'function x(a) { return a.split(""); }',
'asserts': [{'value': ["a", "b", "c"], 'call': ('x',"abc")}],
'ast': [
(Token.FUNC, 'x', ['a'], [
(Token.RETURN, (Token.EXPR, [
@ -63,13 +58,13 @@ tests = [
])
]
}, {
'exclude': ('jsinterp',),
'code': '''
function a(x) { return x; }
function b(x) { return x + 1; }
function c() { return [a, b][0](0); }
c();
''',
'asserts': [{'value': 0}],
'asserts': [{'value': 0, 'call': ('c',)}],
'ast': [
(Token.FUNC, 'a', ['x'], [
(Token.RETURN, (Token.EXPR, [
@ -100,11 +95,6 @@ tests = [
], None)))
]), None)
]))
]),
(Token.EXPR, [
(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.ID, 'c'), None, (Token.CALL, [], None))
]), None)
])
]
}

View File

@ -3,69 +3,80 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _OPERATORS
skip = {'jsinterp': 'Not yet fully implemented'}
tests = [
{
'code': '''
var x = /* 1 + */ 2;
var y = /* 30
* 40 */ 50;
return x + y;''',
'asserts': [{'value': 52}],
function x() {
var x = /* 1 + */ 2;
var y = /* 30
* 40 */ 50;
return x + y;
}
''',
'asserts': [{'value': 52, 'call': ('x',)}],
'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)
]))
(Token.FUNC, 'x', [], [
(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;
function f() {
var x = "/*";
var y = 1 /* comment */ + 2;
return y;
}
''',
'asserts': [{'value': 3}],
'asserts': [{'value': 3, 'call': ('f',)}],
'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)
]))
(Token.FUNC, 'f', [], [
(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)
]))
])
]
}
]

View File

@ -2,8 +2,11 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
skip = {'interpret': 'Interpreting debugger statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'}
skip = {
'jsinterp': 'Debugger statement is not supported',
'interpret': 'Interpreting debugger statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'
}
tests = [
{

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
skip = {'interpret': 'Interpreting do loop not yet implemented'}
skip = {
'jsinterp': 'Do loop is not supportted',
'interpret': 'Interpreting do loop not yet implemented'
}
tests = [
{
@ -16,7 +19,7 @@ tests = [
return i;
}
''',
'asserts': [{'value': 5, 'call': 5}],
'asserts': [{'value': 5, 'call': ('f', 5)}],
'ast': [
(Token.FUNC, 'f', ['x'], [
(Token.EXPR, [

View File

@ -3,21 +3,24 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
tests = [
{'code': 'return; y()',
'asserts': [{'value': None}],
{'code': 'function f() { return; y(); }',
'asserts': [{'value': None, 'call': ('f',)}],
'ast': [
(Token.RETURN, None),
(Token.EXPR, [
(Token.ASSIGN,
None,
(Token.OPEXPR, [
(Token.MEMBER,
(Token.ID, 'y'),
None,
(Token.CALL, [], None)
)
]),
None)
])]
(Token.FUNC, 'f', [], [
(Token.RETURN, None),
(Token.EXPR, [
(Token.ASSIGN,
None,
(Token.OPEXPR, [
(Token.MEMBER,
(Token.ID, 'y'),
None,
(Token.CALL, [], None)
)
]),
None)
])
])
]
}
]

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
skip = {'interpret': 'Interpreting for empty loop not yet implemented'}
skip = {
'jsinterp': 'For loop is not supported',
'interpret': 'Interpreting for empty loop not yet implemented'
}
tests = [
{

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS
skip = {'interpret': 'Interpreting for in loop not yet implemented'}
skip = {
'jsinterp': 'For in loop is not supported',
'interpret': 'Interpreting for in loop not yet implemented'
}
tests = [
{

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
skip = {'interpret': 'Interpreting for loop not yet implemented'}
skip = {
'jsinterp': 'For loop is not supported',
'interpret': 'Interpreting for loop not yet implemented'
}
tests = [
{

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS
skip = {'interpret': 'Interpreting function expression not yet implemented'}
skip = {
'jsinterp': 'not supported',
'interpret': 'Interpreting function expression not yet implemented'
}
tests = [
{

View File

@ -2,24 +2,28 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
skip = {'jsinterp': 'Field access is not supported'}
tests = [
{
'code': 'return a.var;',
'asserts': [{'value': 3}],
'code': 'function f() { return a.var; }',
'asserts': [{'value': 3, 'call': ('f',)}],
'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)
]))
(Token.FUNC, 'f', [], [
(Token.RETURN,
(Token.EXPR, [
(Token.ASSIGN,
None,
(Token.OPEXPR, [
(Token.MEMBER,
(Token.ID, 'a'),
None,
(Token.FIELD, 'var', None)),
]),
None)
]))
])
]
}
]

View File

@ -2,8 +2,11 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
skip = {'interpret': 'Interpreting label not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'}
skip = {
'jsinterp': 'Label statement is not supported',
'interpret': 'Interpreting label not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'
}
tests = [
{

View File

@ -5,27 +5,30 @@ from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS
tests = [
{
'code': 'x = 2 ; return x;',
'asserts': [{'value': 2}],
'code': 'function f() { x = 2 ; return x; }',
'asserts': [{'value': 2, 'call': ('f',)}],
'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)
])
)]
(Token.FUNC, 'f', [], [
(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)}]

View File

@ -5,41 +5,49 @@ from youtube_dl.jsinterp2.tstream import _OPERATORS
tests = [
{
'code': 'return 1 << 5;',
'asserts': [{'value': 32}],
'code': 'function f() { return 1 << 5; }',
'asserts': [{'value': 32, 'call': ('f',)}],
'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)
]))
(Token.FUNC, 'f', [], [
(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 11 >> 2;',
'asserts': [{'value': 2}],
'code': 'function f() { return 19 & 21;}',
'asserts': [{'value': 17, 'call': ('f',)}],
'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)
]))]
(Token.FUNC, 'f', [], [
(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': 'function f() { return 11 >> 2;}',
'asserts': [{'value': 2, 'call': ('f',)}],
'ast': [
(Token.FUNC, 'f', [], [
(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)
]))
])
]
}
]

View File

@ -5,71 +5,75 @@ from youtube_dl.jsinterp2.tstream import _OPERATORS
tests = [
{
'code': 'return (1 + 2) * 3;',
'asserts': [{'value': 9}],
'code': 'function f() { return (1 + 2) * 3; }',
'asserts': [{'value': 9, 'call': ('f',)}],
'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)
]))]
(Token.FUNC, 'f', [], [
(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}],
'code': 'function f() { return (1) + (2) * ((( (( (((((3)))))) )) ));}',
'asserts': [{'value': 7, 'call': ('f',)}],
'ast': [
(Token.FUNC, 'f', [], [
(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.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.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.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [
(Token.MEMBER, (Token.INT, 3), 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.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)
]), None)]), None, None),
]), None)]), None, None),
(Token.OP, _OPERATORS['*'][1]),
(Token.OP, _OPERATORS['+'][1])
]), None)
]))
(Token.OP, _OPERATORS['*'][1]),
(Token.OP, _OPERATORS['+'][1])
]), None)
]))
])
]
}
]

View File

@ -8,75 +8,79 @@ skip = {'interpret': 'Interpreting built-in fields not yet implemented'}
tests = [
{
'code': '''
var a = [10, 20, 30, 40, 50];
var b = 6;
a[0]=a[b%a.length];
return a;
function f() {
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]}],
'asserts': [{'value': [20, 20, 30, 40, 50], 'call': ('f',)}],
'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'),
(Token.FUNC, 'f', [], [
(Token.VAR,
zip(['a'],
[(Token.ASSIGN,
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.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)
])
)
]),
(Token.RETURN,
(Token.EXPR, [
(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), None)
])
)
])
]
}
]

View File

@ -5,29 +5,31 @@ from youtube_dl.jsinterp2.tstream import _OPERATORS
tests = [
{
'code': 'var $_axY2 = $_xY1 + 1; return $_axY2;',
'globals': {'$_xY1': 20},
'asserts': [{'value': 21}],
'code': 'function $_xY1 ($_axY1) { var $_axY2 = $_axY1 + 1; return $_axY2; }',
'asserts': [{'value': 21, 'call': ('$_xY1', 20)}],
'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)]
)
)]
(Token.FUNC, '$_xY1', ['$_axY1'], [
(Token.VAR,
zip(['$_axY2'],
[(Token.ASSIGN,
None,
(Token.OPEXPR, [
(Token.MEMBER, (Token.ID, '$_axY1'), 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)]
)
)
])
]
}
]

View File

@ -1,12 +1,16 @@
from __future__ import unicode_literals
skip = {'parse': 'Ast not yet implemented'}
skip = {
'jsinterp': 'String literals are not supported',
'parse': 'Ast not yet implemented'
}
tests = [
{
'code': '"hello".split("");',
'exclude': ('jsinterp2',),
'code': 'function f() {return "hello".split(""); }',
'globals': {},
'asserts': [{'value': ['h', 'e', 'l', 'l', 'o']}],
'asserts': [{'value': ['h', 'e', 'l', 'l', 'o'], 'call': ('f',)}],
'ast': []
}
]

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS
skip = {'interpret': 'Interpreting switch statement not yet implemented'}
skip = {
'jsinterp': 'Switch statement is not supported',
'interpret': 'Interpreting switch statement not yet implemented'
}
tests = [
{

View File

@ -2,8 +2,11 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
skip = {'interpret': 'Interpreting try statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'}
skip = {
'jsinterp': 'Try statement is not supported',
'interpret': 'Interpreting try statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'
}
tests = [
{

View File

@ -1,11 +1,14 @@
from __future__ import unicode_literals
skip = {'parse': True}
skip = {
'jsinterp': 'Unary opertations are not supported',
'parse': True
}
tests = [
{
'code': 'return -5 + +3;',
'asserts': [{'value': -2}]
'code': 'function f() { return -5 + +3; }',
'asserts': [{'value': -2, 'call': ('f',)}]
}, {
'code': 'function f() {return -5 + ++a;}',
'globals': {'a': -3},

View File

@ -1,5 +1,11 @@
from __future__ import unicode_literals
skip = {
'jsinterp': 'Test not implemented',
'parse': 'Test not implemented',
'interpert': 'Test not implemented'
}
tests = [
{
'code': '''

View File

@ -3,7 +3,10 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
from youtube_dl.jsinterp2.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS
skip = {'interpret': 'Interpreting while loop not yet implemented'}
skip = {
'jsinterp': 'While loop is not supported',
'interpret': 'Interpreting while loop not yet implemented'
}
tests = [
{

View File

@ -2,8 +2,11 @@ from __future__ import unicode_literals
from youtube_dl.jsinterp2.jsgrammar import Token
skip = {'interpret': 'Interpreting with statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'}
skip = {
'jsinterp': 'With statement is not supported',
'interpret': 'Interpreting with statement not yet implemented',
'parse': 'Test not yet implemented: missing code and ast'
}
tests = [
{

View File

@ -17,7 +17,7 @@ else:
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.jsinterp2 import JSInterpreter
from youtube_dl.jsinterp import JSInterpreter
from .js2tests import gettestcases
defs = gettestcases()
@ -33,7 +33,10 @@ class TestJSInterpreter(unittest.TestCase):
def generator(test_case, name):
def test_template(self):
for test in test_case['subtests']:
if 'code' not in test:
excluded = test.get('exclude')
if excluded is not None and 'jsinterp' in excluded:
log_reason = 'jsinterp does not support this subtest:\n%s' % test['code']
elif 'code' not in test:
log_reason = 'No code in subtest, skipping'
elif 'asserts' not in test:
log_reason = 'No assertion in subtest, skipping'
@ -41,13 +44,20 @@ def generator(test_case, name):
log_reason = None
if log_reason is None:
jsi = JSInterpreter(test['code'], variables=test.get('globals'))
for a in test['asserts']:
if 'value' in a:
if 'call' in a:
self.assertEqual(jsi.call_function(*a['call']), a['value'])
else:
self.assertEqual(jsi.run(), a['value'])
variables = test.get('globals')
code = test['code']
call = None
if variables is not None:
code = 'function f(%s){%s}' % ((''.join(variables.keys())), code)
call = ('f',) + tuple(v for v in variables.values())
jsi = JSInterpreter(code, objects=variables)
for assertion in test['asserts']:
if 'value' in assertion:
if call is None:
call = assertion['call']
self.assertEqual(jsi.call_function(*call), assertion['value'])
else:
log.debug('No value in assertion, skipping')
else:
@ -59,7 +69,7 @@ def generator(test_case, name):
# And add them to TestJSInterpreter
for n, tc in enumerate(defs):
reason = tc['skip'].get('interpret', False)
reason = tc['skip'].get('jsinterp', False)
tname = 'test_' + str(tc['name'])
i = 1
while hasattr(TestJSInterpreter, tname):

91
test/test_jsinterp2.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python
# """
# see: `js2tests`
# """
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import logging
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.jsinterp2 import JSInterpreter
from .js2tests import gettestcases
defs = gettestcases()
# set level to logging.DEBUG to see messages about missing assertions
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
class TestJSInterpreter(unittest.TestCase):
def setUp(self):
self.defs = defs
def generator(test_case, name):
def test_template(self):
for test in test_case['subtests']:
excluded = test.get('exclude')
if excluded is not None and 'jsinterp2' in excluded:
log_reason = 'jsinterp does not support this subtest:\n%s' % test['code']
elif 'code' not in test:
log_reason = 'No code in subtest, skipping'
elif 'asserts' not in test:
log_reason = 'No assertion in subtest, skipping'
else:
log_reason = None
if log_reason is None:
jsi = JSInterpreter(test['code'], variables=(test.get('globals')))
jsi.run()
for assertion in test['asserts']:
if 'value' in assertion:
call = assertion['call']
self.assertEqual(jsi.call_function(*call), assertion['value'])
else:
log.debug('No value in assertion, skipping')
else:
log.debug(log_reason)
log = logging.getLogger('TestJSInterpreter.%s' % name)
return test_template
# And add them to TestJSInterpreter
for n, tc in enumerate(defs):
reason = tc['skip'].get('interpret', False)
tname = 'test_' + str(tc['name'])
i = 1
while hasattr(TestJSInterpreter, tname):
tname = 'test_%s_%d' % (tc['name'], i)
i += 1
if reason is not True:
log_reason = 'Entirely'
elif not any('asserts' in test for test in tc['subtests']):
log_reason = '''There isn't any assertion'''
else:
log_reason = None
if log_reason is not None:
test_method = generator(tc, tname)
test_method.__name__ = str(tname)
if reason is not False:
test_method.__unittest_skip__ = True
test_method.__unittest_skip_why__ = reason
setattr(TestJSInterpreter, test_method.__name__, test_method)
del test_method
else:
log = logging.getLogger('TestJSInterpreter')
log.debug('Skipping %s:%s' % (tname, log_reason))
if __name__ == '__main__':
unittest.main()

117
test/test_jsinterp_orig.py Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.jsinterp import JSInterpreter
class TestJSInterpreter(unittest.TestCase):
def test_basic(self):
jsi = JSInterpreter('function x(){;}')
self.assertEqual(jsi.call_function('x'), None)
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 test_calc(self):
jsi = JSInterpreter('function x4(a){return 2*a+1;}')
self.assertEqual(jsi.call_function('x4', 3), 7)
def test_empty_return(self):
jsi = JSInterpreter('function f(){return; y()}')
self.assertEqual(jsi.call_function('f'), None)
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)
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):
'Skipping: Not yet fully implemented'
return
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)
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])
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)
if __name__ == '__main__':
unittest.main()

View File

@ -284,8 +284,8 @@ class JSInterpreter(object):
self._context = self._context_stack.pop()
def call_function(self, funcname, *args):
f = (self.this[funcname] if funcname in self.this else
self.global_vars[funcname] if funcname in self.global_vars else
f = (self.this[funcname].getvalue() if funcname in self.this else
self.global_vars[funcname].getvalue() if funcname in self.global_vars else
self.extract_function(funcname))
return f(*args)