diff options
Diffstat (limited to 'third_party/rust/jsparagus/tests/test_js.py')
-rw-r--r-- | third_party/rust/jsparagus/tests/test_js.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus/tests/test_js.py b/third_party/rust/jsparagus/tests/test_js.py new file mode 100644 index 0000000000..571232f77a --- /dev/null +++ b/third_party/rust/jsparagus/tests/test_js.py @@ -0,0 +1,207 @@ +""" Tests for the JS parser. """ + +import unittest +import jsparagus.lexer +from js_parser.parser import parse_Script, JSParser +from js_parser.lexer import JSLexer + + +class ESTestCase(unittest.TestCase): + def parse(self, s): + if isinstance(s, list): + f = JSLexer(JSParser()) + for chunk in s: + f.write(chunk) + return f.close() + else: + return parse_Script(s) + + def assert_parses(self, s): + self.parse(s) + + def assert_incomplete(self, s): + """Assert that s fails to parse with UnexpectedEndError. + + (This should be the case if `s` is a prefix of a valid Script.) + """ + self.assertRaises(jsparagus.lexer.UnexpectedEndError, + lambda: parse_Script(s)) + + def assert_syntax_error(self, s): + """Assert that s fails to parse.""" + with self.assertRaises(jsparagus.lexer.SyntaxError): + parse_Script(s) + + def assert_can_close_after(self, s): + parser = JSParser() + lexer = JSLexer(parser) + if isinstance(s, list): + for chunk in s: + lexer.write(chunk) + else: + lexer.write(s) + self.assertTrue(lexer.can_close()) + + # === Tests! + + def test_asi_at_end(self): + self.assert_parses("3 + 4") + self.assert_syntax_error("3 4") + self.assert_incomplete("3 +") + self.assert_incomplete("{") + self.assert_incomplete("{;") + + def test_asi_at_block_end(self): + self.assert_parses("{ doCrimes() }") + self.assert_parses("function f() { ok }") + + def test_asi_after_line_terminator(self): + self.assert_parses('''\ + switch (value) { + case 1: break + case 2: console.log('2'); + } + ''') + self.assert_syntax_error( + "switch (value) { case 1: break case 2: console.log('2'); }") + + def test_asi_after_no_line_terminator_here(self): + self.assert_parses('''\ + function f() { + return + x; + } + ''') + + def test_asi_suppressed(self): + # The specification says ASI does not happen in the production + # EmptyStatement : `;`. + self.assert_syntax_error("if (true)") + self.assert_syntax_error("{ for (;;) }") + + # ASI does not happen in for(;;) loops. + self.assert_syntax_error("for ( \n ; ) {}") + self.assert_syntax_error("for ( ; \n ) {}") + self.assert_syntax_error("for ( \n \n ) {}") + self.assert_syntax_error("for (var i = 0 \n i < 9; i++) {}") + self.assert_syntax_error("for (var i = 0; i < 9 \n i++) {}") + self.assert_syntax_error("for (i = 0 \n i < 9; i++) {}") + self.assert_syntax_error("for (i = 0; i < 9 \n i++) {}") + self.assert_syntax_error("for (let i = 0 \n i < 9; i++) {}") + + # ASI is suppressed in the production ClassElement[Yield, Await] : `;` + # to prevent an infinite loop of ASI. lol + self.assert_syntax_error("class Fail { \n +1; }") + + def test_if_else(self): + self.assert_parses("if (x) f();") + self.assert_incomplete("if (x)") + self.assert_parses("if (x) f(); else g();") + self.assert_incomplete("if (x) f(); else") + self.assert_parses("if (x) if (y) g(); else h();") + self.assert_parses("if (x) if (y) g(); else h(); else j();") + + def test_lexer_decimal(self): + self.assert_parses("0.") + self.assert_parses(".5") + self.assert_syntax_error(".") + + def test_arrow(self): + self.assert_parses("x => x") + self.assert_parses("f = x => x;") + self.assert_parses("(x, y) => [y, x]") + self.assert_parses("f = (x, y) => {}") + self.assert_syntax_error("(x, y) => {x: x, y: y}") + + def test_invalid_character(self): + self.assert_syntax_error("\0") + self.assert_syntax_error("—x;") + self.assert_syntax_error("const ONE_THIRD = 1 ÷ 3;") + + def test_regexp(self): + self.assert_parses(r"/\w/") + self.assert_parses("/[A-Z]/") + self.assert_parses("/[//]/") + self.assert_parses("/a*a/") + self.assert_parses("/**//x*/") + self.assert_parses("{} /x/") + self.assert_parses("of / 2") + + def test_incomplete_comments(self): + self.assert_syntax_error("/*") + self.assert_syntax_error("/* hello world") + self.assert_syntax_error("/* hello world *") + self.assert_parses(["/* hello\n", " world */"]) + self.assert_parses(["// oawfeoiawj", "ioawefoawjie"]) + self.assert_parses(["// oawfeoiawj", "ioawefoawjie\n ok();"]) + self.assert_parses(["// oawfeoiawj", "ioawefoawjie", "jiowaeawojefiw"]) + self.assert_parses(["// oawfeoiawj", "ioawefoawjie", "jiowaeawojefiw\n ok();"]) + + def test_awkward_chunks(self): + self.assert_parses(["let", "ter.head = 1;"]) + self.assert_parses(["let", " x = 1;"]) + + # `list()` here explodes the string into a list of one-character strings. + self.assert_parses(list("function f() { ok(); }")) + + self.assertEqual( + self.parse(["/xyzzy/", "g;"]), + ('script', + ('script_body', + ('statement_list_single', + ('expression_statement', + ('regexp_literal', '/xyzzy/g')))))) + + self.assertEqual( + self.parse(['x/', '=2;']), + ('script', + ('script_body', + ('statement_list_single', + ('expression_statement', + ('compound_assignment_expr', + ('identifier_expr', ('identifier_reference', 'x')), + ('box_assign_op', ('div_assign_op', '/=')), + ('numeric_literal', '2'))))))) + + def test_can_close(self): + self.assert_can_close_after([]) + self.assert_can_close_after("") + self.assert_can_close_after("2 + 2;\n") + self.assert_can_close_after("// seems ok\n") + + def test_can_close_with_asi(self): + self.assert_can_close_after("2 + 2\n") + + def test_conditional_keywords(self): + # property names + self.assert_parses("let obj = {if: 3, function: 4};") + self.assert_parses("assert(obj.if == 3);") + + # method names + self.assert_parses(""" + class C { + if() {} + function() {} + } + """) + + self.assert_parses("var let = [new Date];") # let as identifier + self.assert_parses("let v = let;") # let as keyword, then identifier + # Next line would fail because the multitoken `let [` lookahead isn't implemented yet. + # self.assert_parses("let.length;") # `let .` -> ExpressionStatement + self.assert_syntax_error("let[0].getYear();") # `let [` -> LexicalDeclaration + + self.assert_parses(""" + var of = [1, 2, 3]; + for (of of of) console.log(of); // logs 1, 2, 3 + """) + self.assert_parses("var of, let, private, target;") + self.assert_parses("class X { get y() {} }") + self.assert_parses("async: { break async; }") + self.assert_parses("var get = { get get() {}, set get(v) {}, set: 3 };") + self.assert_parses("for (async of => {};;) {}") + # self.assert_parses("for (async of []) {}") # would fail + + +if __name__ == '__main__': + unittest.main() |