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