Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80555 views
1
import {LooseParser} from "./state"
2
import {isDummy} from "./parseutil"
3
import {getLineInfo, tokTypes as tt} from ".."
4
5
const lp = LooseParser.prototype
6
7
lp.parseTopLevel = function() {
8
let node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0)
9
node.body = []
10
while (this.tok.type !== tt.eof) node.body.push(this.parseStatement())
11
this.last = this.tok
12
if (this.options.ecmaVersion >= 6) {
13
node.sourceType = this.options.sourceType
14
}
15
return this.finishNode(node, "Program")
16
}
17
18
lp.parseStatement = function() {
19
let starttype = this.tok.type, node = this.startNode()
20
21
switch (starttype) {
22
case tt._break: case tt._continue:
23
this.next()
24
let isBreak = starttype === tt._break
25
if (this.semicolon() || this.canInsertSemicolon()) {
26
node.label = null
27
} else {
28
node.label = this.tok.type === tt.name ? this.parseIdent() : null
29
this.semicolon()
30
}
31
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
32
33
case tt._debugger:
34
this.next()
35
this.semicolon()
36
return this.finishNode(node, "DebuggerStatement")
37
38
case tt._do:
39
this.next()
40
node.body = this.parseStatement()
41
node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent()
42
this.semicolon()
43
return this.finishNode(node, "DoWhileStatement")
44
45
case tt._for:
46
this.next()
47
this.pushCx()
48
this.expect(tt.parenL)
49
if (this.tok.type === tt.semi) return this.parseFor(node, null)
50
if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) {
51
let init = this.parseVar(true)
52
if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) {
53
return this.parseForIn(node, init)
54
}
55
return this.parseFor(node, init)
56
}
57
let init = this.parseExpression(true)
58
if (this.tok.type === tt._in || this.isContextual("of"))
59
return this.parseForIn(node, this.toAssignable(init))
60
return this.parseFor(node, init)
61
62
case tt._function:
63
this.next()
64
return this.parseFunction(node, true)
65
66
case tt._if:
67
this.next()
68
node.test = this.parseParenExpression()
69
node.consequent = this.parseStatement()
70
node.alternate = this.eat(tt._else) ? this.parseStatement() : null
71
return this.finishNode(node, "IfStatement")
72
73
case tt._return:
74
this.next()
75
if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null
76
else { node.argument = this.parseExpression(); this.semicolon() }
77
return this.finishNode(node, "ReturnStatement")
78
79
case tt._switch:
80
let blockIndent = this.curIndent, line = this.curLineStart
81
this.next()
82
node.discriminant = this.parseParenExpression()
83
node.cases = []
84
this.pushCx()
85
this.expect(tt.braceL)
86
87
let cur
88
while (!this.closes(tt.braceR, blockIndent, line, true)) {
89
if (this.tok.type === tt._case || this.tok.type === tt._default) {
90
let isCase = this.tok.type === tt._case
91
if (cur) this.finishNode(cur, "SwitchCase")
92
node.cases.push(cur = this.startNode())
93
cur.consequent = []
94
this.next()
95
if (isCase) cur.test = this.parseExpression()
96
else cur.test = null
97
this.expect(tt.colon)
98
} else {
99
if (!cur) {
100
node.cases.push(cur = this.startNode())
101
cur.consequent = []
102
cur.test = null
103
}
104
cur.consequent.push(this.parseStatement())
105
}
106
}
107
if (cur) this.finishNode(cur, "SwitchCase")
108
this.popCx()
109
this.eat(tt.braceR)
110
return this.finishNode(node, "SwitchStatement")
111
112
case tt._throw:
113
this.next()
114
node.argument = this.parseExpression()
115
this.semicolon()
116
return this.finishNode(node, "ThrowStatement")
117
118
case tt._try:
119
this.next()
120
node.block = this.parseBlock()
121
node.handler = null
122
if (this.tok.type === tt._catch) {
123
let clause = this.startNode()
124
this.next()
125
this.expect(tt.parenL)
126
clause.param = this.toAssignable(this.parseExprAtom(), true)
127
this.expect(tt.parenR)
128
clause.guard = null
129
clause.body = this.parseBlock()
130
node.handler = this.finishNode(clause, "CatchClause")
131
}
132
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
133
if (!node.handler && !node.finalizer) return node.block
134
return this.finishNode(node, "TryStatement")
135
136
case tt._var:
137
case tt._let:
138
case tt._const:
139
return this.parseVar()
140
141
case tt._while:
142
this.next()
143
node.test = this.parseParenExpression()
144
node.body = this.parseStatement()
145
return this.finishNode(node, "WhileStatement")
146
147
case tt._with:
148
this.next()
149
node.object = this.parseParenExpression()
150
node.body = this.parseStatement()
151
return this.finishNode(node, "WithStatement")
152
153
case tt.braceL:
154
return this.parseBlock()
155
156
case tt.semi:
157
this.next()
158
return this.finishNode(node, "EmptyStatement")
159
160
case tt._class:
161
return this.parseClass(true)
162
163
case tt._import:
164
return this.parseImport()
165
166
case tt._export:
167
return this.parseExport()
168
169
default:
170
let expr = this.parseExpression()
171
if (isDummy(expr)) {
172
this.next()
173
if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement")
174
return this.parseStatement()
175
} else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) {
176
node.body = this.parseStatement()
177
node.label = expr
178
return this.finishNode(node, "LabeledStatement")
179
} else {
180
node.expression = expr
181
this.semicolon()
182
return this.finishNode(node, "ExpressionStatement")
183
}
184
}
185
}
186
187
lp.parseBlock = function() {
188
let node = this.startNode()
189
this.pushCx()
190
this.expect(tt.braceL)
191
let blockIndent = this.curIndent, line = this.curLineStart
192
node.body = []
193
while (!this.closes(tt.braceR, blockIndent, line, true))
194
node.body.push(this.parseStatement())
195
this.popCx()
196
this.eat(tt.braceR)
197
return this.finishNode(node, "BlockStatement")
198
}
199
200
lp.parseFor = function(node, init) {
201
node.init = init
202
node.test = node.update = null
203
if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression()
204
if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression()
205
this.popCx()
206
this.expect(tt.parenR)
207
node.body = this.parseStatement()
208
return this.finishNode(node, "ForStatement")
209
}
210
211
lp.parseForIn = function(node, init) {
212
let type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement"
213
this.next()
214
node.left = init
215
node.right = this.parseExpression()
216
this.popCx()
217
this.expect(tt.parenR)
218
node.body = this.parseStatement()
219
return this.finishNode(node, type)
220
}
221
222
lp.parseVar = function(noIn) {
223
let node = this.startNode()
224
node.kind = this.tok.type.keyword
225
this.next()
226
node.declarations = []
227
do {
228
let decl = this.startNode()
229
decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom(), true) : this.parseIdent()
230
decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null
231
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
232
} while (this.eat(tt.comma))
233
if (!node.declarations.length) {
234
let decl = this.startNode()
235
decl.id = this.dummyIdent()
236
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
237
}
238
if (!noIn) this.semicolon()
239
return this.finishNode(node, "VariableDeclaration")
240
}
241
242
lp.parseClass = function(isStatement) {
243
let node = this.startNode()
244
this.next()
245
if (this.tok.type === tt.name) node.id = this.parseIdent()
246
else if (isStatement) node.id = this.dummyIdent()
247
else node.id = null
248
node.superClass = this.eat(tt._extends) ? this.parseExpression() : null
249
node.body = this.startNode()
250
node.body.body = []
251
this.pushCx()
252
let indent = this.curIndent + 1, line = this.curLineStart
253
this.eat(tt.braceL)
254
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
255
while (!this.closes(tt.braceR, indent, line)) {
256
if (this.semicolon()) continue
257
let method = this.startNode(), isGenerator
258
if (this.options.ecmaVersion >= 6) {
259
method.static = false
260
isGenerator = this.eat(tt.star)
261
}
262
this.parsePropertyName(method)
263
if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
264
if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" &&
265
(this.tok.type != tt.parenL && this.tok.type != tt.braceL)) {
266
method.static = true
267
isGenerator = this.eat(tt.star)
268
this.parsePropertyName(method)
269
} else {
270
method.static = false
271
}
272
if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" &&
273
!method.computed && (method.key.name === "get" || method.key.name === "set") &&
274
this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) {
275
method.kind = method.key.name
276
this.parsePropertyName(method)
277
method.value = this.parseMethod(false)
278
} else {
279
if (!method.computed && !method.static && !isGenerator && (
280
method.key.type === "Identifier" && method.key.name === "constructor" ||
281
method.key.type === "Literal" && method.key.value === "constructor")) {
282
method.kind = "constructor"
283
} else {
284
method.kind = "method"
285
}
286
method.value = this.parseMethod(isGenerator)
287
}
288
node.body.body.push(this.finishNode(method, "MethodDefinition"))
289
}
290
this.popCx()
291
if (!this.eat(tt.braceR)) {
292
// If there is no closing brace, make the node span to the start
293
// of the next token (this is useful for Tern)
294
this.last.end = this.tok.start
295
if (this.options.locations) this.last.loc.end = this.tok.loc.start
296
}
297
this.semicolon()
298
this.finishNode(node.body, "ClassBody")
299
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
300
}
301
302
lp.parseFunction = function(node, isStatement) {
303
this.initFunction(node)
304
if (this.options.ecmaVersion >= 6) {
305
node.generator = this.eat(tt.star)
306
}
307
if (this.tok.type === tt.name) node.id = this.parseIdent()
308
else if (isStatement) node.id = this.dummyIdent()
309
node.params = this.parseFunctionParams()
310
node.body = this.parseBlock()
311
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
312
}
313
314
lp.parseExport = function() {
315
let node = this.startNode()
316
this.next()
317
if (this.eat(tt.star)) {
318
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
319
return this.finishNode(node, "ExportAllDeclaration")
320
}
321
if (this.eat(tt._default)) {
322
let expr = this.parseMaybeAssign()
323
if (expr.id) {
324
switch (expr.type) {
325
case "FunctionExpression": expr.type = "FunctionDeclaration"; break
326
case "ClassExpression": expr.type = "ClassDeclaration"; break
327
}
328
}
329
node.declaration = expr
330
this.semicolon()
331
return this.finishNode(node, "ExportDefaultDeclaration")
332
}
333
if (this.tok.type.keyword) {
334
node.declaration = this.parseStatement()
335
node.specifiers = []
336
node.source = null
337
} else {
338
node.declaration = null
339
node.specifiers = this.parseExportSpecifierList()
340
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
341
this.semicolon()
342
}
343
return this.finishNode(node, "ExportNamedDeclaration")
344
}
345
346
lp.parseImport = function() {
347
let node = this.startNode()
348
this.next()
349
if (this.tok.type === tt.string) {
350
node.specifiers = []
351
node.source = this.parseExprAtom()
352
node.kind = ''
353
} else {
354
let elt
355
if (this.tok.type === tt.name && this.tok.value !== "from") {
356
elt = this.startNode()
357
elt.local = this.parseIdent()
358
this.finishNode(elt, "ImportDefaultSpecifier")
359
this.eat(tt.comma)
360
}
361
node.specifiers = this.parseImportSpecifierList()
362
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
363
if (elt) node.specifiers.unshift(elt)
364
}
365
this.semicolon()
366
return this.finishNode(node, "ImportDeclaration")
367
}
368
369
lp.parseImportSpecifierList = function() {
370
let elts = []
371
if (this.tok.type === tt.star) {
372
let elt = this.startNode()
373
this.next()
374
if (this.eatContextual("as")) elt.local = this.parseIdent()
375
elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"))
376
} else {
377
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
378
this.pushCx()
379
this.eat(tt.braceL)
380
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
381
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
382
let elt = this.startNode()
383
if (this.eat(tt.star)) {
384
if (this.eatContextual("as")) elt.local = this.parseIdent()
385
this.finishNode(elt, "ImportNamespaceSpecifier")
386
} else {
387
if (this.isContextual("from")) break
388
elt.imported = this.parseIdent()
389
elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported
390
this.finishNode(elt, "ImportSpecifier")
391
}
392
elts.push(elt)
393
this.eat(tt.comma)
394
}
395
this.eat(tt.braceR)
396
this.popCx()
397
}
398
return elts
399
}
400
401
lp.parseExportSpecifierList = function() {
402
let elts = []
403
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
404
this.pushCx()
405
this.eat(tt.braceL)
406
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
407
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
408
if (this.isContextual("from")) break
409
let elt = this.startNode()
410
elt.local = this.parseIdent()
411
elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local
412
this.finishNode(elt, "ExportSpecifier")
413
elts.push(elt)
414
this.eat(tt.comma)
415
}
416
this.eat(tt.braceR)
417
this.popCx()
418
return elts
419
}
420
421