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