1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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()
|