1 // A recursive descent parser operates by defining functions for all
2 // syntactic elements, and recursively calling those, each function
3 // advancing the input stream and returning an AST node. Precedence
4 // of constructs (for example, the fact that `!x[1]` means `!(x[1])`
5 // instead of `(!x)[1]` is handled by the fact that the parser
6 // function that parses unary prefix operators is called first, and
7 // in turn calls the function that parses `[]` subscripts — that
8 // way, it'll receive the node for `x[1]` already parsed, and wraps
9 // *that* in the unary operator node.
10 //
11 // Acorn uses an [operator precedence parser][opp] to handle binary
12 // operator precedence, because it is much more compact than using
13 // the technique outlined above, which uses different, nesting
14 // functions to specify precedence, for all of the ten binary
15 // precedence levels that JavaScript defines.
16 //
17 // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
19 import {types as tt} from "./tokentype"
20 import {Parser} from "./state"
21 import {reservedWords} from "./identifier"
22 import {has} from "./util"
24 const pp = Parser.prototype
26 // Check if property name clashes with already added.
27 // Object/class getters and setters are not allowed to clash —
28 // either with each other or with an init property — and in
29 // strict mode, init properties are also not allowed to be repeated.
31 pp.checkPropClash = function(prop, propHash) {
32 if (this.options.ecmaVersion >= 6) return
33 let key = prop.key, name
34 switch (key.type) {
35 case "Identifier": name = key.name; break
36 case "Literal": name = String(key.value); break
37 default: return
38 }
39 let kind = prop.kind || "init", other
40 if (has(propHash, name)) {
41 other = propHash[name]
42 let isGetSet = kind !== "init"
43 if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))
44 this.raise(key.start, "Redefinition of property")
45 } else {
46 other = propHash[name] = {
47 init: false,
48 get: false,
49 set: false
50 }
51 }
52 other[kind] = true
53 }
55 // ### Expression parsing
57 // These nest, from the most general expression type at the top to
58 // 'atomic', nondivisible expression types at the bottom. Most of
59 // the functions will simply let the function(s) below them parse,
60 // and, *if* the syntactic construct they handle is present, wrap
61 // the AST node that the inner parser gave them in another node.
63 // Parse a full expression. The optional arguments are used to
64 // forbid the `in` operator (in for loops initalization expressions)
65 // and provide reference for storing '=' operator inside shorthand
66 // property assignment in contexts where both object expression
67 // and object pattern might appear (so it's possible to raise
68 // delayed syntax error at correct position).
70 pp.parseExpression = function(noIn, refShorthandDefaultPos) {
71 let startPos = this.start, startLoc = this.startLoc
72 let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos)
73 if (this.type === tt.comma) {
74 let node = this.startNodeAt(startPos, startLoc)
75 node.expressions = [expr]
76 while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos))
77 return this.finishNode(node, "SequenceExpression")
78 }
79 return expr
80 }
82 // Parse an assignment expression. This includes applications of
83 // operators like `+=`.
85 pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos, afterLeftParse) {
86 if (this.type == tt._yield && this.inGenerator) return this.parseYield()
88 let failOnShorthandAssign
89 if (!refShorthandDefaultPos) {
90 refShorthandDefaultPos = {start: 0}
91 failOnShorthandAssign = true
92 } else {
93 failOnShorthandAssign = false
94 }
95 let startPos = this.start, startLoc = this.startLoc
96 if (this.type == tt.parenL || this.type == tt.name)
97 this.potentialArrowAt = this.start
98 let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos)
99 if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
100 if (this.type.isAssign) {
101 let node = this.startNodeAt(startPos, startLoc)
102 node.operator = this.value
103 node.left = this.type === tt.eq ? this.toAssignable(left) : left
104 refShorthandDefaultPos.start = 0 // reset because shorthand default was used correctly
105 this.checkLVal(left)
106 this.next()
107 node.right = this.parseMaybeAssign(noIn)
108 return this.finishNode(node, "AssignmentExpression")
109 } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
110 this.unexpected(refShorthandDefaultPos.start)
111 }
112 return left
113 }
115 // Parse a ternary conditional (`?:`) operator.
117 pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) {
118 let startPos = this.start, startLoc = this.startLoc
119 let expr = this.parseExprOps(noIn, refShorthandDefaultPos)
120 if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
121 if (this.eat(tt.question)) {
122 let node = this.startNodeAt(startPos, startLoc)
123 node.test = expr
124 node.consequent = this.parseMaybeAssign()
125 this.expect(tt.colon)
126 node.alternate = this.parseMaybeAssign(noIn)
127 return this.finishNode(node, "ConditionalExpression")
128 }
129 return expr
130 }
132 // Start the precedence parser.
134 pp.parseExprOps = function(noIn, refShorthandDefaultPos) {
135 let startPos = this.start, startLoc = this.startLoc
136 let expr = this.parseMaybeUnary(refShorthandDefaultPos)
137 if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
138 return this.parseExprOp(expr, startPos, startLoc, -1, noIn)
139 }
141 // Parse binary operators with the operator precedence parsing
142 // algorithm. `left` is the left-hand side of the operator.
143 // `minPrec` provides context that allows the function to stop and
144 // defer further parser to one of its callers when it encounters an
145 // operator that has a lower precedence than the set it is parsing.
147 pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
148 let prec = this.type.binop
149 if (Array.isArray(leftStartPos)){
150 if (this.options.locations && noIn === undefined) {
151 // shift arguments to left by one
152 noIn = minPrec
153 minPrec = leftStartLoc
154 // flatten leftStartPos
155 leftStartLoc = leftStartPos[1]
156 leftStartPos = leftStartPos[0]
157 }
158 }
159 if (prec != null && (!noIn || this.type !== tt._in)) {
160 if (prec > minPrec) {
161 let node = this.startNodeAt(leftStartPos, leftStartLoc)
162 node.left = left
163 node.operator = this.value
164 let op = this.type
165 this.next()
166 let startPos = this.start, startLoc = this.startLoc
167 node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)
168 this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")
169 return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
170 }
171 }
172 return left
173 }
175 // Parse unary operators, both prefix and postfix.
177 pp.parseMaybeUnary = function(refShorthandDefaultPos) {
178 if (this.type.prefix) {
179 let node = this.startNode(), update = this.type === tt.incDec
180 node.operator = this.value
181 node.prefix = true
182 this.next()
183 node.argument = this.parseMaybeUnary()
184 if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
185 if (update) this.checkLVal(node.argument)
186 else if (this.strict && node.operator === "delete" &&
187 node.argument.type === "Identifier")
188 this.raise(node.start, "Deleting local variable in strict mode")
189 return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
190 }
191 let startPos = this.start, startLoc = this.startLoc
192 let expr = this.parseExprSubscripts(refShorthandDefaultPos)
193 if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
194 while (this.type.postfix && !this.canInsertSemicolon()) {
195 let node = this.startNodeAt(startPos, startLoc)
196 node.operator = this.value
197 node.prefix = false
198 node.argument = expr
199 this.checkLVal(expr)
200 this.next()
201 expr = this.finishNode(node, "UpdateExpression")
202 }
203 return expr
204 }
206 // Parse call, dot, and `[]`-subscript expressions.
208 pp.parseExprSubscripts = function(refShorthandDefaultPos) {
209 let startPos = this.start, startLoc = this.startLoc
210 let expr = this.parseExprAtom(refShorthandDefaultPos)
211 if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
212 return this.parseSubscripts(expr, startPos, startLoc)
213 }
215 pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
216 if (Array.isArray(startPos)){
217 if (this.options.locations && noCalls === undefined) {
218 // shift arguments to left by one
219 noCalls = startLoc
220 // flatten startPos
221 startLoc = startPos[1]
222 startPos = startPos[0]
223 }
224 }
225 for (;;) {
226 if (this.eat(tt.dot)) {
227 let node = this.startNodeAt(startPos, startLoc)
228 node.object = base
229 node.property = this.parseIdent(true)
230 node.computed = false
231 base = this.finishNode(node, "MemberExpression")
232 } else if (this.eat(tt.bracketL)) {
233 let node = this.startNodeAt(startPos, startLoc)
234 node.object = base
235 node.property = this.parseExpression()
236 node.computed = true
237 this.expect(tt.bracketR)
238 base = this.finishNode(node, "MemberExpression")
239 } else if (!noCalls && this.eat(tt.parenL)) {
240 let node = this.startNodeAt(startPos, startLoc)
241 node.callee = base
242 node.arguments = this.parseExprList(tt.parenR, false)
243 base = this.finishNode(node, "CallExpression")
244 } else if (this.type === tt.backQuote) {
245 let node = this.startNodeAt(startPos, startLoc)
246 node.tag = base
247 node.quasi = this.parseTemplate()
248 base = this.finishNode(node, "TaggedTemplateExpression")
249 } else {
250 return base
251 }
252 }
253 }
255 // Parse an atomic expression — either a single token that is an
256 // expression, an expression started by a keyword like `function` or
257 // `new`, or an expression wrapped in punctuation like `()`, `[]`,
258 // or `{}`.
260 pp.parseExprAtom = function(refShorthandDefaultPos) {
261 let node, canBeArrow = this.potentialArrowAt == this.start
262 switch (this.type) {
263 case tt._this:
264 case tt._super:
265 let type = this.type === tt._this ? "ThisExpression" : "Super"
266 node = this.startNode()
267 this.next()
268 return this.finishNode(node, type)
270 case tt._yield:
271 if (this.inGenerator) this.unexpected()
273 case tt.name:
274 let startPos = this.start, startLoc = this.startLoc
275 let id = this.parseIdent(this.type !== tt.name)
276 if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow))
277 return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id])
278 return id
280 case tt.regexp:
281 let value = this.value
282 node = this.parseLiteral(value.value)
283 node.regex = {pattern: value.pattern, flags: value.flags}
284 return node
286 case tt.num: case tt.string:
287 return this.parseLiteral(this.value)
289 case tt._null: case tt._true: case tt._false:
290 node = this.startNode()
291 node.value = this.type === tt._null ? null : this.type === tt._true
292 node.raw = this.type.keyword
293 this.next()
294 return this.finishNode(node, "Literal")
296 case tt.parenL:
297 return this.parseParenAndDistinguishExpression(canBeArrow)
299 case tt.bracketL:
300 node = this.startNode()
301 this.next()
302 // check whether this is array comprehension or regular array
303 if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
304 return this.parseComprehension(node, false)
305 }
306 node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)
307 return this.finishNode(node, "ArrayExpression")
309 case tt.braceL:
310 return this.parseObj(false, refShorthandDefaultPos)
312 case tt._function:
313 node = this.startNode()
314 this.next()
315 return this.parseFunction(node, false)
317 case tt._class:
318 return this.parseClass(this.startNode(), false)
320 case tt._new:
321 return this.parseNew()
323 case tt.backQuote:
324 return this.parseTemplate()
326 default:
327 this.unexpected()
328 }
329 }
331 pp.parseLiteral = function(value) {
332 let node = this.startNode()
333 node.value = value
334 node.raw = this.input.slice(this.start, this.end)
335 this.next()
336 return this.finishNode(node, "Literal")
337 }
339 pp.parseParenExpression = function() {
340 this.expect(tt.parenL)
341 let val = this.parseExpression()
342 this.expect(tt.parenR)
343 return val
344 }
346 pp.parseParenAndDistinguishExpression = function(canBeArrow) {
347 let startPos = this.start, startLoc = this.startLoc, val
348 if (this.options.ecmaVersion >= 6) {
349 this.next()
351 if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
352 return this.parseComprehension(this.startNodeAt(startPos, startLoc), true)
353 }
355 let innerStartPos = this.start, innerStartLoc = this.startLoc
356 let exprList = [], first = true
357 let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart
358 while (this.type !== tt.parenR) {
359 first ? first = false : this.expect(tt.comma)
360 if (this.type === tt.ellipsis) {
361 spreadStart = this.start
362 exprList.push(this.parseParenItem(this.parseRest()))
363 break
364 } else {
365 if (this.type === tt.parenL && !innerParenStart) {
366 innerParenStart = this.start
367 }
368 exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))
369 }
370 }
371 let innerEndPos = this.start, innerEndLoc = this.startLoc
372 this.expect(tt.parenR)
374 if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
375 if (innerParenStart) this.unexpected(innerParenStart)
376 return this.parseParenArrowList(startPos, startLoc, exprList)
377 }
379 if (!exprList.length) this.unexpected(this.lastTokStart)
380 if (spreadStart) this.unexpected(spreadStart)
381 if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
383 if (exprList.length > 1) {
384 val = this.startNodeAt(innerStartPos, innerStartLoc)
385 val.expressions = exprList
386 this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc)
387 } else {
388 val = exprList[0]
389 }
390 } else {
391 val = this.parseParenExpression()
392 }
394 if (this.options.preserveParens) {
395 let par = this.startNodeAt(startPos, startLoc)
396 par.expression = val
397 return this.finishNode(par, "ParenthesizedExpression")
398 } else {
399 return val
400 }
401 }
403 pp.parseParenItem = function(item) {
404 return item
405 }
407 pp.parseParenArrowList = function(startPos, startLoc, exprList) {
408 return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
409 }
411 // New's precedence is slightly tricky. It must allow its argument
412 // to be a `[]` or dot subscript expression, but not a call — at
413 // least, not without wrapping it in parentheses. Thus, it uses the
415 const empty = []
417 pp.parseNew = function() {
418 let node = this.startNode()
419 let meta = this.parseIdent(true)
420 if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
421 node.meta = meta
422 node.property = this.parseIdent(true)
423 if (node.property.name !== "target")
424 this.raise(node.property.start, "The only valid meta property for new is new.target")
425 return this.finishNode(node, "MetaProperty")
426 }
427 let startPos = this.start, startLoc = this.startLoc
428 node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)
429 if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)
430 else node.arguments = empty
431 return this.finishNode(node, "NewExpression")
432 }
434 // Parse template expression.
436 pp.parseTemplateElement = function() {
437 let elem = this.startNode()
438 elem.value = {
439 raw: this.input.slice(this.start, this.end),
440 cooked: this.value
441 }
442 this.next()
443 elem.tail = this.type === tt.backQuote
444 return this.finishNode(elem, "TemplateElement")
445 }
447 pp.parseTemplate = function() {
448 let node = this.startNode()
449 this.next()
450 node.expressions = []
451 let curElt = this.parseTemplateElement()
452 node.quasis = [curElt]
453 while (!curElt.tail) {
454 this.expect(tt.dollarBraceL)
455 node.expressions.push(this.parseExpression())
456 this.expect(tt.braceR)
457 node.quasis.push(curElt = this.parseTemplateElement())
458 }
459 this.next()
460 return this.finishNode(node, "TemplateLiteral")
461 }
463 // Parse an object literal or binding pattern.
465 pp.parseObj = function(isPattern, refShorthandDefaultPos) {
466 let node = this.startNode(), first = true, propHash = {}
467 node.properties = []
468 this.next()
469 while (!this.eat(tt.braceR)) {
470 if (!first) {
471 this.expect(tt.comma)
472 if (this.afterTrailingComma(tt.braceR)) break
473 } else first = false
475 let prop = this.startNode(), isGenerator, startPos, startLoc
476 if (this.options.ecmaVersion >= 6) {
477 prop.method = false
478 prop.shorthand = false
479 if (isPattern || refShorthandDefaultPos) {
480 startPos = this.start
481 startLoc = this.startLoc
482 }
483 if (!isPattern)
484 isGenerator = this.eat(tt.star)
485 }
486 this.parsePropertyName(prop)
487 this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos)
488 this.checkPropClash(prop, propHash)
489 node.properties.push(this.finishNode(prop, "Property"))
490 }
491 return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
492 }
494 pp.parsePropertyValue = function(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {
495 if (this.eat(tt.colon)) {
496 prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos)
497 prop.kind = "init"
498 } else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {
499 if (isPattern) this.unexpected()
500 prop.kind = "init"
501 prop.method = true
502 prop.value = this.parseMethod(isGenerator)
503 } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
504 (prop.key.name === "get" || prop.key.name === "set") &&
505 (this.type != tt.comma && this.type != tt.braceR)) {
506 if (isGenerator || isPattern) this.unexpected()
507 prop.kind = prop.key.name
508 this.parsePropertyName(prop)
509 prop.value = this.parseMethod(false)
510 } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
511 prop.kind = "init"
512 if (isPattern) {
513 if (this.isKeyword(prop.key.name) ||
514 (this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||
515 (!this.options.allowReserved && this.isReservedWord(prop.key.name)))
516 this.raise(prop.key.start, "Binding " + prop.key.name)
517 prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
518 } else if (this.type === tt.eq && refShorthandDefaultPos) {
519 if (!refShorthandDefaultPos.start)
520 refShorthandDefaultPos.start = this.start
521 prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
522 } else {
523 prop.value = prop.key
524 }
525 prop.shorthand = true
526 } else this.unexpected()
527 }
529 pp.parsePropertyName = function(prop) {
530 if (this.options.ecmaVersion >= 6) {
531 if (this.eat(tt.bracketL)) {
532 prop.computed = true
533 prop.key = this.parseMaybeAssign()
534 this.expect(tt.bracketR)
535 return prop.key
536 } else {
537 prop.computed = false
538 }
539 }
540 return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)
541 }
543 // Initialize empty function node.
545 pp.initFunction = function(node) {
546 node.id = null
547 if (this.options.ecmaVersion >= 6) {
548 node.generator = false
549 node.expression = false
550 }
551 }
553 // Parse object or class method.
555 pp.parseMethod = function(isGenerator) {
556 let node = this.startNode()
557 this.initFunction(node)
558 this.expect(tt.parenL)
559 node.params = this.parseBindingList(tt.parenR, false, false)
560 let allowExpressionBody
561 if (this.options.ecmaVersion >= 6) {
562 node.generator = isGenerator
563 allowExpressionBody = true
564 } else {
565 allowExpressionBody = false
566 }
567 this.parseFunctionBody(node, allowExpressionBody)
568 return this.finishNode(node, "FunctionExpression")
569 }
571 // Parse arrow function expression with given parameters.
573 pp.parseArrowExpression = function(node, params) {
574 this.initFunction(node)
575 node.params = this.toAssignableList(params, true)
576 this.parseFunctionBody(node, true)
577 return this.finishNode(node, "ArrowFunctionExpression")
578 }
580 // Parse function body and check parameters.
582 pp.parseFunctionBody = function(node, allowExpression) {
583 let isExpression = allowExpression && this.type !== tt.braceL
585 if (isExpression) {
586 node.body = this.parseMaybeAssign()
587 node.expression = true
588 } else {
589 // Start a new scope with regard to labels and the `inFunction`
590 // flag (restore them to their old value afterwards).
591 let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels
592 this.inFunction = true; this.inGenerator = node.generator; this.labels = []
593 node.body = this.parseBlock(true)
594 node.expression = false
595 this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels
596 }
598 // If this is a strict mode function, verify that argument names
599 // are not repeated, and it does not try to bind the words `eval`
600 // or `arguments`.
601 if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
602 let nameHash = {}, oldStrict = this.strict
603 this.strict = true
604 if (node.id)
605 this.checkLVal(node.id, true)
606 for (let i = 0; i < node.params.length; i++)
607 this.checkLVal(node.params[i], true, nameHash)
608 this.strict = oldStrict
609 }
610 }
612 // Parses a comma-separated list of expressions, and returns them as
613 // an array. `close` is the token type that ends the list, and
614 // `allowEmpty` can be turned on to allow subsequent commas with
615 // nothing in between them to be parsed as `null` (which is needed
616 // for array literals).
618 pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
619 let elts = [], first = true
620 while (!this.eat(close)) {
621 if (!first) {
622 this.expect(tt.comma)
623 if (allowTrailingComma && this.afterTrailingComma(close)) break
624 } else first = false
626 if (allowEmpty && this.type === tt.comma) {
627 elts.push(null)
628 } else {
629 if (this.type === tt.ellipsis)
630 elts.push(this.parseSpread(refShorthandDefaultPos))
631 else
632 elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos))
633 }
634 }
635 return elts
636 }
638 // Parse the next token as an identifier. If `liberal` is true (used
639 // when parsing properties), it will also convert keywords into
640 // identifiers.
642 pp.parseIdent = function(liberal) {
643 let node = this.startNode()
644 if (liberal && this.options.allowReserved == "never") liberal = false
645 if (this.type === tt.name) {
646 if (!liberal &&
647 ((!this.options.allowReserved && this.isReservedWord(this.value)) ||
648 (this.strict && reservedWords.strict(this.value)) &&
649 (this.options.ecmaVersion >= 6 ||
650 this.input.slice(this.start, this.end).indexOf("\\") == -1)))
651 this.raise(this.start, "The keyword '" + this.value + "' is reserved")
652 node.name = this.value
653 } else if (liberal && this.type.keyword) {
654 node.name = this.type.keyword
655 } else {
656 this.unexpected()
657 }
658 this.next()
659 return this.finishNode(node, "Identifier")
660 }
662 // Parses yield expression inside generator.
664 pp.parseYield = function() {
665 let node = this.startNode()
666 this.next()
667 if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {
668 node.delegate = false
669 node.argument = null
670 } else {
671 node.delegate = this.eat(tt.star)
672 node.argument = this.parseMaybeAssign()
673 }
674 return this.finishNode(node, "YieldExpression")
675 }
677 // Parses array and generator comprehensions.
679 pp.parseComprehension = function(node, isGenerator) {
680 node.blocks = []
681 while (this.type === tt._for) {
682 let block = this.startNode()
683 this.next()
684 this.expect(tt.parenL)
685 block.left = this.parseBindingAtom()
686 this.checkLVal(block.left, true)
687 this.expectContextual("of")
688 block.right = this.parseExpression()
689 this.expect(tt.parenR)
690 node.blocks.push(this.finishNode(block, "ComprehensionBlock"))
691 }
692 node.filter = this.eat(tt._if) ? this.parseParenExpression() : null
693 node.body = this.parseExpression()
694 this.expect(isGenerator ? tt.parenR : tt.bracketR)
695 node.generator = isGenerator
696 return this.finishNode(node, "ComprehensionExpression")
697 }