|
1 // Acorn: Loose parser |
|
2 // |
|
3 // This module provides an alternative parser (`parse_dammit`) that |
|
4 // exposes that same interface as `parse`, but will try to parse |
|
5 // anything as JavaScript, repairing syntax error the best it can. |
|
6 // There are circumstances in which it will raise an error and give |
|
7 // up, but they are very rare. The resulting AST will be a mostly |
|
8 // valid JavaScript AST (as per the [Mozilla parser API][api], except |
|
9 // that: |
|
10 // |
|
11 // - Return outside functions is allowed |
|
12 // |
|
13 // - Label consistency (no conflicts, break only to existing labels) |
|
14 // is not enforced. |
|
15 // |
|
16 // - Bogus Identifier nodes with a name of `"✖"` are inserted whenever |
|
17 // the parser got too confused to return anything meaningful. |
|
18 // |
|
19 // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API |
|
20 // |
|
21 // The expected use for this is to *first* try `acorn.parse`, and only |
|
22 // if that fails switch to `parse_dammit`. The loose parser might |
|
23 // parse badly indented code incorrectly, so **don't** use it as |
|
24 // your default parser. |
|
25 // |
|
26 // Quite a lot of acorn.js is duplicated here. The alternative was to |
|
27 // add a *lot* of extra cruft to that file, making it less readable |
|
28 // and slower. Copying and editing the code allowed me to make |
|
29 // invasive changes and simplifications without creating a complicated |
|
30 // tangle. |
|
31 |
|
32 (function(root, mod) { |
|
33 if (typeof exports == "object" && typeof module == "object") return mod(exports, require("./acorn")); // CommonJS |
|
34 if (typeof define == "function" && define.amd) return define(["exports", "./acorn"], mod); // AMD |
|
35 mod(root.acorn || (root.acorn = {}), root.acorn); // Plain browser env |
|
36 })(this, function(exports, acorn) { |
|
37 "use strict"; |
|
38 |
|
39 var tt = acorn.tokTypes; |
|
40 |
|
41 var options, input, fetchToken, context; |
|
42 |
|
43 exports.parse_dammit = function(inpt, opts) { |
|
44 if (!opts) opts = {}; |
|
45 input = String(inpt); |
|
46 options = opts; |
|
47 if (!opts.tabSize) opts.tabSize = 4; |
|
48 fetchToken = acorn.tokenize(inpt, opts); |
|
49 sourceFile = options.sourceFile || null; |
|
50 context = []; |
|
51 nextLineStart = 0; |
|
52 ahead.length = 0; |
|
53 next(); |
|
54 return parseTopLevel(); |
|
55 }; |
|
56 |
|
57 var lastEnd, token = {start: 0, end: 0}, ahead = []; |
|
58 var curLineStart, nextLineStart, curIndent, lastEndLoc, sourceFile; |
|
59 |
|
60 function next() { |
|
61 lastEnd = token.end; |
|
62 if (options.locations) |
|
63 lastEndLoc = token.endLoc; |
|
64 |
|
65 if (ahead.length) |
|
66 token = ahead.shift(); |
|
67 else |
|
68 token = readToken(); |
|
69 |
|
70 if (token.start >= nextLineStart) { |
|
71 while (token.start >= nextLineStart) { |
|
72 curLineStart = nextLineStart; |
|
73 nextLineStart = lineEnd(curLineStart) + 1; |
|
74 } |
|
75 curIndent = indentationAfter(curLineStart); |
|
76 } |
|
77 } |
|
78 |
|
79 function readToken() { |
|
80 for (;;) { |
|
81 try { |
|
82 return fetchToken(); |
|
83 } catch(e) { |
|
84 if (!(e instanceof SyntaxError)) throw e; |
|
85 |
|
86 // Try to skip some text, based on the error message, and then continue |
|
87 var msg = e.message, pos = e.raisedAt, replace = true; |
|
88 if (/unterminated/i.test(msg)) { |
|
89 pos = lineEnd(e.pos); |
|
90 if (/string/.test(msg)) { |
|
91 replace = {start: e.pos, end: pos, type: tt.string, value: input.slice(e.pos + 1, pos)}; |
|
92 } else if (/regular expr/i.test(msg)) { |
|
93 var re = input.slice(e.pos, pos); |
|
94 try { re = new RegExp(re); } catch(e) {} |
|
95 replace = {start: e.pos, end: pos, type: tt.regexp, value: re}; |
|
96 } else { |
|
97 replace = false; |
|
98 } |
|
99 } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) { |
|
100 while (pos < input.length && !isSpace(input.charCodeAt(pos))) ++pos; |
|
101 } else if (/character escape|expected hexadecimal/i.test(msg)) { |
|
102 while (pos < input.length) { |
|
103 var ch = input.charCodeAt(pos++); |
|
104 if (ch === 34 || ch === 39 || isNewline(ch)) break; |
|
105 } |
|
106 } else if (/unexpected character/i.test(msg)) { |
|
107 pos++; |
|
108 replace = false; |
|
109 } else { |
|
110 throw e; |
|
111 } |
|
112 resetTo(pos); |
|
113 if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}; |
|
114 if (replace) { |
|
115 if (options.locations) { |
|
116 replace.startLoc = acorn.getLineInfo(input, replace.start); |
|
117 replace.endLoc = acorn.getLineInfo(input, replace.end); |
|
118 } |
|
119 return replace; |
|
120 } |
|
121 } |
|
122 } |
|
123 } |
|
124 |
|
125 function resetTo(pos) { |
|
126 var ch = input.charAt(pos - 1); |
|
127 var reAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || |
|
128 /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(input.slice(pos - 10, pos)); |
|
129 fetchToken.jumpTo(pos, reAllowed); |
|
130 } |
|
131 |
|
132 function copyToken(token) { |
|
133 var copy = {start: token.start, end: token.end, type: token.type, value: token.value}; |
|
134 if (options.locations) { |
|
135 copy.startLoc = token.startLoc; |
|
136 copy.endLoc = token.endLoc; |
|
137 } |
|
138 return copy; |
|
139 } |
|
140 |
|
141 function lookAhead(n) { |
|
142 // Copy token objects, because fetchToken will overwrite the one |
|
143 // it returns, and in this case we still need it |
|
144 if (!ahead.length) |
|
145 token = copyToken(token); |
|
146 while (n > ahead.length) |
|
147 ahead.push(copyToken(readToken())); |
|
148 return ahead[n-1]; |
|
149 } |
|
150 |
|
151 var newline = /[\n\r\u2028\u2029]/; |
|
152 |
|
153 function isNewline(ch) { |
|
154 return ch === 10 || ch === 13 || ch === 8232 || ch === 8329; |
|
155 } |
|
156 function isSpace(ch) { |
|
157 return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewline(ch); |
|
158 } |
|
159 |
|
160 function pushCx() { |
|
161 context.push(curIndent); |
|
162 } |
|
163 function popCx() { |
|
164 curIndent = context.pop(); |
|
165 } |
|
166 |
|
167 function lineEnd(pos) { |
|
168 while (pos < input.length && !isNewline(input.charCodeAt(pos))) ++pos; |
|
169 return pos; |
|
170 } |
|
171 function indentationAfter(pos) { |
|
172 for (var count = 0;; ++pos) { |
|
173 var ch = input.charCodeAt(pos); |
|
174 if (ch === 32) ++count; |
|
175 else if (ch === 9) count += options.tabSize; |
|
176 else return count; |
|
177 } |
|
178 } |
|
179 |
|
180 function closes(closeTok, indent, line, blockHeuristic) { |
|
181 if (token.type === closeTok || token.type === tt.eof) return true; |
|
182 if (line != curLineStart && curIndent < indent && tokenStartsLine() && |
|
183 (!blockHeuristic || nextLineStart >= input.length || |
|
184 indentationAfter(nextLineStart) < indent)) return true; |
|
185 return false; |
|
186 } |
|
187 |
|
188 function tokenStartsLine() { |
|
189 for (var p = token.start - 1; p >= curLineStart; --p) { |
|
190 var ch = input.charCodeAt(p); |
|
191 if (ch !== 9 && ch !== 32) return false; |
|
192 } |
|
193 return true; |
|
194 } |
|
195 |
|
196 function node_t(start) { |
|
197 this.type = null; |
|
198 this.start = start; |
|
199 this.end = null; |
|
200 } |
|
201 |
|
202 function node_loc_t(start) { |
|
203 this.start = start || token.startLoc || {line: 1, column: 0}; |
|
204 this.end = null; |
|
205 if (sourceFile !== null) this.source = sourceFile; |
|
206 } |
|
207 |
|
208 function startNode() { |
|
209 var node = new node_t(token.start); |
|
210 if (options.locations) |
|
211 node.loc = new node_loc_t(); |
|
212 if (options.directSourceFile) |
|
213 node.sourceFile = options.directSourceFile; |
|
214 return node; |
|
215 } |
|
216 |
|
217 function startNodeFrom(other) { |
|
218 var node = new node_t(other.start); |
|
219 if (options.locations) |
|
220 node.loc = new node_loc_t(other.loc.start); |
|
221 return node; |
|
222 } |
|
223 |
|
224 function finishNode(node, type) { |
|
225 node.type = type; |
|
226 node.end = lastEnd; |
|
227 if (options.locations) |
|
228 node.loc.end = lastEndLoc; |
|
229 return node; |
|
230 } |
|
231 |
|
232 function getDummyLoc() { |
|
233 if (options.locations) { |
|
234 var loc = new node_loc_t(); |
|
235 loc.end = loc.start; |
|
236 return loc; |
|
237 } |
|
238 }; |
|
239 |
|
240 function dummyIdent() { |
|
241 var dummy = new node_t(token.start); |
|
242 dummy.type = "Identifier"; |
|
243 dummy.end = token.start; |
|
244 dummy.name = "✖"; |
|
245 dummy.loc = getDummyLoc(); |
|
246 return dummy; |
|
247 } |
|
248 function isDummy(node) { return node.name == "✖"; } |
|
249 |
|
250 function eat(type) { |
|
251 if (token.type === type) { |
|
252 next(); |
|
253 return true; |
|
254 } |
|
255 } |
|
256 |
|
257 function canInsertSemicolon() { |
|
258 return (token.type === tt.eof || token.type === tt.braceR || newline.test(input.slice(lastEnd, token.start))); |
|
259 } |
|
260 function semicolon() { |
|
261 eat(tt.semi); |
|
262 } |
|
263 |
|
264 function expect(type) { |
|
265 if (eat(type)) return true; |
|
266 if (lookAhead(1).type == type) { |
|
267 next(); next(); |
|
268 return true; |
|
269 } |
|
270 if (lookAhead(2).type == type) { |
|
271 next(); next(); next(); |
|
272 return true; |
|
273 } |
|
274 } |
|
275 |
|
276 function checkLVal(expr) { |
|
277 if (expr.type === "Identifier" || expr.type === "MemberExpression") return expr; |
|
278 return dummyIdent(); |
|
279 } |
|
280 |
|
281 function parseTopLevel() { |
|
282 var node = startNode(); |
|
283 node.body = []; |
|
284 while (token.type !== tt.eof) node.body.push(parseStatement()); |
|
285 return finishNode(node, "Program"); |
|
286 } |
|
287 |
|
288 function parseStatement() { |
|
289 var starttype = token.type, node = startNode(); |
|
290 |
|
291 switch (starttype) { |
|
292 case tt._break: case tt._continue: |
|
293 next(); |
|
294 var isBreak = starttype === tt._break; |
|
295 node.label = token.type === tt.name ? parseIdent() : null; |
|
296 semicolon(); |
|
297 return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); |
|
298 |
|
299 case tt._debugger: |
|
300 next(); |
|
301 semicolon(); |
|
302 return finishNode(node, "DebuggerStatement"); |
|
303 |
|
304 case tt._do: |
|
305 next(); |
|
306 node.body = parseStatement(); |
|
307 node.test = eat(tt._while) ? parseParenExpression() : dummyIdent(); |
|
308 semicolon(); |
|
309 return finishNode(node, "DoWhileStatement"); |
|
310 |
|
311 case tt._for: |
|
312 next(); |
|
313 pushCx(); |
|
314 expect(tt.parenL); |
|
315 if (token.type === tt.semi) return parseFor(node, null); |
|
316 if (token.type === tt._var) { |
|
317 var init = startNode(); |
|
318 next(); |
|
319 parseVar(init, true); |
|
320 if (init.declarations.length === 1 && eat(tt._in)) |
|
321 return parseForIn(node, init); |
|
322 return parseFor(node, init); |
|
323 } |
|
324 var init = parseExpression(false, true); |
|
325 if (eat(tt._in)) {return parseForIn(node, checkLVal(init));} |
|
326 return parseFor(node, init); |
|
327 |
|
328 case tt._function: |
|
329 next(); |
|
330 return parseFunction(node, true); |
|
331 |
|
332 case tt._if: |
|
333 next(); |
|
334 node.test = parseParenExpression(); |
|
335 node.consequent = parseStatement(); |
|
336 node.alternate = eat(tt._else) ? parseStatement() : null; |
|
337 return finishNode(node, "IfStatement"); |
|
338 |
|
339 case tt._return: |
|
340 next(); |
|
341 if (eat(tt.semi) || canInsertSemicolon()) node.argument = null; |
|
342 else { node.argument = parseExpression(); semicolon(); } |
|
343 return finishNode(node, "ReturnStatement"); |
|
344 |
|
345 case tt._switch: |
|
346 var blockIndent = curIndent, line = curLineStart; |
|
347 next(); |
|
348 node.discriminant = parseParenExpression(); |
|
349 node.cases = []; |
|
350 pushCx(); |
|
351 expect(tt.braceL); |
|
352 |
|
353 for (var cur; !closes(tt.braceR, blockIndent, line, true);) { |
|
354 if (token.type === tt._case || token.type === tt._default) { |
|
355 var isCase = token.type === tt._case; |
|
356 if (cur) finishNode(cur, "SwitchCase"); |
|
357 node.cases.push(cur = startNode()); |
|
358 cur.consequent = []; |
|
359 next(); |
|
360 if (isCase) cur.test = parseExpression(); |
|
361 else cur.test = null; |
|
362 expect(tt.colon); |
|
363 } else { |
|
364 if (!cur) { |
|
365 node.cases.push(cur = startNode()); |
|
366 cur.consequent = []; |
|
367 cur.test = null; |
|
368 } |
|
369 cur.consequent.push(parseStatement()); |
|
370 } |
|
371 } |
|
372 if (cur) finishNode(cur, "SwitchCase"); |
|
373 popCx(); |
|
374 eat(tt.braceR); |
|
375 return finishNode(node, "SwitchStatement"); |
|
376 |
|
377 case tt._throw: |
|
378 next(); |
|
379 node.argument = parseExpression(); |
|
380 semicolon(); |
|
381 return finishNode(node, "ThrowStatement"); |
|
382 |
|
383 case tt._try: |
|
384 next(); |
|
385 node.block = parseBlock(); |
|
386 node.handler = null; |
|
387 if (token.type === tt._catch) { |
|
388 var clause = startNode(); |
|
389 next(); |
|
390 expect(tt.parenL); |
|
391 clause.param = parseIdent(); |
|
392 expect(tt.parenR); |
|
393 clause.guard = null; |
|
394 clause.body = parseBlock(); |
|
395 node.handler = finishNode(clause, "CatchClause"); |
|
396 } |
|
397 node.finalizer = eat(tt._finally) ? parseBlock() : null; |
|
398 if (!node.handler && !node.finalizer) return node.block; |
|
399 return finishNode(node, "TryStatement"); |
|
400 |
|
401 case tt._var: |
|
402 next(); |
|
403 node = parseVar(node); |
|
404 semicolon(); |
|
405 return node; |
|
406 |
|
407 case tt._while: |
|
408 next(); |
|
409 node.test = parseParenExpression(); |
|
410 node.body = parseStatement(); |
|
411 return finishNode(node, "WhileStatement"); |
|
412 |
|
413 case tt._with: |
|
414 next(); |
|
415 node.object = parseParenExpression(); |
|
416 node.body = parseStatement(); |
|
417 return finishNode(node, "WithStatement"); |
|
418 |
|
419 case tt.braceL: |
|
420 return parseBlock(); |
|
421 |
|
422 case tt.semi: |
|
423 next(); |
|
424 return finishNode(node, "EmptyStatement"); |
|
425 |
|
426 default: |
|
427 var expr = parseExpression(); |
|
428 if (isDummy(expr)) { |
|
429 next(); |
|
430 if (token.type === tt.eof) return finishNode(node, "EmptyStatement"); |
|
431 return parseStatement(); |
|
432 } else if (starttype === tt.name && expr.type === "Identifier" && eat(tt.colon)) { |
|
433 node.body = parseStatement(); |
|
434 node.label = expr; |
|
435 return finishNode(node, "LabeledStatement"); |
|
436 } else { |
|
437 node.expression = expr; |
|
438 semicolon(); |
|
439 return finishNode(node, "ExpressionStatement"); |
|
440 } |
|
441 } |
|
442 } |
|
443 |
|
444 function parseBlock() { |
|
445 var node = startNode(); |
|
446 pushCx(); |
|
447 expect(tt.braceL); |
|
448 var blockIndent = curIndent, line = curLineStart; |
|
449 node.body = []; |
|
450 while (!closes(tt.braceR, blockIndent, line, true)) |
|
451 node.body.push(parseStatement()); |
|
452 popCx(); |
|
453 eat(tt.braceR); |
|
454 return finishNode(node, "BlockStatement"); |
|
455 } |
|
456 |
|
457 function parseFor(node, init) { |
|
458 node.init = init; |
|
459 node.test = node.update = null; |
|
460 if (eat(tt.semi) && token.type !== tt.semi) node.test = parseExpression(); |
|
461 if (eat(tt.semi) && token.type !== tt.parenR) node.update = parseExpression(); |
|
462 popCx(); |
|
463 expect(tt.parenR); |
|
464 node.body = parseStatement(); |
|
465 return finishNode(node, "ForStatement"); |
|
466 } |
|
467 |
|
468 function parseForIn(node, init) { |
|
469 node.left = init; |
|
470 node.right = parseExpression(); |
|
471 popCx(); |
|
472 expect(tt.parenR); |
|
473 node.body = parseStatement(); |
|
474 return finishNode(node, "ForInStatement"); |
|
475 } |
|
476 |
|
477 function parseVar(node, noIn) { |
|
478 node.declarations = []; |
|
479 node.kind = "var"; |
|
480 while (token.type === tt.name) { |
|
481 var decl = startNode(); |
|
482 decl.id = parseIdent(); |
|
483 decl.init = eat(tt.eq) ? parseExpression(true, noIn) : null; |
|
484 node.declarations.push(finishNode(decl, "VariableDeclarator")); |
|
485 if (!eat(tt.comma)) break; |
|
486 } |
|
487 if (!node.declarations.length) { |
|
488 var decl = startNode(); |
|
489 decl.id = dummyIdent(); |
|
490 node.declarations.push(finishNode(decl, "VariableDeclarator")); |
|
491 } |
|
492 return finishNode(node, "VariableDeclaration"); |
|
493 } |
|
494 |
|
495 function parseExpression(noComma, noIn) { |
|
496 var expr = parseMaybeAssign(noIn); |
|
497 if (!noComma && token.type === tt.comma) { |
|
498 var node = startNodeFrom(expr); |
|
499 node.expressions = [expr]; |
|
500 while (eat(tt.comma)) node.expressions.push(parseMaybeAssign(noIn)); |
|
501 return finishNode(node, "SequenceExpression"); |
|
502 } |
|
503 return expr; |
|
504 } |
|
505 |
|
506 function parseParenExpression() { |
|
507 pushCx(); |
|
508 expect(tt.parenL); |
|
509 var val = parseExpression(); |
|
510 popCx(); |
|
511 expect(tt.parenR); |
|
512 return val; |
|
513 } |
|
514 |
|
515 function parseMaybeAssign(noIn) { |
|
516 var left = parseMaybeConditional(noIn); |
|
517 if (token.type.isAssign) { |
|
518 var node = startNodeFrom(left); |
|
519 node.operator = token.value; |
|
520 node.left = checkLVal(left); |
|
521 next(); |
|
522 node.right = parseMaybeAssign(noIn); |
|
523 return finishNode(node, "AssignmentExpression"); |
|
524 } |
|
525 return left; |
|
526 } |
|
527 |
|
528 function parseMaybeConditional(noIn) { |
|
529 var expr = parseExprOps(noIn); |
|
530 if (eat(tt.question)) { |
|
531 var node = startNodeFrom(expr); |
|
532 node.test = expr; |
|
533 node.consequent = parseExpression(true); |
|
534 node.alternate = expect(tt.colon) ? parseExpression(true, noIn) : dummyIdent(); |
|
535 return finishNode(node, "ConditionalExpression"); |
|
536 } |
|
537 return expr; |
|
538 } |
|
539 |
|
540 function parseExprOps(noIn) { |
|
541 var indent = curIndent, line = curLineStart; |
|
542 return parseExprOp(parseMaybeUnary(noIn), -1, noIn, indent, line); |
|
543 } |
|
544 |
|
545 function parseExprOp(left, minPrec, noIn, indent, line) { |
|
546 if (curLineStart != line && curIndent < indent && tokenStartsLine()) return left; |
|
547 var prec = token.type.binop; |
|
548 if (prec != null && (!noIn || token.type !== tt._in)) { |
|
549 if (prec > minPrec) { |
|
550 var node = startNodeFrom(left); |
|
551 node.left = left; |
|
552 node.operator = token.value; |
|
553 next(); |
|
554 if (curLineStart != line && curIndent < indent && tokenStartsLine()) |
|
555 node.right = dummyIdent(); |
|
556 else |
|
557 node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn, indent, line); |
|
558 var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression"); |
|
559 return parseExprOp(node, minPrec, noIn, indent, line); |
|
560 } |
|
561 } |
|
562 return left; |
|
563 } |
|
564 |
|
565 function parseMaybeUnary(noIn) { |
|
566 if (token.type.prefix) { |
|
567 var node = startNode(), update = token.type.isUpdate; |
|
568 node.operator = token.value; |
|
569 node.prefix = true; |
|
570 next(); |
|
571 node.argument = parseMaybeUnary(noIn); |
|
572 if (update) node.argument = checkLVal(node.argument); |
|
573 return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); |
|
574 } |
|
575 var expr = parseExprSubscripts(); |
|
576 while (token.type.postfix && !canInsertSemicolon()) { |
|
577 var node = startNodeFrom(expr); |
|
578 node.operator = token.value; |
|
579 node.prefix = false; |
|
580 node.argument = checkLVal(expr); |
|
581 next(); |
|
582 expr = finishNode(node, "UpdateExpression"); |
|
583 } |
|
584 return expr; |
|
585 } |
|
586 |
|
587 function parseExprSubscripts() { |
|
588 return parseSubscripts(parseExprAtom(), false, curIndent, curLineStart); |
|
589 } |
|
590 |
|
591 function parseSubscripts(base, noCalls, startIndent, line) { |
|
592 for (;;) { |
|
593 if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) { |
|
594 if (token.type == tt.dot && curIndent == startIndent) |
|
595 --startIndent; |
|
596 else |
|
597 return base; |
|
598 } |
|
599 |
|
600 if (eat(tt.dot)) { |
|
601 var node = startNodeFrom(base); |
|
602 node.object = base; |
|
603 if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) |
|
604 node.property = dummyIdent(); |
|
605 else |
|
606 node.property = parsePropertyName() || dummyIdent(); |
|
607 node.computed = false; |
|
608 base = finishNode(node, "MemberExpression"); |
|
609 } else if (token.type == tt.bracketL) { |
|
610 pushCx(); |
|
611 next(); |
|
612 var node = startNodeFrom(base); |
|
613 node.object = base; |
|
614 node.property = parseExpression(); |
|
615 node.computed = true; |
|
616 popCx(); |
|
617 expect(tt.bracketR); |
|
618 base = finishNode(node, "MemberExpression"); |
|
619 } else if (!noCalls && token.type == tt.parenL) { |
|
620 pushCx(); |
|
621 var node = startNodeFrom(base); |
|
622 node.callee = base; |
|
623 node.arguments = parseExprList(tt.parenR); |
|
624 base = finishNode(node, "CallExpression"); |
|
625 } else { |
|
626 return base; |
|
627 } |
|
628 } |
|
629 } |
|
630 |
|
631 function parseExprAtom() { |
|
632 switch (token.type) { |
|
633 case tt._this: |
|
634 var node = startNode(); |
|
635 next(); |
|
636 return finishNode(node, "ThisExpression"); |
|
637 case tt.name: |
|
638 return parseIdent(); |
|
639 case tt.num: case tt.string: case tt.regexp: |
|
640 var node = startNode(); |
|
641 node.value = token.value; |
|
642 node.raw = input.slice(token.start, token.end); |
|
643 next(); |
|
644 return finishNode(node, "Literal"); |
|
645 |
|
646 case tt._null: case tt._true: case tt._false: |
|
647 var node = startNode(); |
|
648 node.value = token.type.atomValue; |
|
649 node.raw = token.type.keyword; |
|
650 next(); |
|
651 return finishNode(node, "Literal"); |
|
652 |
|
653 case tt.parenL: |
|
654 var tokStart1 = token.start; |
|
655 next(); |
|
656 var val = parseExpression(); |
|
657 val.start = tokStart1; |
|
658 val.end = token.end; |
|
659 expect(tt.parenR); |
|
660 return val; |
|
661 |
|
662 case tt.bracketL: |
|
663 var node = startNode(); |
|
664 pushCx(); |
|
665 node.elements = parseExprList(tt.bracketR); |
|
666 return finishNode(node, "ArrayExpression"); |
|
667 |
|
668 case tt.braceL: |
|
669 return parseObj(); |
|
670 |
|
671 case tt._function: |
|
672 var node = startNode(); |
|
673 next(); |
|
674 return parseFunction(node, false); |
|
675 |
|
676 case tt._new: |
|
677 return parseNew(); |
|
678 |
|
679 default: |
|
680 return dummyIdent(); |
|
681 } |
|
682 } |
|
683 |
|
684 function parseNew() { |
|
685 var node = startNode(), startIndent = curIndent, line = curLineStart; |
|
686 next(); |
|
687 node.callee = parseSubscripts(parseExprAtom(), true, startIndent, line); |
|
688 if (token.type == tt.parenL) { |
|
689 pushCx(); |
|
690 node.arguments = parseExprList(tt.parenR); |
|
691 } else { |
|
692 node.arguments = []; |
|
693 } |
|
694 return finishNode(node, "NewExpression"); |
|
695 } |
|
696 |
|
697 function parseObj() { |
|
698 var node = startNode(); |
|
699 node.properties = []; |
|
700 pushCx(); |
|
701 next(); |
|
702 var propIndent = curIndent, line = curLineStart; |
|
703 while (!closes(tt.braceR, propIndent, line)) { |
|
704 var name = parsePropertyName(); |
|
705 if (!name) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; } |
|
706 var prop = {key: name}, isGetSet = false, kind; |
|
707 if (eat(tt.colon)) { |
|
708 prop.value = parseExpression(true); |
|
709 kind = prop.kind = "init"; |
|
710 } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" && |
|
711 (prop.key.name === "get" || prop.key.name === "set")) { |
|
712 isGetSet = true; |
|
713 kind = prop.kind = prop.key.name; |
|
714 prop.key = parsePropertyName() || dummyIdent(); |
|
715 prop.value = parseFunction(startNode(), false); |
|
716 } else { |
|
717 next(); |
|
718 eat(tt.comma); |
|
719 continue; |
|
720 } |
|
721 |
|
722 node.properties.push(prop); |
|
723 eat(tt.comma); |
|
724 } |
|
725 popCx(); |
|
726 eat(tt.braceR); |
|
727 return finishNode(node, "ObjectExpression"); |
|
728 } |
|
729 |
|
730 function parsePropertyName() { |
|
731 if (token.type === tt.num || token.type === tt.string) return parseExprAtom(); |
|
732 if (token.type === tt.name || token.type.keyword) return parseIdent(); |
|
733 } |
|
734 |
|
735 function parseIdent() { |
|
736 var node = startNode(); |
|
737 node.name = token.type === tt.name ? token.value : token.type.keyword; |
|
738 next(); |
|
739 return finishNode(node, "Identifier"); |
|
740 } |
|
741 |
|
742 function parseFunction(node, isStatement) { |
|
743 if (token.type === tt.name) node.id = parseIdent(); |
|
744 else if (isStatement) node.id = dummyIdent(); |
|
745 else node.id = null; |
|
746 node.params = []; |
|
747 pushCx(); |
|
748 expect(tt.parenL); |
|
749 while (token.type == tt.name) { |
|
750 node.params.push(parseIdent()); |
|
751 eat(tt.comma); |
|
752 } |
|
753 popCx(); |
|
754 eat(tt.parenR); |
|
755 node.body = parseBlock(); |
|
756 return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); |
|
757 } |
|
758 |
|
759 function parseExprList(close) { |
|
760 var indent = curIndent, line = curLineStart, elts = [], continuedLine = nextLineStart; |
|
761 next(); // Opening bracket |
|
762 if (curLineStart > continuedLine) continuedLine = curLineStart; |
|
763 while (!closes(close, indent + (curLineStart <= continuedLine ? 1 : 0), line)) { |
|
764 var elt = parseExpression(true); |
|
765 if (isDummy(elt)) { |
|
766 if (closes(close, indent, line)) break; |
|
767 next(); |
|
768 } else { |
|
769 elts.push(elt); |
|
770 } |
|
771 while (eat(tt.comma)) {} |
|
772 } |
|
773 popCx(); |
|
774 eat(close); |
|
775 return elts; |
|
776 } |
|
777 }); |