Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80550 views
1
import {types as tt} from "./tokentype"
2
import {Parser} from "./state"
3
import {lineBreak} from "./whitespace"
4
5
const pp = Parser.prototype
6
7
// ### Statement parsing
8
9
// Parse a program. Initializes the parser, reads any number of
10
// statements, and wraps them in a Program node. Optionally takes a
11
// `program` argument. If present, the statements will be appended
12
// to its body instead of creating a new node.
13
14
pp.parseTopLevel = function(node) {
15
let first = true
16
if (!node.body) node.body = []
17
while (this.type !== tt.eof) {
18
let stmt = this.parseStatement(true, true)
19
node.body.push(stmt)
20
if (first && this.isUseStrict(stmt)) this.setStrict(true)
21
first = false
22
}
23
this.next()
24
if (this.options.ecmaVersion >= 6) {
25
node.sourceType = this.options.sourceType
26
}
27
return this.finishNode(node, "Program")
28
}
29
30
const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
31
32
// Parse a single statement.
33
//
34
// If expecting a statement and finding a slash operator, parse a
35
// regular expression literal. This is to handle cases like
36
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
37
// does not help.
38
39
pp.parseStatement = function(declaration, topLevel) {
40
let starttype = this.type, node = this.startNode()
41
42
// Most types of statements are recognized by the keyword they
43
// start with. Many are trivial to parse, some require a bit of
44
// complexity.
45
46
switch (starttype) {
47
case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
48
case tt._debugger: return this.parseDebuggerStatement(node)
49
case tt._do: return this.parseDoStatement(node)
50
case tt._for: return this.parseForStatement(node)
51
case tt._function:
52
if (!declaration && this.options.ecmaVersion >= 6) this.unexpected()
53
return this.parseFunctionStatement(node)
54
case tt._class:
55
if (!declaration) this.unexpected()
56
return this.parseClass(node, true)
57
case tt._if: return this.parseIfStatement(node)
58
case tt._return: return this.parseReturnStatement(node)
59
case tt._switch: return this.parseSwitchStatement(node)
60
case tt._throw: return this.parseThrowStatement(node)
61
case tt._try: return this.parseTryStatement(node)
62
case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var
63
case tt._var: return this.parseVarStatement(node, starttype)
64
case tt._while: return this.parseWhileStatement(node)
65
case tt._with: return this.parseWithStatement(node)
66
case tt.braceL: return this.parseBlock()
67
case tt.semi: return this.parseEmptyStatement(node)
68
case tt._export:
69
case tt._import:
70
if (!this.options.allowImportExportEverywhere) {
71
if (!topLevel)
72
this.raise(this.start, "'import' and 'export' may only appear at the top level")
73
if (!this.inModule)
74
this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
75
}
76
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)
77
78
// If the statement does not start with a statement keyword or a
79
// brace, it's an ExpressionStatement or LabeledStatement. We
80
// simply start parsing an expression, and afterwards, if the
81
// next token is a colon and the expression was a simple
82
// Identifier node, we switch to interpreting it as a label.
83
default:
84
let maybeName = this.value, expr = this.parseExpression()
85
if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))
86
return this.parseLabeledStatement(node, maybeName, expr)
87
else return this.parseExpressionStatement(node, expr)
88
}
89
}
90
91
pp.parseBreakContinueStatement = function(node, keyword) {
92
let isBreak = keyword == "break"
93
this.next()
94
if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
95
else if (this.type !== tt.name) this.unexpected()
96
else {
97
node.label = this.parseIdent()
98
this.semicolon()
99
}
100
101
// Verify that there is an actual destination to break or
102
// continue to.
103
for (var i = 0; i < this.labels.length; ++i) {
104
let lab = this.labels[i]
105
if (node.label == null || lab.name === node.label.name) {
106
if (lab.kind != null && (isBreak || lab.kind === "loop")) break
107
if (node.label && isBreak) break
108
}
109
}
110
if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
111
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
112
}
113
114
pp.parseDebuggerStatement = function(node) {
115
this.next()
116
this.semicolon()
117
return this.finishNode(node, "DebuggerStatement")
118
}
119
120
pp.parseDoStatement = function(node) {
121
this.next()
122
this.labels.push(loopLabel)
123
node.body = this.parseStatement(false)
124
this.labels.pop()
125
this.expect(tt._while)
126
node.test = this.parseParenExpression()
127
if (this.options.ecmaVersion >= 6)
128
this.eat(tt.semi)
129
else
130
this.semicolon()
131
return this.finishNode(node, "DoWhileStatement")
132
}
133
134
// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
135
// loop is non-trivial. Basically, we have to parse the init `var`
136
// statement or expression, disallowing the `in` operator (see
137
// the second parameter to `parseExpression`), and then check
138
// whether the next token is `in` or `of`. When there is no init
139
// part (semicolon immediately after the opening parenthesis), it
140
// is a regular `for` loop.
141
142
pp.parseForStatement = function(node) {
143
this.next()
144
this.labels.push(loopLabel)
145
this.expect(tt.parenL)
146
if (this.type === tt.semi) return this.parseFor(node, null)
147
if (this.type === tt._var || this.type === tt._let || this.type === tt._const) {
148
let init = this.startNode(), varKind = this.type
149
this.next()
150
this.parseVar(init, true, varKind)
151
this.finishNode(init, "VariableDeclaration")
152
if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&
153
!(varKind !== tt._var && init.declarations[0].init))
154
return this.parseForIn(node, init)
155
return this.parseFor(node, init)
156
}
157
let refShorthandDefaultPos = {start: 0}
158
let init = this.parseExpression(true, refShorthandDefaultPos)
159
if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
160
this.toAssignable(init)
161
this.checkLVal(init)
162
return this.parseForIn(node, init)
163
} else if (refShorthandDefaultPos.start) {
164
this.unexpected(refShorthandDefaultPos.start)
165
}
166
return this.parseFor(node, init)
167
}
168
169
pp.parseFunctionStatement = function(node) {
170
this.next()
171
return this.parseFunction(node, true)
172
}
173
174
pp.parseIfStatement = function(node) {
175
this.next()
176
node.test = this.parseParenExpression()
177
node.consequent = this.parseStatement(false)
178
node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null
179
return this.finishNode(node, "IfStatement")
180
}
181
182
pp.parseReturnStatement = function(node) {
183
if (!this.inFunction && !this.options.allowReturnOutsideFunction)
184
this.raise(this.start, "'return' outside of function")
185
this.next()
186
187
// In `return` (and `break`/`continue`), the keywords with
188
// optional arguments, we eagerly look for a semicolon or the
189
// possibility to insert one.
190
191
if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null
192
else { node.argument = this.parseExpression(); this.semicolon() }
193
return this.finishNode(node, "ReturnStatement")
194
}
195
196
pp.parseSwitchStatement = function(node) {
197
this.next()
198
node.discriminant = this.parseParenExpression()
199
node.cases = []
200
this.expect(tt.braceL)
201
this.labels.push(switchLabel)
202
203
// Statements under must be grouped (by label) in SwitchCase
204
// nodes. `cur` is used to keep the node that we are currently
205
// adding statements to.
206
207
for (var cur, sawDefault; this.type != tt.braceR;) {
208
if (this.type === tt._case || this.type === tt._default) {
209
let isCase = this.type === tt._case
210
if (cur) this.finishNode(cur, "SwitchCase")
211
node.cases.push(cur = this.startNode())
212
cur.consequent = []
213
this.next()
214
if (isCase) {
215
cur.test = this.parseExpression()
216
} else {
217
if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses")
218
sawDefault = true
219
cur.test = null
220
}
221
this.expect(tt.colon)
222
} else {
223
if (!cur) this.unexpected()
224
cur.consequent.push(this.parseStatement(true))
225
}
226
}
227
if (cur) this.finishNode(cur, "SwitchCase")
228
this.next() // Closing brace
229
this.labels.pop()
230
return this.finishNode(node, "SwitchStatement")
231
}
232
233
pp.parseThrowStatement = function(node) {
234
this.next()
235
if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
236
this.raise(this.lastTokEnd, "Illegal newline after throw")
237
node.argument = this.parseExpression()
238
this.semicolon()
239
return this.finishNode(node, "ThrowStatement")
240
}
241
242
// Reused empty array added for node fields that are always empty.
243
244
const empty = []
245
246
pp.parseTryStatement = function(node) {
247
this.next()
248
node.block = this.parseBlock()
249
node.handler = null
250
if (this.type === tt._catch) {
251
let clause = this.startNode()
252
this.next()
253
this.expect(tt.parenL)
254
clause.param = this.parseBindingAtom()
255
this.checkLVal(clause.param, true)
256
this.expect(tt.parenR)
257
clause.guard = null
258
clause.body = this.parseBlock()
259
node.handler = this.finishNode(clause, "CatchClause")
260
}
261
node.guardedHandlers = empty
262
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
263
if (!node.handler && !node.finalizer)
264
this.raise(node.start, "Missing catch or finally clause")
265
return this.finishNode(node, "TryStatement")
266
}
267
268
pp.parseVarStatement = function(node, kind) {
269
this.next()
270
this.parseVar(node, false, kind)
271
this.semicolon()
272
return this.finishNode(node, "VariableDeclaration")
273
}
274
275
pp.parseWhileStatement = function(node) {
276
this.next()
277
node.test = this.parseParenExpression()
278
this.labels.push(loopLabel)
279
node.body = this.parseStatement(false)
280
this.labels.pop()
281
return this.finishNode(node, "WhileStatement")
282
}
283
284
pp.parseWithStatement = function(node) {
285
if (this.strict) this.raise(this.start, "'with' in strict mode")
286
this.next()
287
node.object = this.parseParenExpression()
288
node.body = this.parseStatement(false)
289
return this.finishNode(node, "WithStatement")
290
}
291
292
pp.parseEmptyStatement = function(node) {
293
this.next()
294
return this.finishNode(node, "EmptyStatement")
295
}
296
297
pp.parseLabeledStatement = function(node, maybeName, expr) {
298
for (let i = 0; i < this.labels.length; ++i)
299
if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared")
300
let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null
301
this.labels.push({name: maybeName, kind: kind})
302
node.body = this.parseStatement(true)
303
this.labels.pop()
304
node.label = expr
305
return this.finishNode(node, "LabeledStatement")
306
}
307
308
pp.parseExpressionStatement = function(node, expr) {
309
node.expression = expr
310
this.semicolon()
311
return this.finishNode(node, "ExpressionStatement")
312
}
313
314
// Parse a semicolon-enclosed block of statements, handling `"use
315
// strict"` declarations when `allowStrict` is true (used for
316
// function bodies).
317
318
pp.parseBlock = function(allowStrict) {
319
let node = this.startNode(), first = true, oldStrict
320
node.body = []
321
this.expect(tt.braceL)
322
while (!this.eat(tt.braceR)) {
323
let stmt = this.parseStatement(true)
324
node.body.push(stmt)
325
if (first && allowStrict && this.isUseStrict(stmt)) {
326
oldStrict = this.strict
327
this.setStrict(this.strict = true)
328
}
329
first = false
330
}
331
if (oldStrict === false) this.setStrict(false)
332
return this.finishNode(node, "BlockStatement")
333
}
334
335
// Parse a regular `for` loop. The disambiguation code in
336
// `parseStatement` will already have parsed the init statement or
337
// expression.
338
339
pp.parseFor = function(node, init) {
340
node.init = init
341
this.expect(tt.semi)
342
node.test = this.type === tt.semi ? null : this.parseExpression()
343
this.expect(tt.semi)
344
node.update = this.type === tt.parenR ? null : this.parseExpression()
345
this.expect(tt.parenR)
346
node.body = this.parseStatement(false)
347
this.labels.pop()
348
return this.finishNode(node, "ForStatement")
349
}
350
351
// Parse a `for`/`in` and `for`/`of` loop, which are almost
352
// same from parser's perspective.
353
354
pp.parseForIn = function(node, init) {
355
let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"
356
this.next()
357
node.left = init
358
node.right = this.parseExpression()
359
this.expect(tt.parenR)
360
node.body = this.parseStatement(false)
361
this.labels.pop()
362
return this.finishNode(node, type)
363
}
364
365
// Parse a list of variable declarations.
366
367
pp.parseVar = function(node, isFor, kind) {
368
node.declarations = []
369
node.kind = kind.keyword
370
for (;;) {
371
let decl = this.startNode()
372
this.parseVarId(decl)
373
if (this.eat(tt.eq)) {
374
decl.init = this.parseMaybeAssign(isFor)
375
} else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
376
this.unexpected()
377
} else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) {
378
this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")
379
} else {
380
decl.init = null
381
}
382
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
383
if (!this.eat(tt.comma)) break
384
}
385
return node
386
}
387
388
pp.parseVarId = function(decl) {
389
decl.id = this.parseBindingAtom()
390
this.checkLVal(decl.id, true)
391
}
392
393
// Parse a function declaration or literal (depending on the
394
// `isStatement` parameter).
395
396
pp.parseFunction = function(node, isStatement, allowExpressionBody) {
397
this.initFunction(node)
398
if (this.options.ecmaVersion >= 6)
399
node.generator = this.eat(tt.star)
400
if (isStatement || this.type === tt.name)
401
node.id = this.parseIdent()
402
this.parseFunctionParams(node)
403
this.parseFunctionBody(node, allowExpressionBody)
404
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
405
}
406
407
pp.parseFunctionParams = function(node) {
408
this.expect(tt.parenL)
409
node.params = this.parseBindingList(tt.parenR, false, false)
410
}
411
412
// Parse a class declaration or literal (depending on the
413
// `isStatement` parameter).
414
415
pp.parseClass = function(node, isStatement) {
416
this.next()
417
this.parseClassId(node, isStatement)
418
this.parseClassSuper(node)
419
let classBody = this.startNode()
420
let hadConstructor = false
421
classBody.body = []
422
this.expect(tt.braceL)
423
while (!this.eat(tt.braceR)) {
424
if (this.eat(tt.semi)) continue
425
let method = this.startNode()
426
let isGenerator = this.eat(tt.star)
427
let isMaybeStatic = this.type === tt.name && this.value === "static"
428
this.parsePropertyName(method)
429
method.static = isMaybeStatic && this.type !== tt.parenL
430
if (method.static) {
431
if (isGenerator) this.unexpected()
432
isGenerator = this.eat(tt.star)
433
this.parsePropertyName(method)
434
}
435
method.kind = "method"
436
if (!method.computed) {
437
let {key} = method
438
let isGetSet = false
439
if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) {
440
isGetSet = true
441
method.kind = key.name
442
key = this.parsePropertyName(method)
443
}
444
if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||
445
key.type === "Literal" && key.value === "constructor")) {
446
if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class")
447
if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier")
448
if (isGenerator) this.raise(key.start, "Constructor can't be a generator")
449
method.kind = "constructor"
450
hadConstructor = true
451
}
452
}
453
this.parseClassMethod(classBody, method, isGenerator)
454
}
455
node.body = this.finishNode(classBody, "ClassBody")
456
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
457
}
458
459
pp.parseClassMethod = function(classBody, method, isGenerator) {
460
method.value = this.parseMethod(isGenerator)
461
classBody.body.push(this.finishNode(method, "MethodDefinition"))
462
}
463
464
pp.parseClassId = function(node, isStatement) {
465
node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null
466
}
467
468
pp.parseClassSuper = function(node) {
469
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null
470
}
471
472
// Parses module export declaration.
473
474
pp.parseExport = function(node) {
475
this.next()
476
// export * from '...'
477
if (this.eat(tt.star)) {
478
this.expectContextual("from")
479
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
480
this.semicolon()
481
return this.finishNode(node, "ExportAllDeclaration")
482
}
483
if (this.eat(tt._default)) { // export default ...
484
let expr = this.parseMaybeAssign()
485
let needsSemi = true
486
if (expr.type == "FunctionExpression" ||
487
expr.type == "ClassExpression") {
488
needsSemi = false
489
if (expr.id) {
490
expr.type = expr.type == "FunctionExpression"
491
? "FunctionDeclaration"
492
: "ClassDeclaration"
493
}
494
}
495
node.declaration = expr
496
if (needsSemi) this.semicolon()
497
return this.finishNode(node, "ExportDefaultDeclaration")
498
}
499
// export var|const|let|function|class ...
500
if (this.shouldParseExportStatement()) {
501
node.declaration = this.parseStatement(true)
502
node.specifiers = []
503
node.source = null
504
} else { // export { x, y as z } [from '...']
505
node.declaration = null
506
node.specifiers = this.parseExportSpecifiers()
507
if (this.eatContextual("from")) {
508
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
509
} else {
510
node.source = null
511
}
512
this.semicolon()
513
}
514
return this.finishNode(node, "ExportNamedDeclaration")
515
}
516
517
pp.shouldParseExportStatement = function() {
518
return this.type.keyword
519
}
520
521
// Parses a comma-separated list of module exports.
522
523
pp.parseExportSpecifiers = function() {
524
let nodes = [], first = true
525
// export { x, y as z } [from '...']
526
this.expect(tt.braceL)
527
while (!this.eat(tt.braceR)) {
528
if (!first) {
529
this.expect(tt.comma)
530
if (this.afterTrailingComma(tt.braceR)) break
531
} else first = false
532
533
let node = this.startNode()
534
node.local = this.parseIdent(this.type === tt._default)
535
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local
536
nodes.push(this.finishNode(node, "ExportSpecifier"))
537
}
538
return nodes
539
}
540
541
// Parses import declaration.
542
543
pp.parseImport = function(node) {
544
this.next()
545
// import '...'
546
if (this.type === tt.string) {
547
node.specifiers = empty
548
node.source = this.parseExprAtom()
549
node.kind = ""
550
} else {
551
node.specifiers = this.parseImportSpecifiers()
552
this.expectContextual("from")
553
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
554
}
555
this.semicolon()
556
return this.finishNode(node, "ImportDeclaration")
557
}
558
559
// Parses a comma-separated list of module imports.
560
561
pp.parseImportSpecifiers = function() {
562
let nodes = [], first = true
563
if (this.type === tt.name) {
564
// import defaultObj, { x, y as z } from '...'
565
let node = this.startNode()
566
node.local = this.parseIdent()
567
this.checkLVal(node.local, true)
568
nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
569
if (!this.eat(tt.comma)) return nodes
570
}
571
if (this.type === tt.star) {
572
let node = this.startNode()
573
this.next()
574
this.expectContextual("as")
575
node.local = this.parseIdent()
576
this.checkLVal(node.local, true)
577
nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
578
return nodes
579
}
580
this.expect(tt.braceL)
581
while (!this.eat(tt.braceR)) {
582
if (!first) {
583
this.expect(tt.comma)
584
if (this.afterTrailingComma(tt.braceR)) break
585
} else first = false
586
587
let node = this.startNode()
588
node.imported = this.parseIdent(true)
589
node.local = this.eatContextual("as") ? this.parseIdent() : node.imported
590
this.checkLVal(node.local, true)
591
nodes.push(this.finishNode(node, "ImportSpecifier"))
592
}
593
return nodes
594
}
595
596