OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // Utilities for building JS ASTs at runtime. Contains a builder class | 5 // Utilities for building JS ASTs at runtime. Contains a builder class |
6 // and a parser that parses part of the language. | 6 // and a parser that parses part of the language. |
7 | 7 |
8 part of js_ast; | 8 part of js_ast; |
9 | 9 |
10 | |
11 /** | 10 /** |
12 * Global template manager. We should aim to have a fixed number of | 11 * Global template manager. We should aim to have a fixed number of |
13 * templates. This implies that we do not use js('xxx') to parse text that is | 12 * templates. This implies that we do not use js('xxx') to parse text that is |
14 * constructed from values that depend on names in the Dart program. | 13 * constructed from values that depend on names in the Dart program. |
15 * | 14 * |
16 * TODO(sra): Find the remaining places where js('xxx') used to parse an | 15 * TODO(sra): Find the remaining places where js('xxx') used to parse an |
17 * unbounded number of expression, or institute a cache policy. | 16 * unbounded number of expression, or institute a cache policy. |
18 */ | 17 */ |
19 TemplateManager templateManager = new TemplateManager(); | 18 TemplateManager templateManager = new TemplateManager(); |
20 | 19 |
21 | |
22 /** | 20 /** |
23 | 21 |
24 [js] is a singleton instace of JsBuilder. JsBuilder is a set of conveniences | 22 [js] is a singleton instace of JsBuilder. JsBuilder is a set of conveniences |
25 for constructing JavaScript ASTs. | 23 for constructing JavaScript ASTs. |
26 | 24 |
27 [string] and [number] are used to create leaf AST nodes: | 25 [string] and [number] are used to create leaf AST nodes: |
28 | 26 |
29 var s = js.string('hello'); // s = new LiteralString('"hello"') | 27 var s = js.string('hello'); // s = new LiteralString('"hello"') |
30 var n = js.number(123); // n = new LiteralNumber(123) | 28 var n = js.number(123); // n = new LiteralNumber(123) |
31 | 29 |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
185 should be splice or is intended as a single value. | 183 should be splice or is intended as a single value. |
186 | 184 |
187 - There are no placeholders in definition contexts: | 185 - There are no placeholders in definition contexts: |
188 | 186 |
189 function #(){} | 187 function #(){} |
190 var # = 1; | 188 var # = 1; |
191 | 189 |
192 */ | 190 */ |
193 const JsBuilder js = const JsBuilder(); | 191 const JsBuilder js = const JsBuilder(); |
194 | 192 |
195 | |
196 class JsBuilder { | 193 class JsBuilder { |
197 const JsBuilder(); | 194 const JsBuilder(); |
198 | 195 |
199 /** | 196 /** |
200 * Parses a bit of JavaScript, and returns an expression. | 197 * Parses a bit of JavaScript, and returns an expression. |
201 * | 198 * |
202 * See the MiniJsParser class. | 199 * See the MiniJsParser class. |
203 * | 200 * |
204 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or | 201 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or |
205 * a list of [Node]s, which will be interpolated into the source at the '#' | 202 * a list of [Node]s, which will be interpolated into the source at the '#' |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 } | 257 } |
261 return template; | 258 return template; |
262 } | 259 } |
263 | 260 |
264 /** | 261 /** |
265 * Creates an Expression template without caching the result. | 262 * Creates an Expression template without caching the result. |
266 */ | 263 */ |
267 Template uncachedExpressionTemplate(String source) { | 264 Template uncachedExpressionTemplate(String source) { |
268 MiniJsParser parser = new MiniJsParser(source); | 265 MiniJsParser parser = new MiniJsParser(source); |
269 Expression expression = parser.expression(); | 266 Expression expression = parser.expression(); |
270 return new Template( | 267 return new Template(source, expression, |
271 source, expression, isExpression: true, forceCopy: false); | 268 isExpression: true, forceCopy: false); |
272 } | 269 } |
273 | 270 |
274 /** | 271 /** |
275 * Creates a Statement template without caching the result. | 272 * Creates a Statement template without caching the result. |
276 */ | 273 */ |
277 Template uncachedStatementTemplate(String source) { | 274 Template uncachedStatementTemplate(String source) { |
278 MiniJsParser parser = new MiniJsParser(source); | 275 MiniJsParser parser = new MiniJsParser(source); |
279 Statement statement = parser.statement(); | 276 Statement statement = parser.statement(); |
280 return new Template( | 277 return new Template(source, statement, |
281 source, statement, isExpression: false, forceCopy: false); | 278 isExpression: false, forceCopy: false); |
282 } | 279 } |
283 | 280 |
284 /** | 281 /** |
285 * Create an Expression template which has [ast] as the result. This is used | 282 * Create an Expression template which has [ast] as the result. This is used |
286 * to wrap a generated AST in a zero-argument Template so it can be passed to | 283 * to wrap a generated AST in a zero-argument Template so it can be passed to |
287 * context that expects a template. | 284 * context that expects a template. |
288 */ | 285 */ |
289 Template expressionTemplateYielding(Node ast) { | 286 Template expressionTemplateYielding(Node ast) { |
290 return new Template.withExpressionResult(ast); | 287 return new Template.withExpressionResult(ast); |
291 } | 288 } |
292 | 289 |
293 Template statementTemplateYielding(Node ast) { | 290 Template statementTemplateYielding(Node ast) { |
294 return new Template.withStatementResult(ast); | 291 return new Template.withStatementResult(ast); |
295 } | 292 } |
296 | 293 |
297 /// Creates a literal js string from [value]. | 294 /// Creates a literal js string from [value]. |
298 LiteralString escapedString(String value, [String quote = '"']) { | 295 LiteralString escapedString(String value, [String quote = '"']) { |
299 // Start by escaping the backslashes. | 296 // Start by escaping the backslashes. |
300 String escaped = value.replaceAll('\\', '\\\\'); | 297 String escaped = value.replaceAll('\\', '\\\\'); |
301 | 298 |
302 | |
303 // Replace $ in template strings: | 299 // Replace $ in template strings: |
304 // http://www.ecma-international.org/ecma-262/6.0/#sec-template-literal-lexi
cal-components | 300 // http://www.ecma-international.org/ecma-262/6.0/#sec-template-literal-lexi
cal-components |
305 var quoteReplace = quote == '`' ? r'`$' : quote; | 301 var quoteReplace = quote == '`' ? r'`$' : quote; |
306 | 302 |
307 // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-liter
als | 303 // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-liter
als |
308 // > All code points may appear literally in a string literal except for the | 304 // > All code points may appear literally in a string literal except for the |
309 // > closing quote code points, U+005C (REVERSE SOLIDUS), | 305 // > closing quote code points, U+005C (REVERSE SOLIDUS), |
310 // > U+000D (CARRIAGE RETURN), U+2028 (LINE SEPARATOR), | 306 // > U+000D (CARRIAGE RETURN), U+2028 (LINE SEPARATOR), |
311 // > U+2029 (PARAGRAPH SEPARATOR), and U+000A (LINE FEED). | 307 // > U+2029 (PARAGRAPH SEPARATOR), and U+000A (LINE FEED). |
312 var re = new RegExp('[\n\r$quoteReplace\b\f\t\v\u2028\u2029]'); | 308 var re = new RegExp('[\n\r$quoteReplace\b\f\t\v\u2028\u2029]'); |
313 escaped = escaped.replaceAllMapped(re, (m) { | 309 escaped = escaped.replaceAllMapped(re, (m) { |
314 switch (m.group(0)) { | 310 switch (m.group(0)) { |
315 case "\n" : return r"\n"; | 311 case "\n": |
316 case "\r" : return r"\r"; | 312 return r"\n"; |
317 case "\u2028": return r"\u2028"; | 313 case "\r": |
318 case "\u2029": return r"\u2029"; | 314 return r"\r"; |
| 315 case "\u2028": |
| 316 return r"\u2028"; |
| 317 case "\u2029": |
| 318 return r"\u2029"; |
319 // Quotes and $ are only replaced if they conflict with the containing | 319 // Quotes and $ are only replaced if they conflict with the containing |
320 // quote, see regex above. | 320 // quote, see regex above. |
321 case '"': return r'\"'; | 321 case '"': |
322 case "'": return r"\'"; | 322 return r'\"'; |
323 case "`": return r"\`"; | 323 case "'": |
324 case r"$": return r"\$"; | 324 return r"\'"; |
| 325 case "`": |
| 326 return r"\`"; |
| 327 case r"$": |
| 328 return r"\$"; |
325 // TODO(jmesserly): these don't need to be escaped for correctness, | 329 // TODO(jmesserly): these don't need to be escaped for correctness, |
326 // but they are conventionally escaped. | 330 // but they are conventionally escaped. |
327 case "\b": return r"\b"; | 331 case "\b": |
328 case "\t": return r"\t"; | 332 return r"\b"; |
329 case "\f": return r"\f"; | 333 case "\t": |
330 case "\v": return r"\v"; | 334 return r"\t"; |
| 335 case "\f": |
| 336 return r"\f"; |
| 337 case "\v": |
| 338 return r"\v"; |
331 } | 339 } |
332 }); | 340 }); |
333 LiteralString result = new LiteralString('$quote$escaped$quote'); | 341 LiteralString result = new LiteralString('$quote$escaped$quote'); |
334 // We don't escape quotes of a different style under the assumption that the | 342 // We don't escape quotes of a different style under the assumption that the |
335 // string is wrapped into quotes. Verify that assumption. | 343 // string is wrapped into quotes. Verify that assumption. |
336 assert(result.value.codeUnitAt(0) == quote.codeUnitAt(0)); | 344 assert(result.value.codeUnitAt(0) == quote.codeUnitAt(0)); |
337 return result; | 345 return result; |
338 } | 346 } |
339 | 347 |
340 /// Creates a literal js string from [value]. | 348 /// Creates a literal js string from [value]. |
(...skipping 12 matching lines...) Expand all Loading... |
353 ArrayInitializer numArray(Iterable<int> list) => | 361 ArrayInitializer numArray(Iterable<int> list) => |
354 new ArrayInitializer(list.map(number).toList()); | 362 new ArrayInitializer(list.map(number).toList()); |
355 | 363 |
356 ArrayInitializer stringArray(Iterable<String> list) => | 364 ArrayInitializer stringArray(Iterable<String> list) => |
357 new ArrayInitializer(list.map(string).toList()); | 365 new ArrayInitializer(list.map(string).toList()); |
358 | 366 |
359 Comment comment(String text) => new Comment(text); | 367 Comment comment(String text) => new Comment(text); |
360 CommentExpression commentExpression(String text, Expression expression) => | 368 CommentExpression commentExpression(String text, Expression expression) => |
361 new CommentExpression(text, expression); | 369 new CommentExpression(text, expression); |
362 | 370 |
363 Call propertyCall(Expression receiver, | 371 Call propertyCall( |
364 String fieldName, | 372 Expression receiver, String fieldName, List<Expression> arguments) { |
365 List<Expression> arguments) { | |
366 return new Call(new PropertyAccess.field(receiver, fieldName), arguments); | 373 return new Call(new PropertyAccess.field(receiver, fieldName), arguments); |
367 } | 374 } |
368 } | 375 } |
369 | 376 |
370 LiteralString string(String value) => js.string(value); | 377 LiteralString string(String value) => js.string(value); |
371 LiteralNumber number(num value) => js.number(value); | 378 LiteralNumber number(num value) => js.number(value); |
372 ArrayInitializer numArray(Iterable<int> list) => js.numArray(list); | 379 ArrayInitializer numArray(Iterable<int> list) => js.numArray(list); |
373 ArrayInitializer stringArray(Iterable<String> list) => js.stringArray(list); | 380 ArrayInitializer stringArray(Iterable<String> list) => js.stringArray(list); |
374 Call propertyCall(Expression receiver, | 381 Call propertyCall( |
375 String fieldName, | 382 Expression receiver, String fieldName, List<Expression> arguments) { |
376 List<Expression> arguments) { | |
377 return js.propertyCall(receiver, fieldName, arguments); | 383 return js.propertyCall(receiver, fieldName, arguments); |
378 } | 384 } |
379 | 385 |
380 class MiniJsParserError { | 386 class MiniJsParserError { |
381 MiniJsParserError(this.parser, this.message) { } | 387 MiniJsParserError(this.parser, this.message) {} |
382 | 388 |
383 final MiniJsParser parser; | 389 final MiniJsParser parser; |
384 final String message; | 390 final String message; |
385 | 391 |
386 String toString() { | 392 String toString() { |
387 int pos = parser.lastPosition; | 393 int pos = parser.lastPosition; |
388 | 394 |
389 // Discard lines following the line containing lastPosition. | 395 // Discard lines following the line containing lastPosition. |
390 String src = parser.src; | 396 String src = parser.src; |
391 int newlinePos = src.indexOf('\n', pos); | 397 int newlinePos = src.indexOf('\n', pos); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 lastToken = null, | 439 lastToken = null, |
434 lastPosition = 0, | 440 lastPosition = 0, |
435 position = 0 { | 441 position = 0 { |
436 getToken(); | 442 getToken(); |
437 } | 443 } |
438 | 444 |
439 int lastCategory = NONE; | 445 int lastCategory = NONE; |
440 String lastToken = null; | 446 String lastToken = null; |
441 int lastPosition = 0; | 447 int lastPosition = 0; |
442 int position = 0; | 448 int position = 0; |
443 bool skippedNewline = false; // skipped newline in last getToken? | 449 bool skippedNewline = false; // skipped newline in last getToken? |
444 final String src; | 450 final String src; |
445 | 451 |
446 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; | 452 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; |
447 bool get hasNamedHoles => | 453 bool get hasNamedHoles => |
448 interpolatedValues.isNotEmpty && interpolatedValues.first.isNamed; | 454 interpolatedValues.isNotEmpty && interpolatedValues.first.isNamed; |
449 bool get hasPositionalHoles => | 455 bool get hasPositionalHoles => |
450 interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; | 456 interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; |
451 | 457 |
452 static const NONE = -1; | 458 static const NONE = -1; |
453 static const ALPHA = 0; | 459 static const ALPHA = 0; |
(...skipping 19 matching lines...) Expand all Loading... |
473 static const OTHER = 20; | 479 static const OTHER = 20; |
474 | 480 |
475 // Make sure that ]] is two symbols. | 481 // Make sure that ]] is two symbols. |
476 // TODO(jmesserly): => and ... are not single char tokens, should we change | 482 // TODO(jmesserly): => and ... are not single char tokens, should we change |
477 // their numbers? It shouldn't matter because this is only called on values | 483 // their numbers? It shouldn't matter because this is only called on values |
478 // from the [CATEGORIES] table. | 484 // from the [CATEGORIES] table. |
479 bool singleCharCategory(int category) => category > DOT; | 485 bool singleCharCategory(int category) => category > DOT; |
480 | 486 |
481 static String categoryToString(int cat) { | 487 static String categoryToString(int cat) { |
482 switch (cat) { | 488 switch (cat) { |
483 case NONE: return "NONE"; | 489 case NONE: |
484 case ALPHA: return "ALPHA"; | 490 return "NONE"; |
485 case NUMERIC: return "NUMERIC"; | 491 case ALPHA: |
486 case SYMBOL: return "SYMBOL"; | 492 return "ALPHA"; |
487 case ASSIGNMENT: return "ASSIGNMENT"; | 493 case NUMERIC: |
488 case DOT: return "DOT"; | 494 return "NUMERIC"; |
489 case LPAREN: return "LPAREN"; | 495 case SYMBOL: |
490 case RPAREN: return "RPAREN"; | 496 return "SYMBOL"; |
491 case LBRACE: return "LBRACE"; | 497 case ASSIGNMENT: |
492 case RBRACE: return "RBRACE"; | 498 return "ASSIGNMENT"; |
493 case LSQUARE: return "LSQUARE"; | 499 case DOT: |
494 case RSQUARE: return "RSQUARE"; | 500 return "DOT"; |
495 case STRING: return "STRING"; | 501 case LPAREN: |
496 case COMMA: return "COMMA"; | 502 return "LPAREN"; |
497 case QUERY: return "QUERY"; | 503 case RPAREN: |
498 case COLON: return "COLON"; | 504 return "RPAREN"; |
499 case SEMICOLON: return "SEMICOLON"; | 505 case LBRACE: |
500 case ARROW: return "ARROW"; | 506 return "LBRACE"; |
501 case ELLIPSIS: return "ELLIPSIS"; | 507 case RBRACE: |
502 case HASH: return "HASH"; | 508 return "RBRACE"; |
503 case WHITESPACE: return "WHITESPACE"; | 509 case LSQUARE: |
504 case OTHER: return "OTHER"; | 510 return "LSQUARE"; |
| 511 case RSQUARE: |
| 512 return "RSQUARE"; |
| 513 case STRING: |
| 514 return "STRING"; |
| 515 case COMMA: |
| 516 return "COMMA"; |
| 517 case QUERY: |
| 518 return "QUERY"; |
| 519 case COLON: |
| 520 return "COLON"; |
| 521 case SEMICOLON: |
| 522 return "SEMICOLON"; |
| 523 case ARROW: |
| 524 return "ARROW"; |
| 525 case ELLIPSIS: |
| 526 return "ELLIPSIS"; |
| 527 case HASH: |
| 528 return "HASH"; |
| 529 case WHITESPACE: |
| 530 return "WHITESPACE"; |
| 531 case OTHER: |
| 532 return "OTHER"; |
505 } | 533 } |
506 return "Unknown: $cat"; | 534 return "Unknown: $cat"; |
507 } | 535 } |
508 | 536 |
509 static const CATEGORIES = const <int>[ | 537 static const CATEGORIES = const <int>[ |
510 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 | 538 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 |
511 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 | 539 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 |
512 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 | 540 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 |
513 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 | 541 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 |
514 OTHER, OTHER, WHITESPACE, // 30-32 | 542 OTHER, OTHER, WHITESPACE, // 30-32 |
515 SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ | 543 SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ |
516 LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ | 544 LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ |
517 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 | 545 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 |
518 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 | 546 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 |
519 COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ | 547 COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ |
520 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH | 548 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH |
521 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP | 549 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP |
522 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX | 550 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX |
523 ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' | 551 ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' |
524 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh | 552 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh |
525 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop | 553 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop |
526 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx | 554 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx |
527 ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL]; // yz{|}~ | 555 ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL |
| 556 ]; // yz{|}~ |
528 | 557 |
529 // This must be a >= the highest precedence number handled by parseBinary. | 558 // This must be a >= the highest precedence number handled by parseBinary. |
530 static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; | 559 static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; |
531 static bool isAssignment(String symbol) => BINARY_PRECEDENCE[symbol] == 17; | 560 static bool isAssignment(String symbol) => BINARY_PRECEDENCE[symbol] == 17; |
532 | 561 |
533 // From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operator
s/Operator_Precedence | 562 // From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operator
s/Operator_Precedence |
534 static final BINARY_PRECEDENCE = { | 563 static final BINARY_PRECEDENCE = { |
535 '+=': 17, '-=': 17, '*=': 17, '/=': 17, '%=': 17, '^=': 17, '|=': 17, | 564 '+=': 17, |
536 '&=': 17, '<<=': 17, '>>=': 17, '>>>=': 17, '=': 17, | 565 '-=': 17, |
537 '||': 14, | 566 '*=': 17, |
538 '&&': 13, | 567 '/=': 17, |
539 '|': 12, | 568 '%=': 17, |
540 '^': 11, | 569 '^=': 17, |
541 '&': 10, | 570 '|=': 17, |
542 '!=': 9, '==': 9, '!==': 9, '===': 9, | 571 '&=': 17, |
543 '<': 8, '<=': 8, '>=': 8, '>': 8, 'in': 8, 'instanceof': 8, | 572 '<<=': 17, |
544 '<<': 7, '>>': 7, '>>>': 7, | 573 '>>=': 17, |
545 '+': 6, '-': 6, | 574 '>>>=': 17, |
546 '*': 5, '/': 5, '%': 5 | 575 '=': 17, |
| 576 '||': 14, |
| 577 '&&': 13, |
| 578 '|': 12, |
| 579 '^': 11, |
| 580 '&': 10, |
| 581 '!=': 9, |
| 582 '==': 9, |
| 583 '!==': 9, |
| 584 '===': 9, |
| 585 '<': 8, |
| 586 '<=': 8, |
| 587 '>=': 8, |
| 588 '>': 8, |
| 589 'in': 8, |
| 590 'instanceof': 8, |
| 591 '<<': 7, |
| 592 '>>': 7, |
| 593 '>>>': 7, |
| 594 '+': 6, |
| 595 '-': 6, |
| 596 '*': 5, |
| 597 '/': 5, |
| 598 '%': 5 |
547 }; | 599 }; |
548 static final UNARY_OPERATORS = | 600 static final UNARY_OPERATORS = [ |
549 ['++', '--', '+', '-', '~', '!', 'typeof', 'void', 'delete', 'await'] | 601 '++', |
550 .toSet(); | 602 '--', |
| 603 '+', |
| 604 '-', |
| 605 '~', |
| 606 '!', |
| 607 'typeof', |
| 608 'void', |
| 609 'delete', |
| 610 'await' |
| 611 ].toSet(); |
551 | 612 |
552 static final ARROW_TOKEN = '=>'; | 613 static final ARROW_TOKEN = '=>'; |
553 static final ELLIPSIS_TOKEN = '...'; | 614 static final ELLIPSIS_TOKEN = '...'; |
554 | 615 |
555 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = | 616 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = |
556 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); | 617 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); |
557 | 618 |
558 static int category(int code) { | 619 static int category(int code) { |
559 if (code >= CATEGORIES.length) return OTHER; | 620 if (code >= CATEGORIES.length) return OTHER; |
560 return CATEGORIES[code]; | 621 return CATEGORIES[code]; |
561 } | 622 } |
562 | 623 |
563 String getDelimited(int startPosition) { | 624 String getDelimited(int startPosition) { |
564 position = startPosition; | 625 position = startPosition; |
565 int delimiter = src.codeUnitAt(startPosition); | 626 int delimiter = src.codeUnitAt(startPosition); |
566 int currentCode; | 627 int currentCode; |
567 do { | 628 do { |
568 position++; | 629 position++; |
569 if (position >= src.length) error("Unterminated literal"); | 630 if (position >= src.length) error("Unterminated literal"); |
570 currentCode = src.codeUnitAt(position); | 631 currentCode = src.codeUnitAt(position); |
571 if (currentCode == charCodes.$LF) error("Unterminated literal"); | 632 if (currentCode == charCodes.$LF) error("Unterminated literal"); |
572 if (currentCode == charCodes.$BACKSLASH) { | 633 if (currentCode == charCodes.$BACKSLASH) { |
573 if (++position >= src.length) error("Unterminated literal"); | 634 if (++position >= src.length) error("Unterminated literal"); |
574 int escaped = src.codeUnitAt(position); | 635 int escaped = src.codeUnitAt(position); |
575 if (escaped == charCodes.$x || escaped == charCodes.$X || | 636 if (escaped == charCodes.$x || |
576 escaped == charCodes.$u || escaped == charCodes.$U || | 637 escaped == charCodes.$X || |
| 638 escaped == charCodes.$u || |
| 639 escaped == charCodes.$U || |
577 category(escaped) == NUMERIC) { | 640 category(escaped) == NUMERIC) { |
578 error('Numeric and hex escapes are not allowed in literals'); | 641 error('Numeric and hex escapes are not allowed in literals'); |
579 } | 642 } |
580 } | 643 } |
581 } while (currentCode != delimiter); | 644 } while (currentCode != delimiter); |
582 position++; | 645 position++; |
583 return src.substring(lastPosition, position); | 646 return src.substring(lastPosition, position); |
584 } | 647 } |
585 | 648 |
586 void getToken() { | 649 void getToken() { |
587 skippedNewline = false; | 650 skippedNewline = false; |
588 for (;;) { | 651 for (;;) { |
589 if (position >= src.length) break; | 652 if (position >= src.length) break; |
590 int code = src.codeUnitAt(position); | 653 int code = src.codeUnitAt(position); |
591 // Skip '//' and '/*' style comments. | 654 // Skip '//' and '/*' style comments. |
592 if (code == charCodes.$SLASH && | 655 if (code == charCodes.$SLASH && position + 1 < src.length) { |
593 position + 1 < src.length) { | |
594 if (src.codeUnitAt(position + 1) == charCodes.$SLASH) { | 656 if (src.codeUnitAt(position + 1) == charCodes.$SLASH) { |
595 int nextPosition = src.indexOf('\n', position); | 657 int nextPosition = src.indexOf('\n', position); |
596 if (nextPosition == -1) nextPosition = src.length; | 658 if (nextPosition == -1) nextPosition = src.length; |
597 position = nextPosition; | 659 position = nextPosition; |
598 continue; | 660 continue; |
599 } else if (src.codeUnitAt(position + 1) == charCodes.$STAR) { | 661 } else if (src.codeUnitAt(position + 1) == charCodes.$STAR) { |
600 int nextPosition = src.indexOf('*/', position + 2); | 662 int nextPosition = src.indexOf('*/', position + 2); |
601 if (nextPosition == -1) error('Unterminated comment'); | 663 if (nextPosition == -1) error('Unterminated comment'); |
602 position = nextPosition + 2; | 664 position = nextPosition + 2; |
603 continue; | 665 continue; |
(...skipping 10 matching lines...) Expand all Loading... |
614 lastPosition = position; | 676 lastPosition = position; |
615 return; | 677 return; |
616 } | 678 } |
617 int code = src.codeUnitAt(position); | 679 int code = src.codeUnitAt(position); |
618 lastPosition = position; | 680 lastPosition = position; |
619 if (code == charCodes.$SQ || code == charCodes.$DQ) { | 681 if (code == charCodes.$SQ || code == charCodes.$DQ) { |
620 // String literal. | 682 // String literal. |
621 lastCategory = STRING; | 683 lastCategory = STRING; |
622 lastToken = getDelimited(position); | 684 lastToken = getDelimited(position); |
623 } else if (code == charCodes.$0 && | 685 } else if (code == charCodes.$0 && |
624 position + 2 < src.length && | 686 position + 2 < src.length && |
625 src.codeUnitAt(position + 1) == charCodes.$x) { | 687 src.codeUnitAt(position + 1) == charCodes.$x) { |
626 // Hex literal. | 688 // Hex literal. |
627 for (position += 2; position < src.length; position++) { | 689 for (position += 2; position < src.length; position++) { |
628 int cat = category(src.codeUnitAt(position)); | 690 int cat = category(src.codeUnitAt(position)); |
629 if (cat != NUMERIC && cat != ALPHA) break; | 691 if (cat != NUMERIC && cat != ALPHA) break; |
630 } | 692 } |
631 lastCategory = NUMERIC; | 693 lastCategory = NUMERIC; |
632 lastToken = src.substring(lastPosition, position); | 694 lastToken = src.substring(lastPosition, position); |
633 int.parse(lastToken, onError: (_) { | 695 int.parse(lastToken, onError: (_) { |
634 error("Unparseable number"); | 696 error("Unparseable number"); |
635 }); | 697 }); |
(...skipping 10 matching lines...) Expand all Loading... |
646 int cat = category(src.codeUnitAt(position)); | 708 int cat = category(src.codeUnitAt(position)); |
647 int newCat; | 709 int newCat; |
648 do { | 710 do { |
649 position++; | 711 position++; |
650 if (position == src.length) break; | 712 if (position == src.length) break; |
651 int code = src.codeUnitAt(position); | 713 int code = src.codeUnitAt(position); |
652 // Special code to disallow ! and / in non-first position in token, so | 714 // Special code to disallow ! and / in non-first position in token, so |
653 // that !! parses as two tokens and != parses as one, while =/ parses | 715 // that !! parses as two tokens and != parses as one, while =/ parses |
654 // as a an equals token followed by a regexp literal start. | 716 // as a an equals token followed by a regexp literal start. |
655 newCat = (code == charCodes.$BANG || code == charCodes.$SLASH) | 717 newCat = (code == charCodes.$BANG || code == charCodes.$SLASH) |
656 ? NONE | 718 ? NONE |
657 : category(code); | 719 : category(code); |
658 } while (!singleCharCategory(cat) && | 720 } while (!singleCharCategory(cat) && |
659 (cat == newCat || | 721 (cat == newCat || |
660 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. | 722 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. |
661 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 | 723 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 |
662 lastCategory = cat; | 724 lastCategory = cat; |
663 lastToken = src.substring(lastPosition, position); | 725 lastToken = src.substring(lastPosition, position); |
664 if (cat == NUMERIC) { | 726 if (cat == NUMERIC) { |
665 double.parse(lastToken, (_) { | 727 double.parse(lastToken, (_) { |
666 error("Unparseable number"); | 728 error("Unparseable number"); |
667 }); | 729 }); |
668 } else if (cat == DOT && lastToken.length > 1) { | 730 } else if (cat == DOT && lastToken.length > 1) { |
669 if (lastToken == ELLIPSIS_TOKEN) { | 731 if (lastToken == ELLIPSIS_TOKEN) { |
670 lastCategory = ELLIPSIS; | 732 lastCategory = ELLIPSIS; |
671 } else { | 733 } else { |
672 error("Unknown operator"); | 734 error("Unknown operator"); |
673 } | 735 } |
674 } else if (cat == SYMBOL) { | 736 } else if (cat == SYMBOL) { |
675 if (lastToken == ARROW_TOKEN) { | 737 if (lastToken == ARROW_TOKEN) { |
676 lastCategory = ARROW; | 738 lastCategory = ARROW; |
677 } else { | 739 } else { |
678 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; | 740 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; |
679 if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken))
{ | 741 if (binaryPrecendence == null && |
| 742 !UNARY_OPERATORS.contains(lastToken)) { |
680 error("Unknown operator"); | 743 error("Unknown operator"); |
681 } | 744 } |
682 if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; | 745 if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; |
683 } | 746 } |
684 } else if (cat == ALPHA) { | 747 } else if (cat == ALPHA) { |
685 if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { | 748 if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { |
686 lastCategory = SYMBOL; | 749 lastCategory = SYMBOL; |
687 } | 750 } |
688 } | 751 } |
689 } | 752 } |
(...skipping 14 matching lines...) Expand all Loading... |
704 | 767 |
705 void expectSemicolon() { | 768 void expectSemicolon() { |
706 if (acceptSemicolon()) return; | 769 if (acceptSemicolon()) return; |
707 error('Expected SEMICOLON'); | 770 error('Expected SEMICOLON'); |
708 } | 771 } |
709 | 772 |
710 bool acceptSemicolon() { | 773 bool acceptSemicolon() { |
711 // Accept semicolon or automatically inserted semicolon before close brace. | 774 // Accept semicolon or automatically inserted semicolon before close brace. |
712 // Miniparser forbids other kinds of semicolon insertion. | 775 // Miniparser forbids other kinds of semicolon insertion. |
713 if (RBRACE == lastCategory) return true; | 776 if (RBRACE == lastCategory) return true; |
714 if (NONE == lastCategory) return true; // end of input | 777 if (NONE == lastCategory) return true; // end of input |
715 if (skippedNewline) { | 778 if (skippedNewline) { |
716 error('No automatic semicolon insertion at preceding newline'); | 779 error('No automatic semicolon insertion at preceding newline'); |
717 } | 780 } |
718 return acceptCategory(SEMICOLON); | 781 return acceptCategory(SEMICOLON); |
719 } | 782 } |
720 | 783 |
721 bool acceptString(String string) { | 784 bool acceptString(String string) { |
722 if (lastToken == string) { | 785 if (lastToken == string) { |
723 getToken(); | 786 getToken(); |
724 return true; | 787 return true; |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
895 } else { | 958 } else { |
896 body = parseAssignment(); | 959 body = parseAssignment(); |
897 } | 960 } |
898 return new ArrowFun(params, body); | 961 return new ArrowFun(params, body); |
899 } | 962 } |
900 | 963 |
901 Expression parseFunctionExpression() { | 964 Expression parseFunctionExpression() { |
902 String last = lastToken; | 965 String last = lastToken; |
903 if (acceptCategory(ALPHA)) { | 966 if (acceptCategory(ALPHA)) { |
904 String functionName = last; | 967 String functionName = last; |
905 return new NamedFunction(new Identifier(functionName), | 968 return new NamedFunction(new Identifier(functionName), parseFun()); |
906 parseFun()); | |
907 } | 969 } |
908 return parseFun(); | 970 return parseFun(); |
909 } | 971 } |
910 | 972 |
911 Expression parseFun() { | 973 Expression parseFun() { |
912 List<Parameter> params = <Parameter>[]; | 974 List<Parameter> params = <Parameter>[]; |
913 | 975 |
914 expectCategory(LPAREN); | 976 expectCategory(LPAREN); |
915 if (!acceptCategory(RPAREN)) { | 977 if (!acceptCategory(RPAREN)) { |
916 for (;;) { | 978 for (;;) { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1004 if (acceptCategory(ELLIPSIS)) { | 1066 if (acceptCategory(ELLIPSIS)) { |
1005 arguments.add(new Spread(parseAssignment())); | 1067 arguments.add(new Spread(parseAssignment())); |
1006 expectCategory(RPAREN); | 1068 expectCategory(RPAREN); |
1007 break; | 1069 break; |
1008 } | 1070 } |
1009 arguments.add(parseAssignment()); | 1071 arguments.add(parseAssignment()); |
1010 if (acceptCategory(RPAREN)) break; | 1072 if (acceptCategory(RPAREN)) break; |
1011 expectCategory(COMMA); | 1073 expectCategory(COMMA); |
1012 } | 1074 } |
1013 } | 1075 } |
1014 receiver = constructor ? | 1076 receiver = constructor |
1015 new New(receiver, arguments) : | 1077 ? new New(receiver, arguments) |
1016 new Call(receiver, arguments); | 1078 : new Call(receiver, arguments); |
1017 constructor = false; | 1079 constructor = false; |
1018 } else if (!constructor && acceptCategory(LSQUARE)) { | 1080 } else if (!constructor && acceptCategory(LSQUARE)) { |
1019 Expression inBraces = parseExpression(); | 1081 Expression inBraces = parseExpression(); |
1020 expectCategory(RSQUARE); | 1082 expectCategory(RSQUARE); |
1021 receiver = new PropertyAccess(receiver, inBraces); | 1083 receiver = new PropertyAccess(receiver, inBraces); |
1022 } else if (!constructor && acceptCategory(DOT)) { | 1084 } else if (!constructor && acceptCategory(DOT)) { |
1023 receiver = getDotRhs(receiver); | 1085 receiver = getDotRhs(receiver); |
1024 } else { | 1086 } else { |
1025 // JS allows new without (), but we don't. | 1087 // JS allows new without (), but we don't. |
1026 if (constructor) error("Parentheses are required for new"); | 1088 if (constructor) error("Parentheses are required for new"); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1061 return new Postfix(operator, expression); | 1123 return new Postfix(operator, expression); |
1062 } | 1124 } |
1063 // If we don't accept '++' or '--' due to skippedNewline a newline, no other | 1125 // If we don't accept '++' or '--' due to skippedNewline a newline, no other |
1064 // part of the parser will accept the token and we will get an error at the | 1126 // part of the parser will accept the token and we will get an error at the |
1065 // whole expression level. | 1127 // whole expression level. |
1066 return expression; | 1128 return expression; |
1067 } | 1129 } |
1068 | 1130 |
1069 Expression parseUnaryHigh() { | 1131 Expression parseUnaryHigh() { |
1070 String operator = lastToken; | 1132 String operator = lastToken; |
1071 if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && | 1133 if (lastCategory == SYMBOL && |
| 1134 UNARY_OPERATORS.contains(operator) && |
1072 (acceptString("++") || acceptString("--") || acceptString('await'))) { | 1135 (acceptString("++") || acceptString("--") || acceptString('await'))) { |
1073 if (operator == "await") return new Await(parsePostfix()); | 1136 if (operator == "await") return new Await(parsePostfix()); |
1074 return new Prefix(operator, parsePostfix()); | 1137 return new Prefix(operator, parsePostfix()); |
1075 } | 1138 } |
1076 return parsePostfix(); | 1139 return parsePostfix(); |
1077 } | 1140 } |
1078 | 1141 |
1079 Expression parseUnaryLow() { | 1142 Expression parseUnaryLow() { |
1080 String operator = lastToken; | 1143 String operator = lastToken; |
1081 if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && | 1144 if (lastCategory == SYMBOL && |
1082 operator != "++" && operator != "--") { | 1145 UNARY_OPERATORS.contains(operator) && |
| 1146 operator != "++" && |
| 1147 operator != "--") { |
1083 expectCategory(SYMBOL); | 1148 expectCategory(SYMBOL); |
1084 if (operator == "await") return new Await(parsePostfix()); | 1149 if (operator == "await") return new Await(parsePostfix()); |
1085 return new Prefix(operator, parseUnaryLow()); | 1150 return new Prefix(operator, parseUnaryLow()); |
1086 } | 1151 } |
1087 return parseUnaryHigh(); | 1152 return parseUnaryHigh(); |
1088 } | 1153 } |
1089 | 1154 |
1090 Expression parseBinary(int maxPrecedence) { | 1155 Expression parseBinary(int maxPrecedence) { |
1091 Expression lhs = parseUnaryLow(); | 1156 Expression lhs = parseUnaryLow(); |
1092 int minPrecedence; | 1157 int minPrecedence; |
1093 String lastSymbol; | 1158 String lastSymbol; |
1094 Expression rhs; // This is null first time around. | 1159 Expression rhs; // This is null first time around. |
1095 while (true) { | 1160 while (true) { |
1096 String symbol = lastToken; | 1161 String symbol = lastToken; |
1097 if (lastCategory != SYMBOL || | 1162 if (lastCategory != SYMBOL || |
1098 !BINARY_PRECEDENCE.containsKey(symbol) || | 1163 !BINARY_PRECEDENCE.containsKey(symbol) || |
1099 BINARY_PRECEDENCE[symbol] > maxPrecedence) { | 1164 BINARY_PRECEDENCE[symbol] > maxPrecedence) { |
1100 break; | 1165 break; |
1101 } | 1166 } |
1102 expectCategory(SYMBOL); | 1167 expectCategory(SYMBOL); |
1103 if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) { | 1168 if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) { |
1104 if (rhs != null) lhs = new Binary(lastSymbol, lhs, rhs); | 1169 if (rhs != null) lhs = new Binary(lastSymbol, lhs, rhs); |
(...skipping 20 matching lines...) Expand all Loading... |
1125 | 1190 |
1126 Expression parseLeftHandSide() => parseConditional(); | 1191 Expression parseLeftHandSide() => parseConditional(); |
1127 | 1192 |
1128 Expression parseAssignment() { | 1193 Expression parseAssignment() { |
1129 Expression lhs = parseLeftHandSide(); | 1194 Expression lhs = parseLeftHandSide(); |
1130 String assignmentOperator = lastToken; | 1195 String assignmentOperator = lastToken; |
1131 if (acceptCategory(ASSIGNMENT)) { | 1196 if (acceptCategory(ASSIGNMENT)) { |
1132 Expression rhs = parseAssignment(); | 1197 Expression rhs = parseAssignment(); |
1133 if (assignmentOperator == "=") { | 1198 if (assignmentOperator == "=") { |
1134 return new Assignment(lhs, rhs); | 1199 return new Assignment(lhs, rhs); |
1135 } else { | 1200 } else { |
1136 // Handle +=, -=, etc. | 1201 // Handle +=, -=, etc. |
1137 String operator = | 1202 String operator = |
1138 assignmentOperator.substring(0, assignmentOperator.length - 1); | 1203 assignmentOperator.substring(0, assignmentOperator.length - 1); |
1139 return new Assignment.compound(lhs, operator, rhs); | 1204 return new Assignment.compound(lhs, operator, rhs); |
1140 } | 1205 } |
1141 } | 1206 } |
1142 return lhs; | 1207 return lhs; |
1143 } | 1208 } |
1144 | 1209 |
1145 Expression parseExpression() { | 1210 Expression parseExpression() { |
1146 Expression expression = parseAssignment(); | 1211 Expression expression = parseAssignment(); |
1147 while (acceptCategory(COMMA)) { | 1212 while (acceptCategory(COMMA)) { |
1148 Expression right = parseAssignment(); | 1213 Expression right = parseAssignment(); |
1149 expression = new Binary(',', expression, right); | 1214 expression = new Binary(',', expression, right); |
1150 } | 1215 } |
1151 return expression; | 1216 return expression; |
1152 } | 1217 } |
1153 | 1218 |
1154 /** Parse a variable declaration list, with `var` or `let` [keyword] */ | 1219 /** Parse a variable declaration list, with `var` or `let` [keyword] */ |
1155 VariableDeclarationList parseVariableDeclarationList( | 1220 VariableDeclarationList parseVariableDeclarationList(String keyword, |
1156 String keyword, [String firstIdentifier]) { | 1221 [String firstIdentifier]) { |
1157 var initialization = <VariableInitialization>[]; | 1222 var initialization = <VariableInitialization>[]; |
1158 | 1223 |
1159 do { | 1224 do { |
1160 var declarator; | 1225 var declarator; |
1161 if (firstIdentifier != null) { | 1226 if (firstIdentifier != null) { |
1162 declarator = new Identifier(firstIdentifier); | 1227 declarator = new Identifier(firstIdentifier); |
1163 firstIdentifier = null; | 1228 firstIdentifier = null; |
1164 } else { | 1229 } else { |
1165 declarator = parseVariableBinding(); | 1230 declarator = parseVariableBinding(); |
1166 } | 1231 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1202 } | 1267 } |
1203 | 1268 |
1204 ArrayBindingPattern parseArrayBindingPattern() { | 1269 ArrayBindingPattern parseArrayBindingPattern() { |
1205 var variables = <DestructuredVariable>[]; | 1270 var variables = <DestructuredVariable>[]; |
1206 do { | 1271 do { |
1207 var name; | 1272 var name; |
1208 var structure; | 1273 var structure; |
1209 var defaultValue; | 1274 var defaultValue; |
1210 | 1275 |
1211 var declarator = parseVariableBinding(); | 1276 var declarator = parseVariableBinding(); |
1212 if (declarator is Identifier) name = declarator; | 1277 if (declarator is Identifier) |
1213 else if (declarator is BindingPattern) structure = declarator; | 1278 name = declarator; |
1214 else error("Unexpected LHS: $declarator"); | 1279 else if (declarator is BindingPattern) |
| 1280 structure = declarator; |
| 1281 else |
| 1282 error("Unexpected LHS: $declarator"); |
1215 | 1283 |
1216 if (acceptString("=")) { | 1284 if (acceptString("=")) { |
1217 defaultValue = parseExpression(); | 1285 defaultValue = parseExpression(); |
1218 } | 1286 } |
1219 variables.add(new DestructuredVariable( | 1287 variables.add(new DestructuredVariable( |
1220 name: name, structure: structure, defaultValue: defaultValue)); | 1288 name: name, structure: structure, defaultValue: defaultValue)); |
1221 } while (acceptCategory(COMMA)); | 1289 } while (acceptCategory(COMMA)); |
1222 | 1290 |
1223 expectCategory(RSQUARE); | 1291 expectCategory(RSQUARE); |
1224 return new ArrayBindingPattern(variables); | 1292 return new ArrayBindingPattern(variables); |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1331 | 1399 |
1332 if (lastToken == 'case') error("Case outside switch."); | 1400 if (lastToken == 'case') error("Case outside switch."); |
1333 | 1401 |
1334 if (lastToken == 'default') error("Default outside switch."); | 1402 if (lastToken == 'default') error("Default outside switch."); |
1335 | 1403 |
1336 if (lastToken == 'yield') return parseYield(); | 1404 if (lastToken == 'yield') return parseYield(); |
1337 | 1405 |
1338 if (lastToken == 'with') { | 1406 if (lastToken == 'with') { |
1339 error('Not implemented in mini parser'); | 1407 error('Not implemented in mini parser'); |
1340 } | 1408 } |
1341 | |
1342 } | 1409 } |
1343 | 1410 |
1344 bool checkForInterpolatedStatement = lastCategory == HASH; | 1411 bool checkForInterpolatedStatement = lastCategory == HASH; |
1345 | 1412 |
1346 Expression expression = parseExpression(); | 1413 Expression expression = parseExpression(); |
1347 | 1414 |
1348 if (expression is Identifier && acceptCategory(COLON)) { | 1415 if (expression is Identifier && acceptCategory(COLON)) { |
1349 return new LabeledStatement(expression.name, parseStatement()); | 1416 return new LabeledStatement(expression.name, parseStatement()); |
1350 } | 1417 } |
1351 | 1418 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1444 | 1511 |
1445 var keyword = acceptVarLetOrConst(); | 1512 var keyword = acceptVarLetOrConst(); |
1446 if (keyword != null) { | 1513 if (keyword != null) { |
1447 String identifier = lastToken; | 1514 String identifier = lastToken; |
1448 expectCategory(ALPHA); | 1515 expectCategory(ALPHA); |
1449 | 1516 |
1450 if (acceptString('in')) { | 1517 if (acceptString('in')) { |
1451 Expression objectExpression = parseExpression(); | 1518 Expression objectExpression = parseExpression(); |
1452 expectCategory(RPAREN); | 1519 expectCategory(RPAREN); |
1453 Statement body = parseStatement(); | 1520 Statement body = parseStatement(); |
1454 return new ForIn( | 1521 return new ForIn(_createVariableDeclarationList(keyword, identifier), |
1455 _createVariableDeclarationList(keyword, identifier), | 1522 objectExpression, body); |
1456 objectExpression, | |
1457 body); | |
1458 } else if (acceptString('of')) { | 1523 } else if (acceptString('of')) { |
1459 Expression iterableExpression = parseAssignment(); | 1524 Expression iterableExpression = parseAssignment(); |
1460 expectCategory(RPAREN); | 1525 expectCategory(RPAREN); |
1461 Statement body = parseStatement(); | 1526 Statement body = parseStatement(); |
1462 return new ForOf( | 1527 return new ForOf(_createVariableDeclarationList(keyword, identifier), |
1463 _createVariableDeclarationList(keyword, identifier), | 1528 iterableExpression, body); |
1464 iterableExpression, | |
1465 body); | |
1466 } | 1529 } |
1467 var declarations = parseVariableDeclarationList(keyword, identifier); | 1530 var declarations = parseVariableDeclarationList(keyword, identifier); |
1468 expectCategory(SEMICOLON); | 1531 expectCategory(SEMICOLON); |
1469 return finishFor(declarations); | 1532 return finishFor(declarations); |
1470 } | 1533 } |
1471 | 1534 |
1472 Expression init = parseExpression(); | 1535 Expression init = parseExpression(); |
1473 expectCategory(SEMICOLON); | 1536 expectCategory(SEMICOLON); |
1474 return finishFor(init); | 1537 return finishFor(init); |
1475 } | 1538 } |
1476 | 1539 |
1477 static VariableDeclarationList _createVariableDeclarationList( | 1540 static VariableDeclarationList _createVariableDeclarationList( |
1478 String keyword, String identifier) { | 1541 String keyword, String identifier) { |
1479 return new VariableDeclarationList(keyword, [ | 1542 return new VariableDeclarationList(keyword, |
1480 new VariableInitialization( | 1543 [new VariableInitialization(new Identifier(identifier), null)]); |
1481 new Identifier(identifier), null)]); | |
1482 } | 1544 } |
1483 | 1545 |
1484 Statement parseFunctionDeclaration() { | 1546 Statement parseFunctionDeclaration() { |
1485 String name = lastToken; | 1547 String name = lastToken; |
1486 expectCategory(ALPHA); | 1548 expectCategory(ALPHA); |
1487 Expression fun = parseFun(); | 1549 Expression fun = parseFun(); |
1488 return new FunctionDeclaration(new Identifier(name), fun); | 1550 return new FunctionDeclaration(new Identifier(name), fun); |
1489 } | 1551 } |
1490 | 1552 |
1491 Statement parseTry() { | 1553 Statement parseTry() { |
(...skipping 17 matching lines...) Expand all Loading... |
1509 expression = parseExpression(); | 1571 expression = parseExpression(); |
1510 expectCategory(COLON); | 1572 expectCategory(COLON); |
1511 } else { | 1573 } else { |
1512 if (!acceptString('default')) { | 1574 if (!acceptString('default')) { |
1513 error('expected case or default'); | 1575 error('expected case or default'); |
1514 } | 1576 } |
1515 expectCategory(COLON); | 1577 expectCategory(COLON); |
1516 } | 1578 } |
1517 var statements = <Statement>[]; | 1579 var statements = <Statement>[]; |
1518 while (lastCategory != RBRACE && | 1580 while (lastCategory != RBRACE && |
1519 lastToken != 'case' && | 1581 lastToken != 'case' && |
1520 lastToken != 'default') { | 1582 lastToken != 'default') { |
1521 statements.add(parseStatement()); | 1583 statements.add(parseStatement()); |
1522 } | 1584 } |
1523 return expression == null | 1585 return expression == null |
1524 ? new Default(new Block(statements)) | 1586 ? new Default(new Block(statements)) |
1525 : new Case(expression, new Block(statements)); | 1587 : new Case(expression, new Block(statements)); |
1526 } | 1588 } |
1527 | 1589 |
1528 Statement parseWhile() { | 1590 Statement parseWhile() { |
1529 expectCategory(LPAREN); | 1591 expectCategory(LPAREN); |
1530 Expression condition = parseExpression(); | 1592 Expression condition = parseExpression(); |
(...skipping 12 matching lines...) Expand all Loading... |
1543 expectSemicolon(); | 1605 expectSemicolon(); |
1544 return new Do(body, condition); | 1606 return new Do(body, condition); |
1545 } | 1607 } |
1546 | 1608 |
1547 Statement parseSwitch() { | 1609 Statement parseSwitch() { |
1548 expectCategory(LPAREN); | 1610 expectCategory(LPAREN); |
1549 Expression key = parseExpression(); | 1611 Expression key = parseExpression(); |
1550 expectCategory(RPAREN); | 1612 expectCategory(RPAREN); |
1551 expectCategory(LBRACE); | 1613 expectCategory(LBRACE); |
1552 List<SwitchClause> clauses = new List<SwitchClause>(); | 1614 List<SwitchClause> clauses = new List<SwitchClause>(); |
1553 while(lastCategory != RBRACE) { | 1615 while (lastCategory != RBRACE) { |
1554 clauses.add(parseSwitchClause()); | 1616 clauses.add(parseSwitchClause()); |
1555 } | 1617 } |
1556 expectCategory(RBRACE); | 1618 expectCategory(RBRACE); |
1557 return new Switch(key, clauses); | 1619 return new Switch(key, clauses); |
1558 } | 1620 } |
1559 | 1621 |
1560 Catch parseCatch() { | 1622 Catch parseCatch() { |
1561 expectCategory(LPAREN); | 1623 expectCategory(LPAREN); |
1562 String identifier = lastToken; | 1624 String identifier = lastToken; |
1563 expectCategory(ALPHA); | 1625 expectCategory(ALPHA); |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1642 expectCategory(RSQUARE); | 1704 expectCategory(RSQUARE); |
1643 return expr; | 1705 return expr; |
1644 } else if (acceptCategory(HASH)) { | 1706 } else if (acceptCategory(HASH)) { |
1645 return parseInterpolatedExpression(); | 1707 return parseInterpolatedExpression(); |
1646 } else { | 1708 } else { |
1647 error('Expected property name'); | 1709 error('Expected property name'); |
1648 return null; | 1710 return null; |
1649 } | 1711 } |
1650 } | 1712 } |
1651 } | 1713 } |
OLD | NEW |