| 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 |