| OLD | NEW |
| (Empty) | |
| 1 library dart_printer_test; |
| 2 |
| 3 import "package:expect/expect.dart"; |
| 4 import '../../../sdk/lib/_internal/compiler/implementation/dart_backend/dart_pri
nter.dart'; |
| 5 import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.da
rt'; |
| 6 import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart'; |
| 7 import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'; |
| 8 import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart' show
DartString; |
| 9 import 'dart:mirrors'; |
| 10 import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart' as tr
ee; |
| 11 import '../../../sdk/lib/_internal/compiler/implementation/string_validator.dart
'; |
| 12 |
| 13 /// For debugging the [AstBuilder] stack. Prints information about [x]. |
| 14 void show(x) { |
| 15 StringBuffer buf = new StringBuffer(); |
| 16 Unparser unparser = new Unparser(buf); |
| 17 void unparse(x) { |
| 18 if (x is Expression) |
| 19 unparser.writeExpression(x); |
| 20 else if (x is TypeAnnotation) |
| 21 unparser.writeType(x); |
| 22 else if (x is Statement) |
| 23 unparser.writeStatement(x); |
| 24 else if (x is List) { |
| 25 buf.write('['); |
| 26 bool first = true; |
| 27 for (var y in x) { |
| 28 if (first) |
| 29 first = false; |
| 30 else |
| 31 buf.write(', '); |
| 32 unparse(y); |
| 33 } |
| 34 buf.write(']'); |
| 35 } |
| 36 } |
| 37 unparse(x); |
| 38 print("${x.runtimeType}: ${buf.toString()}"); |
| 39 } |
| 40 |
| 41 class PrintDiagnosticListener implements DiagnosticListener { |
| 42 void log(message) { |
| 43 print(message); |
| 44 } |
| 45 |
| 46 void internalError(Spannable spannable, message) { |
| 47 print(message); |
| 48 } |
| 49 |
| 50 SourceSpan spanFromSpannable(Spannable node) { |
| 51 return new SourceSpan(null, 0, 0); |
| 52 } |
| 53 |
| 54 void reportFatalError(Spannable node, MessageKind errorCode, |
| 55 [Map arguments = const {}]) { |
| 56 print(errorCode); |
| 57 throw new Error(); |
| 58 } |
| 59 |
| 60 void reportError(Spannable node, MessageKind errorCode, |
| 61 [Map arguments = const {}]) { |
| 62 print(errorCode); |
| 63 } |
| 64 |
| 65 void reportWarning(Spannable node, MessageKind errorCode, |
| 66 [Map arguments = const {}]) { |
| 67 print(errorCode); |
| 68 } |
| 69 |
| 70 void reportHint(Spannable node, MessageKind errorCode, |
| 71 [Map arguments = const {}]) { |
| 72 print(errorCode); |
| 73 } |
| 74 |
| 75 void reportInfo(Spannable node, MessageKind errorCode, |
| 76 [Map arguments = const {}]) { |
| 77 print(errorCode); |
| 78 } |
| 79 |
| 80 withCurrentElement(element, f()) { |
| 81 f(); |
| 82 } |
| 83 } |
| 84 |
| 85 class AstBuilder extends Listener { |
| 86 final List stack = []; |
| 87 final StringValidator stringValidator |
| 88 = new StringValidator(new PrintDiagnosticListener()); |
| 89 |
| 90 String asName(e) { |
| 91 if (e is Identifier) |
| 92 return e.name; |
| 93 else if (e == null) |
| 94 return null; |
| 95 else |
| 96 throw 'Expression is not a name: ${e.runtimeType}'; |
| 97 } |
| 98 |
| 99 TypeAnnotation asType(x) { |
| 100 if (x is TypeAnnotation) |
| 101 return x; |
| 102 if (x is Identifier) |
| 103 return new TypeAnnotation(x.name); |
| 104 if (x == null) |
| 105 return null; |
| 106 else |
| 107 throw "Not a type: ${x.runtimeType}"; |
| 108 } |
| 109 |
| 110 Parameter asParameter(x) { |
| 111 if (x is Parameter) |
| 112 return x; |
| 113 if (x is Identifier) |
| 114 return new Parameter(x.name); |
| 115 else |
| 116 throw "Not a parameter: ${x.runtimeType}"; |
| 117 } |
| 118 |
| 119 void push(node) { |
| 120 stack.add(node); |
| 121 } |
| 122 dynamic peek() { |
| 123 return stack.last; |
| 124 } |
| 125 dynamic pop([coerce(x) = null]) { |
| 126 var x = stack.removeLast(); |
| 127 if (coerce != null) |
| 128 return coerce(x); |
| 129 else |
| 130 return x; |
| 131 } |
| 132 List popList(int count, [List result, coerce(x) = null]) { |
| 133 if (result == null) |
| 134 result = <Node>[]; |
| 135 for (int i=0; i<count; i++) { |
| 136 var x = stack[stack.length-count+i]; |
| 137 if (coerce != null) { |
| 138 x = coerce(x); |
| 139 } |
| 140 result.add(x); |
| 141 } |
| 142 stack.removeRange(stack.length-count, stack.length); |
| 143 return result; |
| 144 } |
| 145 popTypeAnnotation() { |
| 146 List<TypeAnnotation> args = pop(); |
| 147 if (args == null) |
| 148 return null; |
| 149 String name = pop(asName); |
| 150 return new TypeAnnotation(name, args); |
| 151 } |
| 152 |
| 153 // EXPRESSIONS |
| 154 endCascade() { |
| 155 throw "Cascade not supported yet"; |
| 156 } |
| 157 endIdentifierList(int count) { |
| 158 push(popList(count, <Identifier>[])); |
| 159 } |
| 160 endTypeList(int count) { |
| 161 push(popList(count, <TypeAnnotation>[], asType)); |
| 162 } |
| 163 beginLiteralString(Token token) { |
| 164 String source = token.value; |
| 165 tree.StringQuoting quoting = StringValidator.quotingFromString(source); |
| 166 push(quoting); |
| 167 push(token); // collect token at the end |
| 168 } |
| 169 handleStringPart(Token token) { |
| 170 push(token); // collect token at the end |
| 171 } |
| 172 endLiteralString(int interpCount) { |
| 173 List parts = popList(2 * interpCount + 1, []); |
| 174 tree.StringQuoting quoting = pop(); |
| 175 List<Expression> members = <Expression>[]; |
| 176 for (var i=0; i<parts.length; i++) { |
| 177 var part = parts[i]; |
| 178 if (part is Expression) { |
| 179 members.add(part); |
| 180 } else { |
| 181 assert(part is Token); |
| 182 DartString str = stringValidator.validateInterpolationPart( |
| 183 part as Token, |
| 184 quoting, |
| 185 isFirst: i == 0, |
| 186 isLast: i == parts.length - 1); |
| 187 members.add(new Literal(new StringConstant(str))); |
| 188 } |
| 189 } |
| 190 push(new StringConcat(members)); |
| 191 } |
| 192 handleStringJuxtaposition(int litCount) { |
| 193 push(new StringConcat(popList(litCount, <Expression>[]))); |
| 194 } |
| 195 endArguments(int count, begin, end) { |
| 196 push(popList(count, <Argument>[])); |
| 197 } |
| 198 handleNoArguments(token) { |
| 199 push(null); |
| 200 } |
| 201 handleNoTypeArguments(token) { |
| 202 push(<TypeAnnotation>[]); |
| 203 } |
| 204 endTypeArguments(int count, t, y) { |
| 205 List<TypeAnnotation> args = <TypeAnnotation>[]; |
| 206 for (var i=0; i<count; i++) { |
| 207 args.add(popTypeAnnotation()); |
| 208 } |
| 209 push(args.reversed.toList(growable:false)); |
| 210 } |
| 211 handleVoidKeyword(token) { |
| 212 push(new Identifier("void")); |
| 213 push(<TypeAnnotation>[]); // prepare for popTypeAnnotation |
| 214 } |
| 215 handleQualified(Token period) { |
| 216 String last = pop(asName); |
| 217 String first = pop(asName); |
| 218 push(new Identifier('$first.$last')); |
| 219 } |
| 220 endSend(t) { |
| 221 List<Argument> arguments = pop(); |
| 222 if (arguments == null) |
| 223 return; // not a function call |
| 224 Expression selector = pop(); |
| 225 push(new CallFunction(selector, arguments)); |
| 226 } |
| 227 endThrowExpression(t, tt) { |
| 228 push(new Throw(pop())); |
| 229 } |
| 230 handleAssignmentExpression(Token token) { |
| 231 Expression right = pop(); |
| 232 Expression left = pop(); |
| 233 push(new Assignment(left, token.value, right)); |
| 234 } |
| 235 handleBinaryExpression(Token token) { |
| 236 Expression right = pop(); |
| 237 Receiver left = pop(); |
| 238 String tokenString = token.stringValue; |
| 239 if (tokenString == '.') { |
| 240 if (right is CallFunction) { |
| 241 String name = (right.callee as Identifier).name; |
| 242 push(new CallMethod(left, name, right.arguments)); |
| 243 } else { |
| 244 push(new FieldExpression(left, (right as Identifier).name)); |
| 245 } |
| 246 } else { |
| 247 push(new BinaryOperator(left, tokenString, right)); |
| 248 } |
| 249 } |
| 250 handleConditionalExpression(question, colon) { |
| 251 Expression elseExpression = pop(); |
| 252 Expression thenExpression = pop(); |
| 253 Expression condition = pop(); |
| 254 push(new Conditional(condition, thenExpression, elseExpression)); |
| 255 } |
| 256 handleIdentifier(Token t) { |
| 257 push(new Identifier(t.value)); |
| 258 } |
| 259 handleOperator(t) { |
| 260 push(new Identifier(t.value)); |
| 261 } |
| 262 handleIndexedExpression(open, close) { |
| 263 Expression index = pop(); |
| 264 Receiver object = pop(); |
| 265 push(new IndexExpression(object, index)); |
| 266 } |
| 267 handleIsOperator(operathor, not, endToken) { |
| 268 TypeAnnotation type = popTypeAnnotation(); |
| 269 Expression exp = pop(); |
| 270 TypeOperator r = new TypeOperator(exp, 'is', type); |
| 271 if (not != null) { |
| 272 push(new UnaryOperator('!', r)); |
| 273 } else { |
| 274 push(r); |
| 275 } |
| 276 } |
| 277 handleAsOperator(operathor, endToken) { |
| 278 TypeAnnotation type = popTypeAnnotation(); |
| 279 Expression exp = pop(); |
| 280 push(new TypeOperator(exp, 'as', type)); |
| 281 } |
| 282 handleLiteralBool(Token t) { |
| 283 bool value = t.value == 'true'; |
| 284 push(new Literal(value ? new TrueConstant() : new FalseConstant())); |
| 285 } |
| 286 handleLiteralDouble(t) { |
| 287 push(new Literal(new DoubleConstant(double.parse(t.value)))); |
| 288 } |
| 289 handleLiteralInt(Token t) { |
| 290 push(new Literal(new IntConstant(int.parse(t.value)))); |
| 291 } |
| 292 handleLiteralNull(t) { |
| 293 push(new Literal(new NullConstant())); |
| 294 } |
| 295 endLiteralSymbol(Token hash, int idCount) { |
| 296 List<Identifier> ids = popList(idCount, <Identifier>[]); |
| 297 push(new LiteralSymbol(ids.map((id) => id.name).join('.'))); |
| 298 } |
| 299 handleLiteralList(int count, begin, constKeyword, end) { |
| 300 List<Expression> exps = popList(count, <Expression>[]); |
| 301 List<TypeAnnotation> types = pop(); |
| 302 assert(types.length <= 1); |
| 303 push(new LiteralList(exps, |
| 304 isConst: constKeyword != null, |
| 305 typeArgument: types.length == 0 ? null : types[0] |
| 306 )); |
| 307 } |
| 308 handleLiteralMap(int count, begin, constKeyword, end) { |
| 309 List<LiteralMapEntry> entries = popList(count, <LiteralMapEntry>[]); |
| 310 List<TypeAnnotation> types = pop(); |
| 311 assert(types.length == 0 || types.length == 2); |
| 312 push(new LiteralMap(entries, |
| 313 isConst: constKeyword != null, |
| 314 typeArguments: types |
| 315 )); |
| 316 } |
| 317 endLiteralMapEntry(colon, endToken) { |
| 318 Expression value = pop(); |
| 319 Expression key = pop(); |
| 320 push(new LiteralMapEntry(key,value)); |
| 321 } |
| 322 handleNamedArgument(colon) { |
| 323 Expression exp = pop(); |
| 324 Identifier name = pop(); |
| 325 push(new NamedArgument(name.name, exp)); |
| 326 } |
| 327 endConstructorReference(Token start, Token period, Token end) { |
| 328 if (period == null) { |
| 329 push(null); // indicate missing constructor name |
| 330 } |
| 331 } |
| 332 handleNewExpression(t) { |
| 333 List<Argument> args = pop(); |
| 334 String constructorName = pop(asName); |
| 335 TypeAnnotation type = popTypeAnnotation(); |
| 336 push(new CallNew(type, args, constructorName: constructorName)); |
| 337 } |
| 338 handleConstExpression(t) { |
| 339 List<Argument> args = pop(); |
| 340 String constructorName = pop(asName); |
| 341 TypeAnnotation type = popTypeAnnotation(); |
| 342 push(new CallNew(type, args, constructorName: constructorName, |
| 343 isConst:true)); |
| 344 } |
| 345 handleParenthesizedExpression(t) { |
| 346 // do nothing, just leave expression on top of stack |
| 347 } |
| 348 handleSuperExpression(t) { |
| 349 push(new SuperReceiver()); |
| 350 } |
| 351 handleThisExpression(t) { |
| 352 push(new This()); |
| 353 } |
| 354 handleUnaryPostfixAssignmentExpression(Token t) { |
| 355 push(new Increment.postfix(pop(), t.value)); |
| 356 } |
| 357 handleUnaryPrefixAssignmentExpression(Token t) { |
| 358 push(new Increment.prefix(pop(), t.value)); |
| 359 } |
| 360 handleUnaryPrefixExpression(Token t) { |
| 361 push(new UnaryOperator(t.value, pop())); |
| 362 } |
| 363 |
| 364 handleFunctionTypedFormalParameter(tok) { |
| 365 // handled in endFormalParameter |
| 366 } |
| 367 endFormalParameter(thisKeyword) { |
| 368 Expression defaultValue = null; |
| 369 var x = pop(); |
| 370 if (x is DefaultValue) { |
| 371 defaultValue = x.expression; |
| 372 x = pop(); |
| 373 } |
| 374 if (x is Parameters) { |
| 375 String name = pop(asName); |
| 376 TypeAnnotation returnType = popTypeAnnotation(); |
| 377 push(new Parameter.function(name, returnType, x, defaultValue)); |
| 378 } else { |
| 379 String name = asName(x); |
| 380 TypeAnnotation type = popTypeAnnotation(); |
| 381 push(new Parameter(name, type:type, defaultValue:defaultValue)); |
| 382 } |
| 383 } |
| 384 handleValuedFormalParameter(eq, tok) { |
| 385 push(new DefaultValue(pop())); |
| 386 } |
| 387 endOptionalFormalParameters(int count, begin, end) { |
| 388 bool isNamed = end.value == '}'; |
| 389 push(popList(count, <Parameter>[], asParameter)); |
| 390 push(isNamed); // Indicate optional parameters to endFormalParameters. |
| 391 } |
| 392 endFormalParameters(count, begin, end) { |
| 393 if (count == 0) { |
| 394 push(new Parameters([])); |
| 395 return; |
| 396 } |
| 397 var last = pop(); // Detect if optional parameters are present. |
| 398 if (last is bool) { // See endOptionalFormalParameters. |
| 399 List<Parameter> optional = pop(); |
| 400 List<Parameter> required = popList(count-1, <Parameter>[], asParameter); |
| 401 push(new Parameters(required, optional, last)); |
| 402 } else { |
| 403 // No optional parameters. |
| 404 List<Parameter> required = popList(count-1, <Parameter>[], asParameter); |
| 405 required.add(last); |
| 406 push(new Parameters(required)); |
| 407 } |
| 408 } |
| 409 handleNoFormalParameters(tok) { |
| 410 push(new Parameters([])); |
| 411 } |
| 412 endUnamedFunction(t) { |
| 413 Statement body = pop(); |
| 414 Parameters parameters = pop(); |
| 415 push(new FunctionExpression(parameters, body)); |
| 416 } |
| 417 handleNoType(Token token) { |
| 418 push(null); |
| 419 } |
| 420 |
| 421 endReturnStatement(bool hasExpression, begin, end) { |
| 422 // This is also called for functions whose body is "=> expression" |
| 423 if (hasExpression) { |
| 424 push(new Return(pop())); |
| 425 } else { |
| 426 push(new Return()); |
| 427 } |
| 428 } |
| 429 |
| 430 endExpressionStatement(Token token) { |
| 431 push(new ExpressionStatement(pop())); |
| 432 } |
| 433 |
| 434 endDoWhileStatement(Token doKeyword, Token whileKeyword, Token end) { |
| 435 Expression condition = pop(); |
| 436 Statement body = pop(); |
| 437 push(new DoWhile(body, condition)); |
| 438 } |
| 439 |
| 440 endWhileStatement(Token whileKeyword, Token end) { |
| 441 Statement body = pop(); |
| 442 Expression condition = pop(); |
| 443 push(new While(condition, body)); |
| 444 } |
| 445 |
| 446 endBlock(int count, Token begin, Token end) { |
| 447 push(new Block(popList(count, <Statement>[]))); |
| 448 } |
| 449 |
| 450 endRethrowStatement(Token throwToken, Token endToken) { |
| 451 push(new Rethrow()); |
| 452 } |
| 453 |
| 454 endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) { |
| 455 Statement finallyBlock = null; |
| 456 if (finallyKeyword != null) { |
| 457 finallyBlock = pop(); |
| 458 } |
| 459 List<CatchBlock> catchBlocks = popList(catchCount, <CatchBlock>[]); |
| 460 Statement tryBlock = pop(); |
| 461 push(new Try(tryBlock, catchBlocks, finallyBlock)); |
| 462 } |
| 463 |
| 464 void handleCatchBlock(Token onKeyword, Token catchKeyword) { |
| 465 Statement block = pop(); |
| 466 String exceptionVar = null; |
| 467 String stackVar = null; |
| 468 if (catchKeyword != null) { |
| 469 Parameters params = pop(); |
| 470 exceptionVar = params.requiredParameters[0].name; |
| 471 if (params.requiredParameters.length > 1) { |
| 472 stackVar = params.requiredParameters[1].name; |
| 473 } |
| 474 } |
| 475 TypeAnnotation type = onKeyword == null ? null : pop(); |
| 476 push(new CatchBlock(block, |
| 477 onType: type, |
| 478 exceptionVar: exceptionVar, |
| 479 stackVar: stackVar |
| 480 )); |
| 481 } |
| 482 |
| 483 endSwitchStatement(Token switchKeyword, Token end) { |
| 484 List<SwitchCase> cases = pop(); |
| 485 Expression expression = pop(); |
| 486 push(new Switch(expression, cases)); |
| 487 } |
| 488 |
| 489 endSwitchBlock(int caseCount, Token begin, Token end) { |
| 490 push(popList(caseCount, <SwitchCase>[])); |
| 491 } |
| 492 |
| 493 handleSwitchCase(int labelCount, int caseCount, Token defaultKeyword, |
| 494 int statementCount, Token first, Token end) { |
| 495 List<Statement> statements = popList(statementCount, <Statement>[]); |
| 496 List<Expression> cases = popList(caseCount, <Expression>[]); |
| 497 if (defaultKeyword != null) { |
| 498 cases = null; |
| 499 } |
| 500 push(new SwitchCase(cases, statements)); |
| 501 } |
| 502 |
| 503 handleCaseMatch(Token caseKeyword, Token colon) { |
| 504 // do nothing, leave case expression on stack |
| 505 } |
| 506 |
| 507 handleBreakStatement(bool hasTarget, Token breakKeyword, Token end) { |
| 508 String target = hasTarget ? pop(asName) : null; |
| 509 push(new Break(target)); |
| 510 } |
| 511 |
| 512 handleContinueStatement(bool hasTarget, Token continueKeyword, Token end) { |
| 513 String target = hasTarget ? pop(asName) : null; |
| 514 push(new Continue(target)); |
| 515 } |
| 516 |
| 517 handleEmptyStatement(Token token) { |
| 518 push(new EmptyStatement()); |
| 519 } |
| 520 |
| 521 |
| 522 VariableDeclaration asVariableDeclaration(x) { |
| 523 if (x is VariableDeclaration) |
| 524 return x; |
| 525 if (x is Identifier) |
| 526 return new VariableDeclaration(x.name); |
| 527 throw "Not a variable definition: ${x.runtimeType}"; |
| 528 } |
| 529 |
| 530 endVariablesDeclaration(int count, Token end) { |
| 531 List<VariableDeclaration> variables = |
| 532 popList(count, <VariableDeclaration>[], asVariableDeclaration); |
| 533 TypeAnnotation type = popTypeAnnotation(); |
| 534 push(new VariableDeclarations(variables, |
| 535 type: type, |
| 536 isFinal: false, // TODO(asgerf): Parse modifiers. |
| 537 isConst: false |
| 538 )); |
| 539 } |
| 540 |
| 541 endInitializer(Token assign) { |
| 542 Expression init = pop(); |
| 543 String name = pop(asName); |
| 544 push(new VariableDeclaration(name, init)); |
| 545 } |
| 546 |
| 547 endIfStatement(Token ifToken, Token elseToken) { |
| 548 Statement elsePart = (elseToken == null) ? null : pop(); |
| 549 Statement thenPart = pop(); |
| 550 Expression condition = pop(); |
| 551 push(new If(condition, thenPart, elsePart)); |
| 552 } |
| 553 |
| 554 endForStatement(int updateCount, Token begin, Token end) { |
| 555 Statement body = pop(); |
| 556 List<Expression> updates = popList(updateCount, <Expression>[]); |
| 557 ExpressionStatement condition = pop(); // parsed as expression statement |
| 558 Expression exp = condition == null ? null : condition.expression; |
| 559 Node initializer = pop(); |
| 560 push(new For(initializer, exp, updates, body)); |
| 561 } |
| 562 |
| 563 handleNoExpression(Token token) { |
| 564 push(null); |
| 565 } |
| 566 |
| 567 endForIn(Token begin, Token inKeyword, Token end) { |
| 568 Statement body = pop(); |
| 569 Expression exp = pop(); |
| 570 Node declaredIdentifier = pop(); |
| 571 push(new ForIn(declaredIdentifier, exp, body)); |
| 572 } |
| 573 |
| 574 handleAssertStatement(Token assertKeyword, Token semicolonToken) { |
| 575 Expression exp = pop(); |
| 576 Expression call = new CallFunction(new Identifier("assert"), [exp]); |
| 577 push(new ExpressionStatement(call)); |
| 578 } |
| 579 |
| 580 endLabeledStatement(int labelCount) { |
| 581 Statement statement = pop(); |
| 582 for (int i=0; i<labelCount; i++) { |
| 583 String label = pop(asName); |
| 584 statement = new LabeledStatement(label, statement); |
| 585 } |
| 586 push(statement); |
| 587 } |
| 588 |
| 589 endFunctionDeclaration(Token end) { |
| 590 Statement body = pop(); |
| 591 Parameters parameters = pop(); |
| 592 String name = pop(asName); |
| 593 TypeAnnotation returnType = popTypeAnnotation(); |
| 594 push(new FunctionStatement(name, parameters, body, returnType)); |
| 595 } |
| 596 |
| 597 endFunctionBody(int count, Token begin, Token end) { |
| 598 push(new Block(popList(count, <Statement>[]))); |
| 599 } |
| 600 } |
| 601 |
| 602 class DefaultValue { |
| 603 final Expression expression; |
| 604 DefaultValue(this.expression); |
| 605 } |
| 606 |
| 607 /// Compares ASTs for structural equality. |
| 608 void checkDeepEqual(x, y) { |
| 609 if (x is List && y is List) { |
| 610 if (x.length != y.length) |
| 611 return; |
| 612 for (var i=0; i<x.length; i++) { |
| 613 checkDeepEqual(x[i], y[i]); |
| 614 } |
| 615 } |
| 616 else if (x is Node && y is Node) { |
| 617 if (x.runtimeType != y.runtimeType) |
| 618 throw new Error(); |
| 619 InstanceMirror xm = reflect(x); |
| 620 InstanceMirror ym = reflect(y); |
| 621 for (Symbol name in xm.type.instanceMembers.keys) { |
| 622 if (reflectClass(Object).declarations.containsKey(name)) { |
| 623 continue; // do not check things from Object, such as hashCode |
| 624 } |
| 625 MethodMirror mm = xm.type.instanceMembers[name]; |
| 626 if (mm.isGetter) { |
| 627 var xv = xm.getField(name).reflectee; |
| 628 var yv = ym.getField(name).reflectee; |
| 629 checkDeepEqual(xv,yv); |
| 630 } |
| 631 } |
| 632 } |
| 633 else if (x is PrimitiveConstant && y is PrimitiveConstant) { |
| 634 checkDeepEqual(x.value, y.value); |
| 635 } |
| 636 else if (x is DartString && y is DartString) { |
| 637 if (x.slowToString() != y.slowToString()) { |
| 638 throw new Error(); |
| 639 } |
| 640 } |
| 641 else { |
| 642 if (x != y) { |
| 643 throw new Error(); |
| 644 } |
| 645 } |
| 646 } |
| 647 |
| 648 Expression parseExpression(String code) { |
| 649 SourceFile file = new StringSourceFile('', code); |
| 650 Scanner scan = new Scanner(file); |
| 651 Token tok = scan.tokenize(); |
| 652 AstBuilder builder = new AstBuilder(); |
| 653 Parser parser = new Parser(builder); |
| 654 tok = parser.parseExpression(tok); |
| 655 if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) { |
| 656 throw "Parse error in $code"; |
| 657 } |
| 658 return builder.pop(); |
| 659 } |
| 660 Statement parseStatement(String code) { |
| 661 SourceFile file = new StringSourceFile('', code); |
| 662 Scanner scan = new Scanner(file); |
| 663 Token tok = scan.tokenize(); |
| 664 AstBuilder builder = new AstBuilder(); |
| 665 Parser parser = new Parser(builder); |
| 666 tok = parser.parseStatement(tok); |
| 667 if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) { |
| 668 throw "Parse error in $code"; |
| 669 } |
| 670 return builder.pop(); |
| 671 } |
| 672 |
| 673 String unparseExpression(Expression exp) { |
| 674 StringBuffer buf = new StringBuffer(); |
| 675 new Unparser(buf).writeExpression(exp); |
| 676 return buf.toString(); |
| 677 } |
| 678 String unparseStatement(Statement stmt) { |
| 679 StringBuffer buf = new StringBuffer(); |
| 680 new Unparser(buf).writeStatement(stmt); |
| 681 return buf.toString(); |
| 682 } |
| 683 |
| 684 /// Parses [code], unparses the resulting AST, then parses the unparsed text. |
| 685 /// The ASTs from the first and second parse are then compared for structural |
| 686 /// equality. Alternatively, if [expected] is not an empty string, the second |
| 687 /// parse must match the AST of parsing [expected]. |
| 688 void checkFn(String code, String expected, Function parse, Function unparse) { |
| 689 String unparsed = ""; |
| 690 try { |
| 691 var firstParse = parse(code); |
| 692 unparsed = unparse(firstParse); |
| 693 var secondParse = parse(unparsed); |
| 694 var baseline = expected == "" ? firstParse : parse(expected); |
| 695 checkDeepEqual(baseline, secondParse); |
| 696 } catch (e, stack) { |
| 697 Expect.fail('"$code" was unparsed as "$unparsed"'); |
| 698 } |
| 699 } |
| 700 |
| 701 void checkExpression(String code, [String expected=""]) { |
| 702 checkFn(code, expected, parseExpression, unparseExpression); |
| 703 } |
| 704 void checkStatement(String code, [String expected=""]) { |
| 705 checkFn(code, expected, parseStatement, unparseStatement); |
| 706 } |
| 707 |
| 708 void debugTokens(String code) { |
| 709 SourceFile file = new StringSourceFile('', code); |
| 710 Scanner scan = new Scanner(file); |
| 711 Token tok = scan.tokenize(); |
| 712 while (tok.next != tok) { |
| 713 print(tok.toString()); |
| 714 tok = tok.next; |
| 715 } |
| 716 } |
| 717 |
| 718 void main() { |
| 719 // To check if these tests are effective, one should manually alter |
| 720 // something in [Unparser] and see if a test fails. |
| 721 |
| 722 checkExpression(" a + b + c"); |
| 723 checkExpression("(a + b) + c"); |
| 724 checkExpression(" a + (b + c)"); |
| 725 |
| 726 checkExpression(" a + b - c"); |
| 727 checkExpression("(a + b) - c"); |
| 728 checkExpression(" a + (b - c)"); |
| 729 |
| 730 checkExpression(" a - b + c"); |
| 731 checkExpression("(a - b) + c"); |
| 732 checkExpression(" a - (b + c)"); |
| 733 |
| 734 checkExpression(" a * b + c"); |
| 735 checkExpression("(a * b) + c"); |
| 736 checkExpression(" a * (b + c)"); |
| 737 |
| 738 checkExpression(" a + b * c"); |
| 739 checkExpression("(a + b) * c"); |
| 740 checkExpression(" a + (b * c)"); |
| 741 |
| 742 checkExpression(" a * b * c"); |
| 743 checkExpression("(a * b) * c"); |
| 744 checkExpression(" a * (b * c)"); |
| 745 |
| 746 checkExpression("a is T"); |
| 747 checkExpression("a is! T"); |
| 748 checkExpression("!(a is T)"); |
| 749 |
| 750 checkExpression("a is T.x"); |
| 751 checkExpression("a is! T.x"); |
| 752 checkExpression("!(a is T.x)"); |
| 753 checkExpression("!(a is T).x"); |
| 754 |
| 755 checkExpression("a as T.x"); |
| 756 checkExpression("(a as T).x"); |
| 757 |
| 758 checkExpression("a == b"); |
| 759 checkExpression("a != b"); |
| 760 checkExpression("!(a == b)", "a != b"); |
| 761 |
| 762 checkExpression("a && b ? c : d"); |
| 763 checkExpression("(a && b) ? c : d"); |
| 764 checkExpression("a && (b ? c : d)"); |
| 765 |
| 766 checkExpression("a || b ? c : d"); |
| 767 checkExpression("(a || b) ? c : d"); |
| 768 checkExpression("a || (b ? c : d)"); |
| 769 |
| 770 checkExpression(" a ? b : c && d"); |
| 771 checkExpression(" a ? b : (c && d)"); |
| 772 checkExpression("(a ? b : c) && d"); |
| 773 |
| 774 checkExpression(" a ? b : c = d"); |
| 775 checkExpression(" a ? b : (c = d)"); |
| 776 |
| 777 checkExpression("(a == b) == c"); |
| 778 checkExpression("a == (b == c)"); |
| 779 |
| 780 checkExpression(" a < b == c"); |
| 781 checkExpression("(a < b) == c"); |
| 782 checkExpression(" a < (b == c)"); |
| 783 |
| 784 checkExpression(" a == b < c"); |
| 785 checkExpression("(a == b) < c"); |
| 786 checkExpression(" a == (b < c)"); |
| 787 |
| 788 checkExpression("x.f()"); |
| 789 checkExpression("(x.f)()"); |
| 790 |
| 791 checkExpression("x.f()()"); |
| 792 checkExpression("(x.f)()()"); |
| 793 |
| 794 checkExpression("x.f().g()"); |
| 795 checkExpression("(x.f)().g()"); |
| 796 |
| 797 checkExpression("x.f()"); |
| 798 checkExpression("x.f(1 + 2)"); |
| 799 checkExpression("x.f(1 + 2, 3 + 4)"); |
| 800 checkExpression("x.f(1 + 2, foo:3 + 4)"); |
| 801 checkExpression("x.f(1 + 2, foo:3 + 4, bar: 5)"); |
| 802 checkExpression("x.f(foo:3 + 4)"); |
| 803 checkExpression("x.f(foo:3 + 4, bar: 5)"); |
| 804 |
| 805 checkExpression("x.f.g.h"); |
| 806 checkExpression("(x.f).g.h"); |
| 807 checkExpression("(x.f.g).h"); |
| 808 |
| 809 checkExpression(" a = b + c"); |
| 810 checkExpression(" a = (b + c)"); |
| 811 checkExpression("(a = b) + c"); |
| 812 |
| 813 checkExpression("a + (b = c)"); |
| 814 |
| 815 checkExpression("dx * dx + dy * dy < r * r", |
| 816 "((dx * dx) + (dy * dy)) < (r * r)"); |
| 817 checkExpression("mid = left + right << 1", |
| 818 "mid = ((left + right) << 1)"); |
| 819 checkExpression("a + b % c * -d ^ e - f ~/ x & ++y / z++ | w > a ? b : c"); |
| 820 checkExpression("a + b % c * -d ^ (e - f) ~/ x & ++y / z++ | w > a ? b : c"); |
| 821 |
| 822 checkExpression("'foo'"); |
| 823 checkExpression("'foo' 'bar'", "'foobar'"); |
| 824 |
| 825 checkExpression("{}.length"); |
| 826 checkExpression("{x: 1+2}.length"); |
| 827 checkExpression("<String,int>{}.length"); |
| 828 checkExpression("<String,int>{x: 1+2}.length"); |
| 829 |
| 830 checkExpression("[].length"); |
| 831 checkExpression("[1+2].length"); |
| 832 checkExpression("<num>[].length"); |
| 833 checkExpression("<num>[1+2].length"); |
| 834 |
| 835 checkExpression("x + -y"); |
| 836 checkExpression("x + --y"); |
| 837 checkExpression("x++ + y"); |
| 838 checkExpression("x + ++y"); |
| 839 checkExpression("x-- - y"); |
| 840 checkExpression("x-- - -y"); |
| 841 checkExpression("x - --y"); |
| 842 |
| 843 checkExpression("x && !y"); |
| 844 checkExpression("!x && y"); |
| 845 checkExpression("!(x && y)"); |
| 846 |
| 847 checkExpression(" super + 1 * 2"); |
| 848 checkExpression("(super + 1) * 2"); |
| 849 checkExpression(" super + (1 * 2)"); |
| 850 checkExpression("x + -super"); |
| 851 checkExpression("x-- - -super"); |
| 852 checkExpression("x - -super"); |
| 853 checkExpression("x && !super"); |
| 854 |
| 855 checkExpression("super.f(1, 2) + 3"); |
| 856 checkExpression("super.f + 3"); |
| 857 |
| 858 checkExpression(r"'foo\nbar'"); |
| 859 checkExpression(r"'foo\r\nbar'"); |
| 860 checkExpression(r"'foo\rbar'"); |
| 861 checkExpression(r"'foo\'bar'"); |
| 862 checkExpression(r"""'foo"bar'"""); |
| 863 checkExpression(r"r'foo\nbar'"); |
| 864 checkExpression("''"); |
| 865 checkExpression("r''"); |
| 866 |
| 867 var sq = "'"; |
| 868 var dq = '"'; |
| 869 checkExpression("'$dq$dq' \"$sq$sq\""); |
| 870 checkExpression("'$dq$dq$dq$dq' \"$sq$sq$sq$sq\""); |
| 871 checkExpression(r"'\$\$\$\$\$\$\$\$\$'"); |
| 872 checkExpression("'$dq$dq$dq' '\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n' \"$sq$sq$sq\""); |
| 873 checkExpression("'$dq$dq$dq' '\\r\\r\\r\\r\\r\\r\\r\\r\\r\\r' \"$sq$sq$sq\""); |
| 874 checkExpression("'$dq$dq$dq' '\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n' \"$sq$sq$sq\""); |
| 875 |
| 876 checkExpression(r"'$foo'"); |
| 877 checkExpression(r"'${foo}x'"); |
| 878 checkExpression(r"'${foo}x\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'"); |
| 879 checkExpression(r"'abc' '${foo}' r'\\\\\\\'"); |
| 880 |
| 881 checkExpression(r"'${$x}'"); |
| 882 checkExpression(r"'${$x}y'"); |
| 883 |
| 884 checkStatement("var x, y, z;"); |
| 885 checkStatement("final x, y, z;"); |
| 886 checkStatement("dynamic x, y, z;"); |
| 887 checkStatement("String x, y, z;"); |
| 888 checkStatement("List<int> x, y, z;"); |
| 889 checkStatement("final dynamic x, y, z;"); |
| 890 checkStatement("final String x, y, z;"); |
| 891 checkStatement("final List<int> x, y, z;"); |
| 892 |
| 893 checkStatement("var x = y, z;"); |
| 894 checkStatement("var x, y = z;"); |
| 895 checkStatement("var x = y = z;"); |
| 896 |
| 897 // Note: We sometimes have to pass an expected string to account for |
| 898 // block flattening which does not preserve structural AST equality |
| 899 checkStatement("if (x) if (y) foo(); else bar(); "); |
| 900 checkStatement("if (x) { if (y) foo(); } else bar(); "); |
| 901 checkStatement("if (x) { if (y) foo(); else bar(); }", |
| 902 "if (x) if (y) foo(); else bar(); "); |
| 903 |
| 904 checkStatement("if (x) while (y) if (z) foo(); else bar(); "); |
| 905 checkStatement("if (x) while (y) { if (z) foo(); } else bar(); "); |
| 906 checkStatement("if (x) while (y) { if (z) foo(); else bar(); }", |
| 907 "if (x) while (y) if (z) foo(); else bar(); "); |
| 908 |
| 909 checkStatement("{var x = 1; {var x = 2;} return x;}"); |
| 910 checkStatement("{var x = 1; {x = 2;} return x;}", |
| 911 "{var x = 1; x = 2; return x;}"); |
| 912 |
| 913 checkStatement("if (x) {var x = 1;}"); |
| 914 |
| 915 checkStatement("({'foo': 1}).bar();"); |
| 916 checkStatement("({'foo': 1}).length;"); |
| 917 checkStatement("({'foo': 1}).length + 1;"); |
| 918 checkStatement("({'foo': 1})['foo'].toString();"); |
| 919 checkStatement("({'foo': 1})['foo'] = 3;"); |
| 920 checkStatement("({'foo': 1}['foo']());"); |
| 921 checkStatement("({'foo': 1}['foo'])();"); |
| 922 checkStatement("({'foo': 1})['foo'].x++;"); |
| 923 checkStatement("({'foo': 1}) is Map;"); |
| 924 checkStatement("({'foo': 1}) as Map;"); |
| 925 checkStatement("({'foo': 1}) is util.Map;"); |
| 926 checkStatement("({'foo': 1}) + 1;"); |
| 927 |
| 928 checkStatement("[1].bar();"); |
| 929 checkStatement("1.bar();"); |
| 930 checkStatement("'foo'.bar();"); |
| 931 |
| 932 checkStatement("do while(x); while (y);"); |
| 933 checkStatement("{do; while(x); while (y);}"); |
| 934 |
| 935 } |
| 936 |
| OLD | NEW |