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 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 | 178 |
181 What is not implemented: | 179 What is not implemented: |
182 | 180 |
183 - Array initializers and object initializers could support splicing. In the | 181 - Array initializers and object initializers could support splicing. In the |
184 array case, we would need some way to know if an ArrayInitializer argument | 182 array case, we would need some way to know if an ArrayInitializer argument |
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 */ | 185 */ |
188 const JsBuilder js = const JsBuilder(); | 186 const JsBuilder js = const JsBuilder(); |
189 | 187 |
190 | |
191 class JsBuilder { | 188 class JsBuilder { |
192 const JsBuilder(); | 189 const JsBuilder(); |
193 | 190 |
194 /** | 191 /** |
195 * Parses a bit of JavaScript, and returns an expression. | 192 * Parses a bit of JavaScript, and returns an expression. |
196 * | 193 * |
197 * See the MiniJsParser class. | 194 * See the MiniJsParser class. |
198 * | 195 * |
199 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or | 196 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or |
200 * a list of [Node]s, which will be interpolated into the source at the '#' | 197 * a list of [Node]s, which will be interpolated into the source at the '#' |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 Template expressionTemplateFor(String source) { | 261 Template expressionTemplateFor(String source) { |
265 return _findExpressionTemplate(source); | 262 return _findExpressionTemplate(source); |
266 } | 263 } |
267 | 264 |
268 /** | 265 /** |
269 * Creates an Expression template without caching the result. | 266 * Creates an Expression template without caching the result. |
270 */ | 267 */ |
271 Template uncachedExpressionTemplate(String source) { | 268 Template uncachedExpressionTemplate(String source) { |
272 MiniJsParser parser = new MiniJsParser(source); | 269 MiniJsParser parser = new MiniJsParser(source); |
273 Expression expression = parser.expression(); | 270 Expression expression = parser.expression(); |
274 return new Template( | 271 return new Template(source, expression, |
275 source, expression, isExpression: true, forceCopy: false); | 272 isExpression: true, forceCopy: false); |
276 } | 273 } |
277 | 274 |
278 /** | 275 /** |
279 * Creates a Statement template without caching the result. | 276 * Creates a Statement template without caching the result. |
280 */ | 277 */ |
281 Template uncachedStatementTemplate(String source) { | 278 Template uncachedStatementTemplate(String source) { |
282 MiniJsParser parser = new MiniJsParser(source); | 279 MiniJsParser parser = new MiniJsParser(source); |
283 Statement statement = parser.statement(); | 280 Statement statement = parser.statement(); |
284 return new Template( | 281 return new Template(source, statement, |
285 source, statement, isExpression: false, forceCopy: false); | 282 isExpression: false, forceCopy: false); |
286 } | 283 } |
287 | 284 |
288 /** | 285 /** |
289 * Create an Expression template which has [ast] as the result. This is used | 286 * Create an Expression template which has [ast] as the result. This is used |
290 * to wrap a generated AST in a zero-argument Template so it can be passed to | 287 * to wrap a generated AST in a zero-argument Template so it can be passed to |
291 * context that expects a template. | 288 * context that expects a template. |
292 */ | 289 */ |
293 Template expressionTemplateYielding(Node ast) { | 290 Template expressionTemplateYielding(Node ast) { |
294 return new Template.withExpressionResult(ast); | 291 return new Template.withExpressionResult(ast); |
295 } | 292 } |
296 | 293 |
297 Template statementTemplateYielding(Node ast) { | 294 Template statementTemplateYielding(Node ast) { |
298 return new Template.withStatementResult(ast); | 295 return new Template.withStatementResult(ast); |
299 } | 296 } |
300 | 297 |
301 /// Creates a literal js string from [value]. | 298 /// Creates a literal js string from [value]. |
302 LiteralString _legacyEscapedString(String value) { | 299 LiteralString _legacyEscapedString(String value) { |
303 // Start by escaping the backslashes. | 300 // Start by escaping the backslashes. |
304 String escaped = value.replaceAll('\\', '\\\\'); | 301 String escaped = value.replaceAll('\\', '\\\\'); |
305 // Do not escape unicode characters and ' because they are allowed in the | 302 // Do not escape unicode characters and ' because they are allowed in the |
306 // string literal anyway. | 303 // string literal anyway. |
307 escaped = escaped.replaceAllMapped(new RegExp('\n|"|\b|\t|\v|\r'), (match) { | 304 escaped = escaped.replaceAllMapped(new RegExp('\n|"|\b|\t|\v|\r'), (match) { |
308 switch (match.group(0)) { | 305 switch (match.group(0)) { |
309 case "\n" : return r"\n"; | 306 case "\n": |
310 case "\"" : return r'\"'; | 307 return r"\n"; |
311 case "\b" : return r"\b"; | 308 case "\"": |
312 case "\t" : return r"\t"; | 309 return r'\"'; |
313 case "\f" : return r"\f"; | 310 case "\b": |
314 case "\r" : return r"\r"; | 311 return r"\b"; |
315 case "\v" : return r"\v"; | 312 case "\t": |
| 313 return r"\t"; |
| 314 case "\f": |
| 315 return r"\f"; |
| 316 case "\r": |
| 317 return r"\r"; |
| 318 case "\v": |
| 319 return r"\v"; |
316 } | 320 } |
317 }); | 321 }); |
318 LiteralString result = string(escaped); | 322 LiteralString result = string(escaped); |
319 // We don't escape ' under the assumption that the string is wrapped | 323 // We don't escape ' under the assumption that the string is wrapped |
320 // into ". Verify that assumption. | 324 // into ". Verify that assumption. |
321 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); | 325 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); |
322 return result; | 326 return result; |
323 } | 327 } |
324 | 328 |
325 /// Creates a literal js string from [value]. | 329 /// Creates a literal js string from [value]. |
326 LiteralString escapedString(String value, | 330 LiteralString escapedString(String value, |
327 {bool utf8: false, bool ascii: false}) { | 331 {bool utf8: false, bool ascii: false}) { |
328 if (utf8 == false && ascii == false) return _legacyEscapedString(value); | 332 if (utf8 == false && ascii == false) return _legacyEscapedString(value); |
329 if (utf8 && ascii) throw new ArgumentError('Cannot be both UTF8 and ASCII'); | 333 if (utf8 && ascii) throw new ArgumentError('Cannot be both UTF8 and ASCII'); |
330 | 334 |
331 int singleQuotes = 0; | 335 int singleQuotes = 0; |
332 int doubleQuotes = 0; | 336 int doubleQuotes = 0; |
333 int otherEscapes = 0; | 337 int otherEscapes = 0; |
334 int unpairedSurrogates = 0; | 338 int unpairedSurrogates = 0; |
335 | 339 |
336 for (int rune in value.runes) { | 340 for (int rune in value.runes) { |
337 if (rune == charCodes.$BACKSLASH) { | 341 if (rune == charCodes.$BACKSLASH) { |
338 ++otherEscapes; | 342 ++otherEscapes; |
339 } else if (rune == charCodes.$SQ) { | 343 } else if (rune == charCodes.$SQ) { |
340 ++singleQuotes; | 344 ++singleQuotes; |
341 } else if (rune == charCodes.$DQ) { | 345 } else if (rune == charCodes.$DQ) { |
342 ++doubleQuotes; | 346 ++doubleQuotes; |
343 } else if (rune == charCodes.$LF || rune == charCodes.$CR || | 347 } else if (rune == charCodes.$LF || |
344 rune == charCodes.$LS || rune == charCodes.$PS) { | 348 rune == charCodes.$CR || |
| 349 rune == charCodes.$LS || |
| 350 rune == charCodes.$PS) { |
345 // Line terminators. | 351 // Line terminators. |
346 ++otherEscapes; | 352 ++otherEscapes; |
347 } else if (rune == charCodes.$BS || rune == charCodes.$TAB || | 353 } else if (rune == charCodes.$BS || |
348 rune == charCodes.$VTAB || rune == charCodes.$FF) { | 354 rune == charCodes.$TAB || |
| 355 rune == charCodes.$VTAB || |
| 356 rune == charCodes.$FF) { |
349 ++otherEscapes; | 357 ++otherEscapes; |
350 } else if (_isUnpairedSurrogate(rune)) { | 358 } else if (_isUnpairedSurrogate(rune)) { |
351 ++unpairedSurrogates; | 359 ++unpairedSurrogates; |
352 } else { | 360 } else { |
353 if (ascii && (rune < charCodes.$SPACE || rune >= charCodes.$DEL)) { | 361 if (ascii && (rune < charCodes.$SPACE || rune >= charCodes.$DEL)) { |
354 ++otherEscapes; | 362 ++otherEscapes; |
355 } | 363 } |
356 } | 364 } |
357 } | 365 } |
358 | 366 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 } | 409 } |
402 } | 410 } |
403 | 411 |
404 return finish(useSingleQuotes ? "'" : '"', sb.toString()); | 412 return finish(useSingleQuotes ? "'" : '"', sb.toString()); |
405 } | 413 } |
406 | 414 |
407 static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800; | 415 static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800; |
408 | 416 |
409 static String _irregularEscape(int code, bool useSingleQuotes) { | 417 static String _irregularEscape(int code, bool useSingleQuotes) { |
410 switch (code) { | 418 switch (code) { |
411 case charCodes.$SQ: return useSingleQuotes ? r"\'" : r"'"; | 419 case charCodes.$SQ: |
412 case charCodes.$DQ: return useSingleQuotes ? r'"' : r'\"'; | 420 return useSingleQuotes ? r"\'" : r"'"; |
413 case charCodes.$BACKSLASH: return r'\\'; | 421 case charCodes.$DQ: |
414 case charCodes.$BS: return r'\b'; | 422 return useSingleQuotes ? r'"' : r'\"'; |
415 case charCodes.$TAB: return r'\t'; | 423 case charCodes.$BACKSLASH: |
416 case charCodes.$LF: return r'\n'; | 424 return r'\\'; |
417 case charCodes.$VTAB: return r'\v'; | 425 case charCodes.$BS: |
418 case charCodes.$FF: return r'\f'; | 426 return r'\b'; |
419 case charCodes.$CR: return r'\r'; | 427 case charCodes.$TAB: |
| 428 return r'\t'; |
| 429 case charCodes.$LF: |
| 430 return r'\n'; |
| 431 case charCodes.$VTAB: |
| 432 return r'\v'; |
| 433 case charCodes.$FF: |
| 434 return r'\f'; |
| 435 case charCodes.$CR: |
| 436 return r'\r'; |
420 } | 437 } |
421 return null; | 438 return null; |
422 } | 439 } |
423 | 440 |
424 /// Creates a literal js string from [value]. | 441 /// Creates a literal js string from [value]. |
425 /// | 442 /// |
426 /// Note that this function only puts quotes around [value]. It does not do | 443 /// Note that this function only puts quotes around [value]. It does not do |
427 /// any escaping, so use only when you can guarantee that [value] does not | 444 /// any escaping, so use only when you can guarantee that [value] does not |
428 /// contain newlines or backslashes. For escaping the string use | 445 /// contain newlines or backslashes. For escaping the string use |
429 /// [escapedString]. | 446 /// [escapedString]. |
430 LiteralString string(String value) => new LiteralString('"$value"'); | 447 LiteralString string(String value) => new LiteralString('"$value"'); |
431 | 448 |
432 /// Creates an instance of [LiteralString] from [value]. | 449 /// Creates an instance of [LiteralString] from [value]. |
433 /// | 450 /// |
434 /// Does not add quotes or do any escaping. | 451 /// Does not add quotes or do any escaping. |
435 LiteralString stringPart(String value) => new LiteralString(value); | 452 LiteralString stringPart(String value) => new LiteralString(value); |
436 | 453 |
437 StringConcatenation concatenateStrings(Iterable<Literal> parts, | 454 StringConcatenation concatenateStrings(Iterable<Literal> parts, |
438 {addQuotes: false}) { | 455 {addQuotes: false}) { |
439 List<Literal> _parts; | 456 List<Literal> _parts; |
440 if (addQuotes) { | 457 if (addQuotes) { |
441 Literal quote = stringPart('"'); | 458 Literal quote = stringPart('"'); |
442 _parts = <Literal>[quote] | 459 _parts = <Literal>[quote] |
443 ..addAll(parts) | 460 ..addAll(parts) |
444 ..add(quote); | 461 ..add(quote); |
445 } else { | 462 } else { |
446 _parts = new List.from(parts, growable: false); | 463 _parts = new List.from(parts, growable: false); |
447 } | 464 } |
448 return new StringConcatenation(_parts); | 465 return new StringConcatenation(_parts); |
449 } | 466 } |
450 | 467 |
451 Iterable<Literal> joinLiterals(Iterable<Literal> list, Literal separator) { | 468 Iterable<Literal> joinLiterals(Iterable<Literal> list, Literal separator) { |
452 return new _InterleaveIterable(list, separator); | 469 return new _InterleaveIterable(list, separator); |
453 } | 470 } |
454 | 471 |
(...skipping 10 matching lines...) Expand all Loading... |
465 LiteralBool boolean(bool value) => new LiteralBool(value); | 482 LiteralBool boolean(bool value) => new LiteralBool(value); |
466 | 483 |
467 ArrayInitializer numArray(Iterable<int> list) => | 484 ArrayInitializer numArray(Iterable<int> list) => |
468 new ArrayInitializer(list.map(number).toList()); | 485 new ArrayInitializer(list.map(number).toList()); |
469 | 486 |
470 ArrayInitializer stringArray(Iterable<String> list) => | 487 ArrayInitializer stringArray(Iterable<String> list) => |
471 new ArrayInitializer(list.map(string).toList()); | 488 new ArrayInitializer(list.map(string).toList()); |
472 | 489 |
473 Comment comment(String text) => new Comment(text); | 490 Comment comment(String text) => new Comment(text); |
474 | 491 |
475 Call propertyCall(Expression receiver, | 492 Call propertyCall( |
476 Expression fieldName, | 493 Expression receiver, Expression fieldName, List<Expression> arguments) { |
477 List<Expression> arguments) { | |
478 return new Call(new PropertyAccess(receiver, fieldName), arguments); | 494 return new Call(new PropertyAccess(receiver, fieldName), arguments); |
479 } | 495 } |
480 | 496 |
481 ObjectInitializer objectLiteral(Map<String, Expression> map) { | 497 ObjectInitializer objectLiteral(Map<String, Expression> map) { |
482 List<Property> properties = <Property>[]; | 498 List<Property> properties = <Property>[]; |
483 map.forEach((name, value) { | 499 map.forEach((name, value) { |
484 properties.add(new Property(string(name), value)); | 500 properties.add(new Property(string(name), value)); |
485 }); | 501 }); |
486 return new ObjectInitializer(properties); | 502 return new ObjectInitializer(properties); |
487 } | 503 } |
488 } | 504 } |
489 | 505 |
490 LiteralString string(String value) => js.string(value); | 506 LiteralString string(String value) => js.string(value); |
491 LiteralString quoteName(Name name, {allowNull: false}) { | 507 LiteralString quoteName(Name name, {allowNull: false}) { |
492 return js.quoteName(name, allowNull: allowNull); | 508 return js.quoteName(name, allowNull: allowNull); |
493 } | 509 } |
| 510 |
494 LiteralString stringPart(String value) => js.stringPart(value); | 511 LiteralString stringPart(String value) => js.stringPart(value); |
495 Iterable<Literal> joinLiterals(Iterable<Literal> list, Literal separator) { | 512 Iterable<Literal> joinLiterals(Iterable<Literal> list, Literal separator) { |
496 return js.joinLiterals(list, separator); | 513 return js.joinLiterals(list, separator); |
497 } | 514 } |
| 515 |
498 StringConcatenation concatenateStrings(Iterable<Literal> parts, | 516 StringConcatenation concatenateStrings(Iterable<Literal> parts, |
499 {addQuotes: false}) { | 517 {addQuotes: false}) { |
500 return js.concatenateStrings(parts, addQuotes: addQuotes); | 518 return js.concatenateStrings(parts, addQuotes: addQuotes); |
501 } | 519 } |
502 | 520 |
503 LiteralNumber number(num value) => js.number(value); | 521 LiteralNumber number(num value) => js.number(value); |
504 ArrayInitializer numArray(Iterable<int> list) => js.numArray(list); | 522 ArrayInitializer numArray(Iterable<int> list) => js.numArray(list); |
505 ArrayInitializer stringArray(Iterable<String> list) => js.stringArray(list); | 523 ArrayInitializer stringArray(Iterable<String> list) => js.stringArray(list); |
506 Call propertyCall(Expression receiver, | 524 Call propertyCall( |
507 Expression fieldName, | 525 Expression receiver, Expression fieldName, List<Expression> arguments) { |
508 List<Expression> arguments) { | |
509 return js.propertyCall(receiver, fieldName, arguments); | 526 return js.propertyCall(receiver, fieldName, arguments); |
510 } | 527 } |
| 528 |
511 ObjectInitializer objectLiteral(Map<String, Expression> map) { | 529 ObjectInitializer objectLiteral(Map<String, Expression> map) { |
512 return js.objectLiteral(map); | 530 return js.objectLiteral(map); |
513 } | 531 } |
514 | 532 |
515 class MiniJsParserError { | 533 class MiniJsParserError { |
516 MiniJsParserError(this.parser, this.message) { } | 534 MiniJsParserError(this.parser, this.message) {} |
517 | 535 |
518 final MiniJsParser parser; | 536 final MiniJsParser parser; |
519 final String message; | 537 final String message; |
520 | 538 |
521 String toString() { | 539 String toString() { |
522 int pos = parser.lastPosition; | 540 int pos = parser.lastPosition; |
523 | 541 |
524 // Discard lines following the line containing lastPosition. | 542 // Discard lines following the line containing lastPosition. |
525 String src = parser.src; | 543 String src = parser.src; |
526 int newlinePos = src.indexOf('\n', pos); | 544 int newlinePos = src.indexOf('\n', pos); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 lastToken = null, | 586 lastToken = null, |
569 lastPosition = 0, | 587 lastPosition = 0, |
570 position = 0 { | 588 position = 0 { |
571 getToken(); | 589 getToken(); |
572 } | 590 } |
573 | 591 |
574 int lastCategory = NONE; | 592 int lastCategory = NONE; |
575 String lastToken = null; | 593 String lastToken = null; |
576 int lastPosition = 0; | 594 int lastPosition = 0; |
577 int position = 0; | 595 int position = 0; |
578 bool skippedNewline = false; // skipped newline in last getToken? | 596 bool skippedNewline = false; // skipped newline in last getToken? |
579 final String src; | 597 final String src; |
580 | 598 |
581 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; | 599 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; |
582 bool get hasNamedHoles => | 600 bool get hasNamedHoles => |
583 interpolatedValues.isNotEmpty && interpolatedValues.first.isNamed; | 601 interpolatedValues.isNotEmpty && interpolatedValues.first.isNamed; |
584 bool get hasPositionalHoles => | 602 bool get hasPositionalHoles => |
585 interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; | 603 interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; |
586 | 604 |
587 static const NONE = -1; | 605 static const NONE = -1; |
588 static const ALPHA = 0; | 606 static const ALPHA = 0; |
(...skipping 14 matching lines...) Expand all Loading... |
603 static const SEMICOLON = 15; | 621 static const SEMICOLON = 15; |
604 static const HASH = 16; | 622 static const HASH = 16; |
605 static const WHITESPACE = 17; | 623 static const WHITESPACE = 17; |
606 static const OTHER = 18; | 624 static const OTHER = 18; |
607 | 625 |
608 // Make sure that ]] is two symbols. | 626 // Make sure that ]] is two symbols. |
609 bool singleCharCategory(int category) => category >= DOT; | 627 bool singleCharCategory(int category) => category >= DOT; |
610 | 628 |
611 static String categoryToString(int cat) { | 629 static String categoryToString(int cat) { |
612 switch (cat) { | 630 switch (cat) { |
613 case NONE: return "NONE"; | 631 case NONE: |
614 case ALPHA: return "ALPHA"; | 632 return "NONE"; |
615 case NUMERIC: return "NUMERIC"; | 633 case ALPHA: |
616 case SYMBOL: return "SYMBOL"; | 634 return "ALPHA"; |
617 case ASSIGNMENT: return "ASSIGNMENT"; | 635 case NUMERIC: |
618 case DOT: return "DOT"; | 636 return "NUMERIC"; |
619 case LPAREN: return "LPAREN"; | 637 case SYMBOL: |
620 case RPAREN: return "RPAREN"; | 638 return "SYMBOL"; |
621 case LBRACE: return "LBRACE"; | 639 case ASSIGNMENT: |
622 case RBRACE: return "RBRACE"; | 640 return "ASSIGNMENT"; |
623 case LSQUARE: return "LSQUARE"; | 641 case DOT: |
624 case RSQUARE: return "RSQUARE"; | 642 return "DOT"; |
625 case STRING: return "STRING"; | 643 case LPAREN: |
626 case COMMA: return "COMMA"; | 644 return "LPAREN"; |
627 case QUERY: return "QUERY"; | 645 case RPAREN: |
628 case COLON: return "COLON"; | 646 return "RPAREN"; |
629 case SEMICOLON: return "SEMICOLON"; | 647 case LBRACE: |
630 case HASH: return "HASH"; | 648 return "LBRACE"; |
631 case WHITESPACE: return "WHITESPACE"; | 649 case RBRACE: |
632 case OTHER: return "OTHER"; | 650 return "RBRACE"; |
| 651 case LSQUARE: |
| 652 return "LSQUARE"; |
| 653 case RSQUARE: |
| 654 return "RSQUARE"; |
| 655 case STRING: |
| 656 return "STRING"; |
| 657 case COMMA: |
| 658 return "COMMA"; |
| 659 case QUERY: |
| 660 return "QUERY"; |
| 661 case COLON: |
| 662 return "COLON"; |
| 663 case SEMICOLON: |
| 664 return "SEMICOLON"; |
| 665 case HASH: |
| 666 return "HASH"; |
| 667 case WHITESPACE: |
| 668 return "WHITESPACE"; |
| 669 case OTHER: |
| 670 return "OTHER"; |
633 } | 671 } |
634 return "Unknown: $cat"; | 672 return "Unknown: $cat"; |
635 } | 673 } |
636 | 674 |
637 static const CATEGORIES = const <int>[ | 675 static const CATEGORIES = const <int>[ |
638 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 | 676 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 |
639 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 | 677 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 |
640 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 | 678 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 |
641 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 | 679 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 |
642 OTHER, OTHER, WHITESPACE, // 30-32 | 680 OTHER, OTHER, WHITESPACE, // 30-32 |
643 SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ | 681 SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ |
644 LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ | 682 LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ |
645 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 | 683 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 |
646 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 | 684 NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 |
647 COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ | 685 COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ |
648 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH | 686 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH |
649 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP | 687 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP |
650 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX | 688 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX |
651 ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' | 689 ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' |
652 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh | 690 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh |
653 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop | 691 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop |
654 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx | 692 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx |
655 ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL]; // yz{|}~ | 693 ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL |
| 694 ]; // yz{|}~ |
656 | 695 |
657 // This must be a >= the highest precedence number handled by parseBinary. | 696 // This must be a >= the highest precedence number handled by parseBinary. |
658 static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; | 697 static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; |
659 static bool isAssignment(String symbol) => BINARY_PRECEDENCE[symbol] == 17; | 698 static bool isAssignment(String symbol) => BINARY_PRECEDENCE[symbol] == 17; |
660 | 699 |
661 // From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operator
s/Operator_Precedence | 700 // From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operator
s/Operator_Precedence |
662 static final BINARY_PRECEDENCE = { | 701 static final BINARY_PRECEDENCE = { |
663 '+=': 17, '-=': 17, '*=': 17, '/=': 17, '%=': 17, '^=': 17, '|=': 17, | 702 '+=': 17, |
664 '&=': 17, '<<=': 17, '>>=': 17, '>>>=': 17, '=': 17, | 703 '-=': 17, |
665 '||': 14, | 704 '*=': 17, |
666 '&&': 13, | 705 '/=': 17, |
667 '|': 12, | 706 '%=': 17, |
668 '^': 11, | 707 '^=': 17, |
669 '&': 10, | 708 '|=': 17, |
670 '!=': 9, '==': 9, '!==': 9, '===': 9, | 709 '&=': 17, |
671 '<': 8, '<=': 8, '>=': 8, '>': 8, 'in': 8, 'instanceof': 8, | 710 '<<=': 17, |
672 '<<': 7, '>>': 7, '>>>': 7, | 711 '>>=': 17, |
673 '+': 6, '-': 6, | 712 '>>>=': 17, |
674 '*': 5, '/': 5, '%': 5 | 713 '=': 17, |
| 714 '||': 14, |
| 715 '&&': 13, |
| 716 '|': 12, |
| 717 '^': 11, |
| 718 '&': 10, |
| 719 '!=': 9, |
| 720 '==': 9, |
| 721 '!==': 9, |
| 722 '===': 9, |
| 723 '<': 8, |
| 724 '<=': 8, |
| 725 '>=': 8, |
| 726 '>': 8, |
| 727 'in': 8, |
| 728 'instanceof': 8, |
| 729 '<<': 7, |
| 730 '>>': 7, |
| 731 '>>>': 7, |
| 732 '+': 6, |
| 733 '-': 6, |
| 734 '*': 5, |
| 735 '/': 5, |
| 736 '%': 5 |
675 }; | 737 }; |
676 static final UNARY_OPERATORS = | 738 static final UNARY_OPERATORS = [ |
677 ['++', '--', '+', '-', '~', '!', 'typeof', 'void', 'delete', 'await'] | 739 '++', |
678 .toSet(); | 740 '--', |
| 741 '+', |
| 742 '-', |
| 743 '~', |
| 744 '!', |
| 745 'typeof', |
| 746 'void', |
| 747 'delete', |
| 748 'await' |
| 749 ].toSet(); |
679 | 750 |
680 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = | 751 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = |
681 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); | 752 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); |
682 | 753 |
683 static int category(int code) { | 754 static int category(int code) { |
684 if (code >= CATEGORIES.length) return OTHER; | 755 if (code >= CATEGORIES.length) return OTHER; |
685 return CATEGORIES[code]; | 756 return CATEGORIES[code]; |
686 } | 757 } |
687 | 758 |
688 String getDelimited(int startPosition) { | 759 String getDelimited(int startPosition) { |
689 position = startPosition; | 760 position = startPosition; |
690 int delimiter = src.codeUnitAt(startPosition); | 761 int delimiter = src.codeUnitAt(startPosition); |
691 int currentCode; | 762 int currentCode; |
692 do { | 763 do { |
693 position++; | 764 position++; |
694 if (position >= src.length) error("Unterminated literal"); | 765 if (position >= src.length) error("Unterminated literal"); |
695 currentCode = src.codeUnitAt(position); | 766 currentCode = src.codeUnitAt(position); |
696 if (currentCode == charCodes.$LF) error("Unterminated literal"); | 767 if (currentCode == charCodes.$LF) error("Unterminated literal"); |
697 if (currentCode == charCodes.$BACKSLASH) { | 768 if (currentCode == charCodes.$BACKSLASH) { |
698 if (++position >= src.length) error("Unterminated literal"); | 769 if (++position >= src.length) error("Unterminated literal"); |
699 int escaped = src.codeUnitAt(position); | 770 int escaped = src.codeUnitAt(position); |
700 if (escaped == charCodes.$x || escaped == charCodes.$X || | 771 if (escaped == charCodes.$x || |
701 escaped == charCodes.$u || escaped == charCodes.$U || | 772 escaped == charCodes.$X || |
| 773 escaped == charCodes.$u || |
| 774 escaped == charCodes.$U || |
702 category(escaped) == NUMERIC) { | 775 category(escaped) == NUMERIC) { |
703 error('Numeric and hex escapes are not allowed in literals'); | 776 error('Numeric and hex escapes are not allowed in literals'); |
704 } | 777 } |
705 } | 778 } |
706 } while (currentCode != delimiter); | 779 } while (currentCode != delimiter); |
707 position++; | 780 position++; |
708 return src.substring(lastPosition, position); | 781 return src.substring(lastPosition, position); |
709 } | 782 } |
710 | 783 |
711 void getToken() { | 784 void getToken() { |
712 skippedNewline = false; | 785 skippedNewline = false; |
713 for (;;) { | 786 for (;;) { |
714 if (position >= src.length) break; | 787 if (position >= src.length) break; |
715 int code = src.codeUnitAt(position); | 788 int code = src.codeUnitAt(position); |
716 // Skip '//' and '/*' style comments. | 789 // Skip '//' and '/*' style comments. |
717 if (code == charCodes.$SLASH && | 790 if (code == charCodes.$SLASH && position + 1 < src.length) { |
718 position + 1 < src.length) { | |
719 if (src.codeUnitAt(position + 1) == charCodes.$SLASH) { | 791 if (src.codeUnitAt(position + 1) == charCodes.$SLASH) { |
720 int nextPosition = src.indexOf('\n', position); | 792 int nextPosition = src.indexOf('\n', position); |
721 if (nextPosition == -1) nextPosition = src.length; | 793 if (nextPosition == -1) nextPosition = src.length; |
722 position = nextPosition; | 794 position = nextPosition; |
723 continue; | 795 continue; |
724 } else if (src.codeUnitAt(position + 1) == charCodes.$STAR) { | 796 } else if (src.codeUnitAt(position + 1) == charCodes.$STAR) { |
725 int nextPosition = src.indexOf('*/', position + 2); | 797 int nextPosition = src.indexOf('*/', position + 2); |
726 if (nextPosition == -1) error('Unterminated comment'); | 798 if (nextPosition == -1) error('Unterminated comment'); |
727 position = nextPosition + 2; | 799 position = nextPosition + 2; |
728 continue; | 800 continue; |
(...skipping 10 matching lines...) Expand all Loading... |
739 lastPosition = position; | 811 lastPosition = position; |
740 return; | 812 return; |
741 } | 813 } |
742 int code = src.codeUnitAt(position); | 814 int code = src.codeUnitAt(position); |
743 lastPosition = position; | 815 lastPosition = position; |
744 if (code == charCodes.$SQ || code == charCodes.$DQ) { | 816 if (code == charCodes.$SQ || code == charCodes.$DQ) { |
745 // String literal. | 817 // String literal. |
746 lastCategory = STRING; | 818 lastCategory = STRING; |
747 lastToken = getDelimited(position); | 819 lastToken = getDelimited(position); |
748 } else if (code == charCodes.$0 && | 820 } else if (code == charCodes.$0 && |
749 position + 2 < src.length && | 821 position + 2 < src.length && |
750 src.codeUnitAt(position + 1) == charCodes.$x) { | 822 src.codeUnitAt(position + 1) == charCodes.$x) { |
751 // Hex literal. | 823 // Hex literal. |
752 for (position += 2; position < src.length; position++) { | 824 for (position += 2; position < src.length; position++) { |
753 int cat = category(src.codeUnitAt(position)); | 825 int cat = category(src.codeUnitAt(position)); |
754 if (cat != NUMERIC && cat != ALPHA) break; | 826 if (cat != NUMERIC && cat != ALPHA) break; |
755 } | 827 } |
756 lastCategory = NUMERIC; | 828 lastCategory = NUMERIC; |
757 lastToken = src.substring(lastPosition, position); | 829 lastToken = src.substring(lastPosition, position); |
758 int.parse(lastToken, onError: (_) { | 830 int.parse(lastToken, onError: (_) { |
759 error("Unparseable number"); | 831 error("Unparseable number"); |
760 }); | 832 }); |
761 } else if (code == charCodes.$SLASH) { | 833 } else if (code == charCodes.$SLASH) { |
762 // Tokens that start with / are special due to regexp literals. | 834 // Tokens that start with / are special due to regexp literals. |
763 lastCategory = SYMBOL; | 835 lastCategory = SYMBOL; |
764 position++; | 836 position++; |
765 if (position < src.length && src.codeUnitAt(position) == charCodes.$EQ) { | 837 if (position < src.length && src.codeUnitAt(position) == charCodes.$EQ) { |
766 position++; | 838 position++; |
767 } | 839 } |
768 lastToken = src.substring(lastPosition, position); | 840 lastToken = src.substring(lastPosition, position); |
769 } else { | 841 } else { |
770 // All other tokens handled here. | 842 // All other tokens handled here. |
771 int cat = category(src.codeUnitAt(position)); | 843 int cat = category(src.codeUnitAt(position)); |
772 int newCat; | 844 int newCat; |
773 do { | 845 do { |
774 position++; | 846 position++; |
775 if (position == src.length) break; | 847 if (position == src.length) break; |
776 int code = src.codeUnitAt(position); | 848 int code = src.codeUnitAt(position); |
777 // Special code to disallow !, ~ and / in non-first position in token, | 849 // Special code to disallow !, ~ and / in non-first position in token, |
778 // so that !! and ~~ parse as two tokens and != parses as one, while =/ | 850 // so that !! and ~~ parse as two tokens and != parses as one, while =/ |
779 // parses as a an equals token followed by a regexp literal start. | 851 // parses as a an equals token followed by a regexp literal start. |
780 newCat = | 852 newCat = (code == charCodes.$BANG || |
781 (code == charCodes.$BANG || | 853 code == charCodes.$SLASH || |
782 code == charCodes.$SLASH || | 854 code == charCodes.$TILDE) |
783 code == charCodes.$TILDE) | |
784 ? NONE | 855 ? NONE |
785 : category(code); | 856 : category(code); |
786 } while (!singleCharCategory(cat) && | 857 } while (!singleCharCategory(cat) && |
787 (cat == newCat || | 858 (cat == newCat || |
788 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. | 859 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. |
789 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 | 860 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 |
790 lastCategory = cat; | 861 lastCategory = cat; |
791 lastToken = src.substring(lastPosition, position); | 862 lastToken = src.substring(lastPosition, position); |
792 if (cat == NUMERIC) { | 863 if (cat == NUMERIC) { |
793 double.parse(lastToken, (_) { | 864 double.parse(lastToken, (_) { |
794 error("Unparseable number"); | 865 error("Unparseable number"); |
795 }); | 866 }); |
796 } else if (cat == SYMBOL) { | 867 } else if (cat == SYMBOL) { |
797 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; | 868 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; |
798 if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken)) { | 869 if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken)) { |
799 error("Unknown operator"); | 870 error("Unknown operator"); |
(...skipping 22 matching lines...) Expand all Loading... |
822 | 893 |
823 void expectSemicolon() { | 894 void expectSemicolon() { |
824 if (acceptSemicolon()) return; | 895 if (acceptSemicolon()) return; |
825 error('Expected SEMICOLON'); | 896 error('Expected SEMICOLON'); |
826 } | 897 } |
827 | 898 |
828 bool acceptSemicolon() { | 899 bool acceptSemicolon() { |
829 // Accept semicolon or automatically inserted semicolon before close brace. | 900 // Accept semicolon or automatically inserted semicolon before close brace. |
830 // Miniparser forbids other kinds of semicolon insertion. | 901 // Miniparser forbids other kinds of semicolon insertion. |
831 if (RBRACE == lastCategory) return true; | 902 if (RBRACE == lastCategory) return true; |
832 if (NONE == lastCategory) return true; // end of input | 903 if (NONE == lastCategory) return true; // end of input |
833 if (skippedNewline) { | 904 if (skippedNewline) { |
834 error('No automatic semicolon insertion at preceding newline'); | 905 error('No automatic semicolon insertion at preceding newline'); |
835 } | 906 } |
836 return acceptCategory(SEMICOLON); | 907 return acceptCategory(SEMICOLON); |
837 } | 908 } |
838 | 909 |
839 bool acceptString(String string) { | 910 bool acceptString(String string) { |
840 if (lastToken == string) { | 911 if (lastToken == string) { |
841 getToken(); | 912 getToken(); |
842 return true; | 913 return true; |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
977 List<Property> properties = <Property>[]; | 1048 List<Property> properties = <Property>[]; |
978 for (;;) { | 1049 for (;;) { |
979 if (acceptCategory(RBRACE)) break; | 1050 if (acceptCategory(RBRACE)) break; |
980 // Limited subset: keys are identifiers, no 'get' or 'set' properties. | 1051 // Limited subset: keys are identifiers, no 'get' or 'set' properties. |
981 Literal propertyName; | 1052 Literal propertyName; |
982 String identifier = lastToken; | 1053 String identifier = lastToken; |
983 if (acceptCategory(ALPHA)) { | 1054 if (acceptCategory(ALPHA)) { |
984 propertyName = new LiteralString('"$identifier"'); | 1055 propertyName = new LiteralString('"$identifier"'); |
985 } else if (acceptCategory(STRING)) { | 1056 } else if (acceptCategory(STRING)) { |
986 propertyName = new LiteralString(identifier); | 1057 propertyName = new LiteralString(identifier); |
987 } else if (acceptCategory(SYMBOL)) { // e.g. void | 1058 } else if (acceptCategory(SYMBOL)) { |
| 1059 // e.g. void |
988 propertyName = new LiteralString('"$identifier"'); | 1060 propertyName = new LiteralString('"$identifier"'); |
989 } else if (acceptCategory(HASH)) { | 1061 } else if (acceptCategory(HASH)) { |
990 var nameOrPosition = parseHash(); | 1062 var nameOrPosition = parseHash(); |
991 InterpolatedLiteral interpolatedLiteral = | 1063 InterpolatedLiteral interpolatedLiteral = |
992 new InterpolatedLiteral(nameOrPosition); | 1064 new InterpolatedLiteral(nameOrPosition); |
993 interpolatedValues.add(interpolatedLiteral); | 1065 interpolatedValues.add(interpolatedLiteral); |
994 propertyName = interpolatedLiteral; | 1066 propertyName = interpolatedLiteral; |
995 } else { | 1067 } else { |
996 error('Expected property name'); | 1068 error('Expected property name'); |
997 } | 1069 } |
(...skipping 29 matching lines...) Expand all Loading... |
1027 if (acceptCategory(LPAREN)) { | 1099 if (acceptCategory(LPAREN)) { |
1028 final arguments = <Expression>[]; | 1100 final arguments = <Expression>[]; |
1029 if (!acceptCategory(RPAREN)) { | 1101 if (!acceptCategory(RPAREN)) { |
1030 while (true) { | 1102 while (true) { |
1031 Expression argument = parseAssignment(); | 1103 Expression argument = parseAssignment(); |
1032 arguments.add(argument); | 1104 arguments.add(argument); |
1033 if (acceptCategory(RPAREN)) break; | 1105 if (acceptCategory(RPAREN)) break; |
1034 expectCategory(COMMA); | 1106 expectCategory(COMMA); |
1035 } | 1107 } |
1036 } | 1108 } |
1037 receiver = constructor ? | 1109 receiver = constructor |
1038 new New(receiver, arguments) : | 1110 ? new New(receiver, arguments) |
1039 new Call(receiver, arguments); | 1111 : new Call(receiver, arguments); |
1040 constructor = false; | 1112 constructor = false; |
1041 } else if (!constructor && acceptCategory(LSQUARE)) { | 1113 } else if (!constructor && acceptCategory(LSQUARE)) { |
1042 Expression inBraces = parseExpression(); | 1114 Expression inBraces = parseExpression(); |
1043 expectCategory(RSQUARE); | 1115 expectCategory(RSQUARE); |
1044 receiver = new PropertyAccess(receiver, inBraces); | 1116 receiver = new PropertyAccess(receiver, inBraces); |
1045 } else if (!constructor && acceptCategory(DOT)) { | 1117 } else if (!constructor && acceptCategory(DOT)) { |
1046 receiver = getDotRhs(receiver); | 1118 receiver = getDotRhs(receiver); |
1047 } else { | 1119 } else { |
1048 // JS allows new without (), but we don't. | 1120 // JS allows new without (), but we don't. |
1049 if (constructor) error("Parentheses are required for new"); | 1121 if (constructor) error("Parentheses are required for new"); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1084 return new Postfix(operator, expression); | 1156 return new Postfix(operator, expression); |
1085 } | 1157 } |
1086 // If we don't accept '++' or '--' due to skippedNewline a newline, no other | 1158 // If we don't accept '++' or '--' due to skippedNewline a newline, no other |
1087 // part of the parser will accept the token and we will get an error at the | 1159 // part of the parser will accept the token and we will get an error at the |
1088 // whole expression level. | 1160 // whole expression level. |
1089 return expression; | 1161 return expression; |
1090 } | 1162 } |
1091 | 1163 |
1092 Expression parseUnaryHigh() { | 1164 Expression parseUnaryHigh() { |
1093 String operator = lastToken; | 1165 String operator = lastToken; |
1094 if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && | 1166 if (lastCategory == SYMBOL && |
| 1167 UNARY_OPERATORS.contains(operator) && |
1095 (acceptString("++") || acceptString("--") || acceptString('await'))) { | 1168 (acceptString("++") || acceptString("--") || acceptString('await'))) { |
1096 if (operator == "await") return new Await(parsePostfix()); | 1169 if (operator == "await") return new Await(parsePostfix()); |
1097 return new Prefix(operator, parsePostfix()); | 1170 return new Prefix(operator, parsePostfix()); |
1098 } | 1171 } |
1099 return parsePostfix(); | 1172 return parsePostfix(); |
1100 } | 1173 } |
1101 | 1174 |
1102 Expression parseUnaryLow() { | 1175 Expression parseUnaryLow() { |
1103 String operator = lastToken; | 1176 String operator = lastToken; |
1104 if (lastCategory == SYMBOL && UNARY_OPERATORS.contains(operator) && | 1177 if (lastCategory == SYMBOL && |
1105 operator != "++" && operator != "--") { | 1178 UNARY_OPERATORS.contains(operator) && |
| 1179 operator != "++" && |
| 1180 operator != "--") { |
1106 expectCategory(SYMBOL); | 1181 expectCategory(SYMBOL); |
1107 if (operator == "await") return new Await(parsePostfix()); | 1182 if (operator == "await") return new Await(parsePostfix()); |
1108 return new Prefix(operator, parseUnaryLow()); | 1183 return new Prefix(operator, parseUnaryLow()); |
1109 } | 1184 } |
1110 return parseUnaryHigh(); | 1185 return parseUnaryHigh(); |
1111 } | 1186 } |
1112 | 1187 |
1113 Expression parseBinary(int maxPrecedence) { | 1188 Expression parseBinary(int maxPrecedence) { |
1114 Expression lhs = parseUnaryLow(); | 1189 Expression lhs = parseUnaryLow(); |
1115 int minPrecedence; | 1190 int minPrecedence; |
1116 String lastSymbol; | 1191 String lastSymbol; |
1117 Expression rhs; // This is null first time around. | 1192 Expression rhs; // This is null first time around. |
1118 while (true) { | 1193 while (true) { |
1119 String symbol = lastToken; | 1194 String symbol = lastToken; |
1120 if (lastCategory != SYMBOL || | 1195 if (lastCategory != SYMBOL || |
1121 !BINARY_PRECEDENCE.containsKey(symbol) || | 1196 !BINARY_PRECEDENCE.containsKey(symbol) || |
1122 BINARY_PRECEDENCE[symbol] > maxPrecedence) { | 1197 BINARY_PRECEDENCE[symbol] > maxPrecedence) { |
1123 break; | 1198 break; |
1124 } | 1199 } |
1125 expectCategory(SYMBOL); | 1200 expectCategory(SYMBOL); |
1126 if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) { | 1201 if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) { |
1127 if (rhs != null) lhs = new Binary(lastSymbol, lhs, rhs); | 1202 if (rhs != null) lhs = new Binary(lastSymbol, lhs, rhs); |
(...skipping 11 matching lines...) Expand all Loading... |
1139 | 1214 |
1140 Expression parseConditional() { | 1215 Expression parseConditional() { |
1141 Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); | 1216 Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); |
1142 if (!acceptCategory(QUERY)) return lhs; | 1217 if (!acceptCategory(QUERY)) return lhs; |
1143 Expression ifTrue = parseAssignment(); | 1218 Expression ifTrue = parseAssignment(); |
1144 expectCategory(COLON); | 1219 expectCategory(COLON); |
1145 Expression ifFalse = parseAssignment(); | 1220 Expression ifFalse = parseAssignment(); |
1146 return new Conditional(lhs, ifTrue, ifFalse); | 1221 return new Conditional(lhs, ifTrue, ifFalse); |
1147 } | 1222 } |
1148 | 1223 |
1149 | |
1150 Expression parseAssignment() { | 1224 Expression parseAssignment() { |
1151 Expression lhs = parseConditional(); | 1225 Expression lhs = parseConditional(); |
1152 String assignmentOperator = lastToken; | 1226 String assignmentOperator = lastToken; |
1153 if (acceptCategory(ASSIGNMENT)) { | 1227 if (acceptCategory(ASSIGNMENT)) { |
1154 Expression rhs = parseAssignment(); | 1228 Expression rhs = parseAssignment(); |
1155 if (assignmentOperator == "=") { | 1229 if (assignmentOperator == "=") { |
1156 return new Assignment(lhs, rhs); | 1230 return new Assignment(lhs, rhs); |
1157 } else { | 1231 } else { |
1158 // Handle +=, -=, etc. | 1232 // Handle +=, -=, etc. |
1159 String operator = | 1233 String operator = |
1160 assignmentOperator.substring(0, assignmentOperator.length - 1); | 1234 assignmentOperator.substring(0, assignmentOperator.length - 1); |
1161 return new Assignment.compound(lhs, operator, rhs); | 1235 return new Assignment.compound(lhs, operator, rhs); |
1162 } | 1236 } |
1163 } | 1237 } |
1164 return lhs; | 1238 return lhs; |
1165 } | 1239 } |
1166 | 1240 |
1167 Expression parseExpression() { | 1241 Expression parseExpression() { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1273 | 1347 |
1274 if (lastToken == 'case') error("Case outside switch."); | 1348 if (lastToken == 'case') error("Case outside switch."); |
1275 | 1349 |
1276 if (lastToken == 'default') error("Default outside switch."); | 1350 if (lastToken == 'default') error("Default outside switch."); |
1277 | 1351 |
1278 if (lastToken == 'yield') return parseYield(); | 1352 if (lastToken == 'yield') return parseYield(); |
1279 | 1353 |
1280 if (lastToken == 'with') { | 1354 if (lastToken == 'with') { |
1281 error('Not implemented in mini parser'); | 1355 error('Not implemented in mini parser'); |
1282 } | 1356 } |
1283 | |
1284 } | 1357 } |
1285 | 1358 |
1286 bool checkForInterpolatedStatement = lastCategory == HASH; | 1359 bool checkForInterpolatedStatement = lastCategory == HASH; |
1287 | 1360 |
1288 Expression expression = parseExpression(); | 1361 Expression expression = parseExpression(); |
1289 | 1362 |
1290 if (expression is VariableUse && acceptCategory(COLON)) { | 1363 if (expression is VariableUse && acceptCategory(COLON)) { |
1291 return new LabeledStatement(expression.name, parseStatement()); | 1364 return new LabeledStatement(expression.name, parseStatement()); |
1292 } | 1365 } |
1293 | 1366 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1380 return finishFor(null); | 1453 return finishFor(null); |
1381 } | 1454 } |
1382 | 1455 |
1383 if (acceptString('var')) { | 1456 if (acceptString('var')) { |
1384 Declaration declaration = parseVariableDeclaration(); | 1457 Declaration declaration = parseVariableDeclaration(); |
1385 if (acceptString('in')) { | 1458 if (acceptString('in')) { |
1386 Expression objectExpression = parseExpression(); | 1459 Expression objectExpression = parseExpression(); |
1387 expectCategory(RPAREN); | 1460 expectCategory(RPAREN); |
1388 Statement body = parseStatement(); | 1461 Statement body = parseStatement(); |
1389 return new ForIn( | 1462 return new ForIn( |
1390 new VariableDeclarationList([ | 1463 new VariableDeclarationList( |
1391 new VariableInitialization(declaration, null)]), | 1464 [new VariableInitialization(declaration, null)]), |
1392 objectExpression, | 1465 objectExpression, |
1393 body); | 1466 body); |
1394 } | 1467 } |
1395 Expression declarations = finishVariableDeclarationList(declaration); | 1468 Expression declarations = finishVariableDeclarationList(declaration); |
1396 expectCategory(SEMICOLON); | 1469 expectCategory(SEMICOLON); |
1397 return finishFor(declarations); | 1470 return finishFor(declarations); |
1398 } | 1471 } |
1399 | 1472 |
1400 Expression init = parseExpression(); | 1473 Expression init = parseExpression(); |
1401 expectCategory(SEMICOLON); | 1474 expectCategory(SEMICOLON); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1443 expression = parseExpression(); | 1516 expression = parseExpression(); |
1444 expectCategory(COLON); | 1517 expectCategory(COLON); |
1445 } else { | 1518 } else { |
1446 if (!acceptString('default')) { | 1519 if (!acceptString('default')) { |
1447 error('expected case or default'); | 1520 error('expected case or default'); |
1448 } | 1521 } |
1449 expectCategory(COLON); | 1522 expectCategory(COLON); |
1450 } | 1523 } |
1451 List statements = new List<Statement>(); | 1524 List statements = new List<Statement>(); |
1452 while (lastCategory != RBRACE && | 1525 while (lastCategory != RBRACE && |
1453 lastToken != 'case' && | 1526 lastToken != 'case' && |
1454 lastToken != 'default') { | 1527 lastToken != 'default') { |
1455 statements.add(parseStatement()); | 1528 statements.add(parseStatement()); |
1456 } | 1529 } |
1457 return expression == null | 1530 return expression == null |
1458 ? new Default(new Block(statements)) | 1531 ? new Default(new Block(statements)) |
1459 : new Case(expression, new Block(statements)); | 1532 : new Case(expression, new Block(statements)); |
1460 } | 1533 } |
1461 | 1534 |
1462 Statement parseWhile() { | 1535 Statement parseWhile() { |
1463 expectCategory(LPAREN); | 1536 expectCategory(LPAREN); |
1464 Expression condition = parseExpression(); | 1537 Expression condition = parseExpression(); |
(...skipping 12 matching lines...) Expand all Loading... |
1477 expectSemicolon(); | 1550 expectSemicolon(); |
1478 return new Do(body, condition); | 1551 return new Do(body, condition); |
1479 } | 1552 } |
1480 | 1553 |
1481 Statement parseSwitch() { | 1554 Statement parseSwitch() { |
1482 expectCategory(LPAREN); | 1555 expectCategory(LPAREN); |
1483 Expression key = parseExpression(); | 1556 Expression key = parseExpression(); |
1484 expectCategory(RPAREN); | 1557 expectCategory(RPAREN); |
1485 expectCategory(LBRACE); | 1558 expectCategory(LBRACE); |
1486 List<SwitchClause> clauses = new List<SwitchClause>(); | 1559 List<SwitchClause> clauses = new List<SwitchClause>(); |
1487 while(lastCategory != RBRACE) { | 1560 while (lastCategory != RBRACE) { |
1488 clauses.add(parseSwitchClause()); | 1561 clauses.add(parseSwitchClause()); |
1489 } | 1562 } |
1490 expectCategory(RBRACE); | 1563 expectCategory(RBRACE); |
1491 return new Switch(key, clauses); | 1564 return new Switch(key, clauses); |
1492 } | 1565 } |
1493 | 1566 |
1494 Catch parseCatch() { | 1567 Catch parseCatch() { |
1495 expectCategory(LPAREN); | 1568 expectCategory(LPAREN); |
1496 Declaration errorName = parseVariableDeclaration(); | 1569 Declaration errorName = parseVariableDeclaration(); |
1497 expectCategory(RPAREN); | 1570 expectCategory(RPAREN); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1530 class _InterleaveIterable extends IterableBase { | 1603 class _InterleaveIterable extends IterableBase { |
1531 Iterable<Node> source; | 1604 Iterable<Node> source; |
1532 Node separator; | 1605 Node separator; |
1533 | 1606 |
1534 _InterleaveIterable(this.source, this.separator); | 1607 _InterleaveIterable(this.source, this.separator); |
1535 | 1608 |
1536 Iterator<Node> get iterator { | 1609 Iterator<Node> get iterator { |
1537 return new _InterleaveIterator(source.iterator, separator); | 1610 return new _InterleaveIterator(source.iterator, separator); |
1538 } | 1611 } |
1539 } | 1612 } |
OLD | NEW |