| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2012, 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 class Printer implements NodeVisitor { |
| 6 final bool shouldCompressOutput = false; |
| 7 leg.Compiler compiler; |
| 8 var positionElement; |
| 9 leg.CodeBuffer outBuffer; |
| 10 int indentLevel = 0; |
| 11 bool inForInit = false; |
| 12 bool atStatementBegin = false; |
| 13 |
| 14 Printer(this.compiler, this.positionElement) |
| 15 : outBuffer = new leg.CodeBuffer(); |
| 16 |
| 17 void spaceOut() { |
| 18 if (!shouldCompressOutput) out(" "); |
| 19 } |
| 20 void lineOut() { |
| 21 if (!shouldCompressOutput) out("\n"); |
| 22 } |
| 23 |
| 24 String lastAddedString = null; |
| 25 int get lastCharCode() { |
| 26 if (lastAddedString == null) return 0; |
| 27 assert(lastAddedString.length != ""); |
| 28 return lastAddedString.charCodeAt(lastAddedString.length - 1); |
| 29 } |
| 30 |
| 31 void out(String str) { |
| 32 if (str != "") { |
| 33 outBuffer.add(str); |
| 34 lastAddedString = str; |
| 35 } |
| 36 } |
| 37 void outLn(String str) { |
| 38 out(str); |
| 39 lineOut(); |
| 40 } |
| 41 void outIndent(String str) { indent(); out(str); } |
| 42 void outIndentLn(String str) { indent(); outLn(str); } |
| 43 void indent() { |
| 44 if (!shouldCompressOutput) { |
| 45 for (int i = 0; i < indentLevel; i++) out(" "); |
| 46 } |
| 47 } |
| 48 |
| 49 void recordSourcePosition(var position) { |
| 50 if (position != null) { |
| 51 outBuffer.setSourceLocation(positionElement, position); |
| 52 } |
| 53 } |
| 54 |
| 55 visit(Node node) { |
| 56 recordSourcePosition(node.sourcePosition); |
| 57 node.accept(this); |
| 58 recordSourcePosition(node.endSourcePosition); |
| 59 } |
| 60 |
| 61 visitCommaSeparated(List<Node> nodes, int hasRequiredType, |
| 62 [bool newInForInit, bool newAtStatementBegin]) { |
| 63 for (int i = 0; i < nodes.length; i++) { |
| 64 if (i != 0) { |
| 65 atStatementBegin = false; |
| 66 out(","); |
| 67 spaceOut(); |
| 68 } |
| 69 visitNestedExpression(nodes[i], hasRequiredType, |
| 70 newInForInit, newAtStatementBegin); |
| 71 } |
| 72 } |
| 73 |
| 74 visitAll(List<Node> nodes) { |
| 75 nodes.forEach(visit); |
| 76 } |
| 77 |
| 78 visitProgram(Program program) { |
| 79 visitAll(program.body); |
| 80 } |
| 81 |
| 82 bool blockBody(Node body, [bool needsSeparation, bool needsNewline]) { |
| 83 if (body is Block) { |
| 84 spaceOut(); |
| 85 blockOut(body, false, needsNewline); |
| 86 return true; |
| 87 } |
| 88 if (shouldCompressOutput && needsSeparation) { |
| 89 // If [shouldCompressOutput] is false, then the 'lineOut' will insert |
| 90 // the separation. |
| 91 out(" "); |
| 92 } else { |
| 93 lineOut(); |
| 94 } |
| 95 indentLevel++; |
| 96 visit(body); |
| 97 indentLevel--; |
| 98 return false; |
| 99 } |
| 100 |
| 101 void blockOutWithoutBraces(Node node) { |
| 102 if (node is Block) { |
| 103 node.statements.forEach(blockOutWithoutBraces); |
| 104 } else { |
| 105 visit(node); |
| 106 } |
| 107 } |
| 108 |
| 109 void blockOut(Block node, bool shouldIndent, bool needsNewline) { |
| 110 if (shouldIndent) indent(); |
| 111 out("{"); |
| 112 lineOut(); |
| 113 indentLevel++; |
| 114 node.statements.forEach(blockOutWithoutBraces); |
| 115 indentLevel--; |
| 116 indent(); |
| 117 out("}"); |
| 118 if (needsNewline) lineOut(); |
| 119 } |
| 120 |
| 121 visitBlock(Block block) { |
| 122 blockOut(block, true, true); |
| 123 } |
| 124 |
| 125 visitExpressionStatement(ExpressionStatement expressionStatement) { |
| 126 indent(); |
| 127 visitNestedExpression(expressionStatement.expression, EXPRESSION, |
| 128 newInForInit: false, newAtStatementBegin: true); |
| 129 outLn(";"); |
| 130 } |
| 131 |
| 132 visitEmptyStatement(EmptyStatement nop) { |
| 133 outIndentLn(";"); |
| 134 } |
| 135 |
| 136 void ifOut(If node, bool shouldIndent) { |
| 137 Node then = node.then; |
| 138 Node elsePart = node.otherwise; |
| 139 bool hasElse = node.hasElse; |
| 140 // Handle dangling elses. |
| 141 // If the then-branch is an if, which has no else-branch, but we do |
| 142 // have one, then we need to put the nested if into braces. |
| 143 if (hasElse && then is If) { |
| 144 Node nested = then; |
| 145 do { |
| 146 If nestedIf = nested; |
| 147 if (!nestedIf.hasElse) { |
| 148 then = new Block(<Statement>[then]); |
| 149 break; |
| 150 } |
| 151 nested = nestedIf.otherwise; |
| 152 } while (nested is If); |
| 153 } |
| 154 if (then is If && !(then as If).hasElse && hasElse) { |
| 155 then = new Block(<Statement>[then]); |
| 156 } |
| 157 if (shouldIndent) indent(); |
| 158 out("if"); |
| 159 spaceOut(); |
| 160 out("("); |
| 161 visitNestedExpression(node.condition, EXPRESSION, |
| 162 newInForInit: false, newAtStatementBegin: false); |
| 163 out(")"); |
| 164 bool thenWasBlock = |
| 165 blockBody(then, needsSeparation: false, needsNewline: !hasElse); |
| 166 if (hasElse) { |
| 167 if (thenWasBlock) { |
| 168 spaceOut(); |
| 169 } else { |
| 170 indent(); |
| 171 } |
| 172 out("else"); |
| 173 if (elsePart is If) { |
| 174 out(" "); |
| 175 ifOut(elsePart, false); |
| 176 } else { |
| 177 blockBody(elsePart, needsSeparation: true, needsNewline: true); |
| 178 } |
| 179 } |
| 180 } |
| 181 |
| 182 visitIf(If node) { |
| 183 ifOut(node, true); |
| 184 } |
| 185 |
| 186 visitFor(For loop) { |
| 187 outIndent("for"); |
| 188 spaceOut(); |
| 189 out("("); |
| 190 if (loop.init !== null) { |
| 191 visitNestedExpression(loop.init, EXPRESSION, |
| 192 newInForInit: true, newAtStatementBegin: false); |
| 193 } |
| 194 out(";"); |
| 195 if (loop.condition !== null) { |
| 196 spaceOut(); |
| 197 visitNestedExpression(loop.condition, EXPRESSION, |
| 198 newInForInit: false, newAtStatementBegin: false); |
| 199 } |
| 200 out(";"); |
| 201 if (loop.update !== null) { |
| 202 spaceOut(); |
| 203 visitNestedExpression(loop.update, EXPRESSION, |
| 204 newInForInit: false, newAtStatementBegin: false); |
| 205 } |
| 206 out(")"); |
| 207 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| 208 } |
| 209 |
| 210 visitForIn(ForIn loop) { |
| 211 outIndent("for"); |
| 212 spaceOut(); |
| 213 out("("); |
| 214 visitNestedExpression(loop.leftHandSide, EXPRESSION, |
| 215 newInForInit: true, newAtStatementBegin: false); |
| 216 out(" in "); |
| 217 visitNestedExpression(loop.object, EXPRESSION, |
| 218 newInForInit: false, newAtStatementBegin: false); |
| 219 out(")"); |
| 220 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| 221 } |
| 222 |
| 223 visitWhile(While loop) { |
| 224 outIndent("while"); |
| 225 spaceOut(); |
| 226 out("("); |
| 227 visitNestedExpression(loop.condition, EXPRESSION, |
| 228 newInForInit: false, newAtStatementBegin: false); |
| 229 out(")"); |
| 230 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| 231 } |
| 232 |
| 233 visitDo(Do loop) { |
| 234 outIndent("do"); |
| 235 if (blockBody(loop.body, needsSeparation: true, needsNewline: false)) { |
| 236 spaceOut(); |
| 237 } else { |
| 238 indent(); |
| 239 } |
| 240 out("while"); |
| 241 spaceOut(); |
| 242 out("("); |
| 243 visitNestedExpression(loop.condition, EXPRESSION, |
| 244 newInForInit: false, newAtStatementBegin: false); |
| 245 outLn(");"); |
| 246 } |
| 247 |
| 248 visitContinue(Continue node) { |
| 249 if (node.targetLabel == null) { |
| 250 outIndentLn("continue;"); |
| 251 } else { |
| 252 outIndentLn("continue ${node.targetLabel};"); |
| 253 } |
| 254 } |
| 255 |
| 256 visitBreak(Break node) { |
| 257 if (node.targetLabel == null) { |
| 258 outIndentLn("break;"); |
| 259 } else { |
| 260 outIndentLn("break ${node.targetLabel};"); |
| 261 } |
| 262 } |
| 263 |
| 264 visitReturn(Return node) { |
| 265 if (node.value == null) { |
| 266 outIndentLn("return;"); |
| 267 } else { |
| 268 outIndent("return "); |
| 269 visitNestedExpression(node.value, EXPRESSION, |
| 270 newInForInit: false, newAtStatementBegin: false); |
| 271 outLn(";"); |
| 272 } |
| 273 } |
| 274 |
| 275 visitThrow(Throw node) { |
| 276 outIndent("throw "); |
| 277 visitNestedExpression(node.expression, EXPRESSION, |
| 278 newInForInit: false, newAtStatementBegin: false); |
| 279 outLn(";"); |
| 280 } |
| 281 |
| 282 visitTry(Try node) { |
| 283 outIndent("try"); |
| 284 blockBody(node.body, needsSeparation: true, needsNewline: false); |
| 285 spaceOut(); |
| 286 if (node.catchPart !== null) { |
| 287 visit(node.catchPart); |
| 288 } |
| 289 if (node.finallyPart !== null) { |
| 290 spaceOut(); |
| 291 out("finally"); |
| 292 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); |
| 293 } else { |
| 294 lineOut(); |
| 295 } |
| 296 } |
| 297 |
| 298 visitCatch(Catch node) { |
| 299 spaceOut(); |
| 300 out("catch"); |
| 301 spaceOut(); |
| 302 out("("); |
| 303 visitNestedExpression(node.declaration, EXPRESSION, |
| 304 newInForInit: false, newAtStatementBegin: false); |
| 305 out(")"); |
| 306 blockBody(node.body, needsSeparation: false, needsNewline: true); |
| 307 } |
| 308 |
| 309 visitSwitch(Switch node) { |
| 310 outIndent("switch"); |
| 311 spaceOut(); |
| 312 out("("); |
| 313 visitNestedExpression(node.key, EXPRESSION, |
| 314 newInForInit: false, newAtStatementBegin: false); |
| 315 out(")"); |
| 316 spaceOut(); |
| 317 outLn("{"); |
| 318 indentLevel++; |
| 319 visitAll(node.cases); |
| 320 indentLevel--; |
| 321 outIndentLn("}"); |
| 322 } |
| 323 |
| 324 visitCase(Case node) { |
| 325 outIndent("case "); |
| 326 visitNestedExpression(node.expression, EXPRESSION, |
| 327 newInForInit: false, newAtStatementBegin: false); |
| 328 outLn(":"); |
| 329 if (!node.body.statements.isEmpty()) { |
| 330 indentLevel++; |
| 331 blockOutWithoutBraces(node.body); |
| 332 indentLevel--; |
| 333 } |
| 334 } |
| 335 |
| 336 visitDefault(Default node) { |
| 337 outIndentLn("default:"); |
| 338 if (!node.body.statements.isEmpty()) { |
| 339 indentLevel++; |
| 340 blockOutWithoutBraces(node.body); |
| 341 indentLevel--; |
| 342 } |
| 343 } |
| 344 |
| 345 visitLabeledStatement(LabeledStatement node) { |
| 346 outIndent("${node.label}:"); |
| 347 blockBody(node.body, needsSeparation: false, needsNewline: true); |
| 348 } |
| 349 |
| 350 void functionOut(Fun fun, Node name) { |
| 351 out("function"); |
| 352 if (name != null) { |
| 353 out(" "); |
| 354 // Name must be a [Decl]. Therefore only test for primary expressions. |
| 355 visitNestedExpression(name, PRIMARY, |
| 356 newInForInit: false, newAtStatementBegin: false); |
| 357 } |
| 358 out("("); |
| 359 if (fun.params != null) { |
| 360 visitCommaSeparated(fun.params, PRIMARY, |
| 361 newInForInit: false, newAtStatementBegin: false); |
| 362 } |
| 363 out(")"); |
| 364 blockBody(fun.body, needsSeparation: false, needsNewline: false); |
| 365 } |
| 366 |
| 367 visitFunctionDeclaration(FunctionDeclaration declaration) { |
| 368 indent(); |
| 369 functionOut(declaration.function, declaration.name); |
| 370 lineOut(); |
| 371 } |
| 372 |
| 373 visitNestedExpression(Expression node, int requiredPrecedence, |
| 374 [bool newInForInit, bool newAtStatementBegin]) { |
| 375 bool needsParentheses = |
| 376 // a - (b + c). |
| 377 (requiredPrecedence != EXPRESSION && |
| 378 node.precedenceLevel < requiredPrecedence) || |
| 379 // for (a = (x in o); ... ; ... ) { ... } |
| 380 (newInForInit && node is Binary && (node as Binary).op == "in") || |
| 381 // (function() { ... })(). |
| 382 // ({a: 2, b: 3}.toString()). |
| 383 (newAtStatementBegin && (node is NamedFunction || |
| 384 node is Fun || |
| 385 node is ObjectInitialization)); |
| 386 if (needsParentheses) { |
| 387 inForInit = false; |
| 388 atStatementBegin = false; |
| 389 out("("); |
| 390 visit(node); |
| 391 out(")"); |
| 392 } else { |
| 393 inForInit = newInForInit; |
| 394 atStatementBegin = newAtStatementBegin; |
| 395 visit(node); |
| 396 } |
| 397 } |
| 398 |
| 399 visitVariableDeclarationList(VariableDeclarationList list) { |
| 400 out("var "); |
| 401 visitCommaSeparated(list.declarations, ASSIGNMENT, |
| 402 newInForInit: inForInit, newAtStatementBegin: false); |
| 403 } |
| 404 |
| 405 visitSequence(Sequence sequence) { |
| 406 // Note that we only require that the entries are expressions and not |
| 407 // assignments. This means that nested sequences are not put into |
| 408 // parenthesis. |
| 409 visitCommaSeparated(sequence.expressions, EXPRESSION, |
| 410 newInForInit: false, |
| 411 newAtStatementBegin: atStatementBegin); |
| 412 } |
| 413 |
| 414 visitAssignment(Assignment assignment) { |
| 415 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, |
| 416 newInForInit: inForInit, |
| 417 newAtStatementBegin: atStatementBegin); |
| 418 if (assignment.value !== null) { |
| 419 spaceOut(); |
| 420 String op = assignment.op; |
| 421 if (op != null) out(op); |
| 422 out("="); |
| 423 spaceOut(); |
| 424 visitNestedExpression(assignment.value, ASSIGNMENT, |
| 425 newInForInit: inForInit, |
| 426 newAtStatementBegin: false); |
| 427 } |
| 428 } |
| 429 |
| 430 visitVariableInitialization(VariableInitialization initialization) { |
| 431 visitAssignment(initialization); |
| 432 } |
| 433 |
| 434 visitConditional(Conditional cond) { |
| 435 visitNestedExpression(cond.condition, LOGICAL_OR, |
| 436 newInForInit: inForInit, |
| 437 newAtStatementBegin: atStatementBegin); |
| 438 spaceOut(); |
| 439 out("?"); |
| 440 spaceOut(); |
| 441 // The then part is allowed to have an 'in'. |
| 442 visitNestedExpression(cond.then, ASSIGNMENT, |
| 443 newInForInit: false, newAtStatementBegin: false); |
| 444 spaceOut(); |
| 445 out(":"); |
| 446 spaceOut(); |
| 447 visitNestedExpression(cond.otherwise, ASSIGNMENT, |
| 448 newInForInit: inForInit, newAtStatementBegin: false); |
| 449 } |
| 450 |
| 451 visitNew(New node) { |
| 452 out("new "); |
| 453 visitNestedExpression(node.target, CALL, |
| 454 newInForInit: inForInit, newAtStatementBegin: false); |
| 455 out("("); |
| 456 visitCommaSeparated(node.arguments, ASSIGNMENT, |
| 457 newInForInit: false, newAtStatementBegin: false); |
| 458 out(")"); |
| 459 } |
| 460 |
| 461 visitCall(Call call) { |
| 462 visitNestedExpression(call.target, LEFT_HAND_SIDE, |
| 463 newInForInit: inForInit, |
| 464 newAtStatementBegin: atStatementBegin); |
| 465 out("("); |
| 466 visitCommaSeparated(call.arguments, ASSIGNMENT, |
| 467 newInForInit: false, newAtStatementBegin: false); |
| 468 out(")"); |
| 469 } |
| 470 |
| 471 visitBinary(Binary binary) { |
| 472 Expression left = binary.left; |
| 473 Expression right = binary.right; |
| 474 String op = binary.op; |
| 475 int leftPrecedenceRequirement; |
| 476 int rightPrecedenceRequirement; |
| 477 switch (op) { |
| 478 case "||": |
| 479 leftPrecedenceRequirement = LOGICAL_OR; |
| 480 // x || (y || z) <=> (x || y) || z. |
| 481 rightPrecedenceRequirement = LOGICAL_OR; |
| 482 break; |
| 483 case "&&": |
| 484 leftPrecedenceRequirement = LOGICAL_AND; |
| 485 // x && (y && z) <=> (x && y) && z. |
| 486 rightPrecedenceRequirement = LOGICAL_AND; |
| 487 break; |
| 488 case "|": |
| 489 leftPrecedenceRequirement = BIT_OR; |
| 490 // x | (y | z) <=> (x | y) | z. |
| 491 rightPrecedenceRequirement = BIT_OR; |
| 492 break; |
| 493 case "^": |
| 494 leftPrecedenceRequirement = BIT_XOR; |
| 495 // x ^ (y ^ z) <=> (x ^ y) ^ z. |
| 496 rightPrecedenceRequirement = BIT_XOR; |
| 497 break; |
| 498 case "&": |
| 499 leftPrecedenceRequirement = BIT_AND; |
| 500 // x & (y & z) <=> (x & y) & z. |
| 501 rightPrecedenceRequirement = BIT_AND; |
| 502 break; |
| 503 case "==": |
| 504 case "!=": |
| 505 case "===": |
| 506 case "!==": |
| 507 leftPrecedenceRequirement = EQUALITY; |
| 508 rightPrecedenceRequirement = RELATIONAL; |
| 509 break; |
| 510 case "<": |
| 511 case ">": |
| 512 case "<=": |
| 513 case ">=": |
| 514 case "instanceof": |
| 515 case "in": |
| 516 leftPrecedenceRequirement = RELATIONAL; |
| 517 rightPrecedenceRequirement = SHIFT; |
| 518 break; |
| 519 case ">>": |
| 520 case "<<": |
| 521 case ">>>": |
| 522 leftPrecedenceRequirement = SHIFT; |
| 523 rightPrecedenceRequirement = ADDITIVE; |
| 524 break; |
| 525 case "+": |
| 526 case "-": |
| 527 leftPrecedenceRequirement = ADDITIVE; |
| 528 // We cannot remove parenthesis for "+" because |
| 529 // x + (y + z) <!=> (x + y) + z: |
| 530 // Example: |
| 531 // "a" + (1 + 2) => "a3"; |
| 532 // ("a" + 1) + 2 => "a12"; |
| 533 rightPrecedenceRequirement = MULTIPLICATIVE; |
| 534 break; |
| 535 case "*": |
| 536 case "/": |
| 537 case "%": |
| 538 leftPrecedenceRequirement = MULTIPLICATIVE; |
| 539 // We cannot remove parenthesis for "*" because of precision issues. |
| 540 rightPrecedenceRequirement = UNARY; |
| 541 break; |
| 542 default: |
| 543 compiler.internalError("Forgot operator: $op"); |
| 544 } |
| 545 |
| 546 visitNestedExpression(left, leftPrecedenceRequirement, |
| 547 newInForInit: inForInit, |
| 548 newAtStatementBegin: atStatementBegin); |
| 549 |
| 550 if (op == "in" || op == "instanceof") { |
| 551 // There are cases where the space is not required but without further |
| 552 // analysis we cannot know. |
| 553 out(" "); |
| 554 out(op); |
| 555 out(" "); |
| 556 } else { |
| 557 spaceOut(); |
| 558 out(op); |
| 559 spaceOut(); |
| 560 } |
| 561 visitNestedExpression(right, rightPrecedenceRequirement, |
| 562 newInForInit: inForInit, |
| 563 newAtStatementBegin: false); |
| 564 } |
| 565 |
| 566 visitPrefix(Prefix unary) { |
| 567 String op = unary.op; |
| 568 switch (op) { |
| 569 case "delete": |
| 570 case "void": |
| 571 case "typeof": |
| 572 // There are cases where the space is not required but without further |
| 573 // analysis we cannot know. |
| 574 out(op); |
| 575 out(" "); |
| 576 break; |
| 577 case "+": |
| 578 case "++": |
| 579 if (lastCharCode == charCodes.$PLUS) out(" "); |
| 580 out(op); |
| 581 break; |
| 582 case "-": |
| 583 case "--": |
| 584 if (lastCharCode == charCodes.$MINUS) out(" "); |
| 585 out(op); |
| 586 break; |
| 587 default: |
| 588 out(op); |
| 589 } |
| 590 visitNestedExpression(unary.argument, UNARY, |
| 591 newInForInit: inForInit, newAtStatementBegin: false); |
| 592 } |
| 593 |
| 594 visitPostfix(Postfix postfix) { |
| 595 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, |
| 596 newInForInit: inForInit, |
| 597 newAtStatementBegin: atStatementBegin); |
| 598 out(postfix.op); |
| 599 } |
| 600 |
| 601 visitVariableUse(VariableUse ref) { |
| 602 out(ref.name); |
| 603 } |
| 604 |
| 605 visitThis(This node) { |
| 606 out("this"); |
| 607 } |
| 608 |
| 609 visitVariableDeclaration(VariableDeclaration decl) { |
| 610 out(decl.name); |
| 611 } |
| 612 |
| 613 visitParameter(Parameter param) { |
| 614 out(param.name); |
| 615 } |
| 616 |
| 617 bool isDigit(int charCode) { |
| 618 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
| 619 } |
| 620 |
| 621 bool isValidJavaScriptId(String field) { |
| 622 if (field.length < 3) return false; |
| 623 // Ignore the leading and trailing string-delimiter. |
| 624 for (int i = 1; i < field.length - 1; i++) { |
| 625 // TODO(floitsch): allow more characters. |
| 626 int charCode = field.charCodeAt(i); |
| 627 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || |
| 628 charCodes.$A <= charCode && charCode <= charCodes.$Z || |
| 629 charCode == charCodes.$$ || |
| 630 charCode == charCodes.$_ || |
| 631 i != 1 && isDigit(charCode))) { |
| 632 return false; |
| 633 } |
| 634 } |
| 635 // TODO(floitsch): normally we should also check that the field is not |
| 636 // a reserved word. |
| 637 return true; |
| 638 } |
| 639 |
| 640 visitAccess(PropertyAccess access) { |
| 641 visitNestedExpression(access.receiver, CALL, |
| 642 newInForInit: inForInit, |
| 643 newAtStatementBegin: atStatementBegin); |
| 644 Node selector = access.selector; |
| 645 if (selector is LiteralString) { |
| 646 String fieldWithQuotes = (selector as LiteralString).value; |
| 647 if (isValidJavaScriptId(fieldWithQuotes)) { |
| 648 if (isDigit(lastCharCode)) out(" "); |
| 649 out("."); |
| 650 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); |
| 651 return; |
| 652 } |
| 653 } |
| 654 out("["); |
| 655 visitNestedExpression(selector, EXPRESSION, |
| 656 newInForInit: false, newAtStatementBegin: false); |
| 657 out("]"); |
| 658 } |
| 659 |
| 660 visitNamedFunction(NamedFunction namedFunction) { |
| 661 functionOut(namedFunction.function, namedFunction.name); |
| 662 } |
| 663 |
| 664 visitFun(Fun fun) { |
| 665 functionOut(fun, null); |
| 666 } |
| 667 |
| 668 visitLiteralBool(LiteralBool node) { |
| 669 out(node.value ? "true" : "false"); |
| 670 } |
| 671 |
| 672 visitLiteralString(LiteralString node) { |
| 673 out(node.value); |
| 674 } |
| 675 |
| 676 visitLiteralNumber(LiteralNumber node) { |
| 677 int charCode = node.value.charCodeAt(0); |
| 678 if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) { |
| 679 out(" "); |
| 680 } |
| 681 out(node.value); |
| 682 } |
| 683 |
| 684 visitLiteralNull(LiteralNull node) { |
| 685 out("null"); |
| 686 } |
| 687 |
| 688 visitArrayInitialization(ArrayInitialization node) { |
| 689 out("["); |
| 690 List<ArrayElement> elements = node.elements; |
| 691 int elementIndex = 0; |
| 692 for (int i = 0; i < node.length; i++) { |
| 693 if (elementIndex < elements.length && |
| 694 elements[elementIndex].index == i) { |
| 695 visitNestedExpression(elements[elementIndex].value, ASSIGNMENT, |
| 696 newInForInit: false, newAtStatementBegin: false); |
| 697 elementIndex++; |
| 698 // We can avoid a trailing "," if there was an element just before. So |
| 699 // `[1]` and `[1,]` are the same, but `[,]` and `[]` are not. |
| 700 if (i != node.length - 1) { |
| 701 out(","); |
| 702 spaceOut(); |
| 703 } |
| 704 } else { |
| 705 out(","); |
| 706 } |
| 707 } |
| 708 out("]"); |
| 709 } |
| 710 |
| 711 visitArrayElement(ArrayElement node) { |
| 712 throw "Unreachable"; |
| 713 } |
| 714 |
| 715 visitObjectLiteral(ObjectInitialization node) { |
| 716 out("{"); |
| 717 List<Property> properties = node.properties; |
| 718 for (int i = 0; i < properties.length; i++) { |
| 719 if (i != 0) { |
| 720 out(","); |
| 721 spaceOut(); |
| 722 } |
| 723 visitProperty(properties[i]); |
| 724 } |
| 725 out("}"); |
| 726 } |
| 727 |
| 728 visitProperty(Property node) { |
| 729 if (node.name is LiteralString) { |
| 730 String name = (node.name as LiteralString).value; |
| 731 if (isValidJavaScriptId(name)) { |
| 732 out(name.substring(1, name.length - 1)); |
| 733 } else { |
| 734 out(name); |
| 735 } |
| 736 } else { |
| 737 assert(node.name is LiteralNumber); |
| 738 out((node.name as LiteralNumber).value); |
| 739 } |
| 740 out(":"); |
| 741 spaceOut(); |
| 742 visitNestedExpression(node.value, ASSIGNMENT, |
| 743 newInForInit: false, newAtStatementBegin: false); |
| 744 } |
| 745 |
| 746 visitRegExpLiteral(RegExpLiteral node) { |
| 747 out(node.pattern); |
| 748 } |
| 749 |
| 750 visitLiteralExpression(LiteralExpression node) { |
| 751 String template = node.template; |
| 752 List<Expression> inputs = node.inputs; |
| 753 |
| 754 List<String> parts = template.split('#'); |
| 755 if (parts.length != inputs.length + 1) { |
| 756 compiler.internalError('Wrong number of arguments for JS: $template'); |
| 757 } |
| 758 out("("); |
| 759 out(parts[0]); |
| 760 for (int i = 0; i < inputs.length; i++) { |
| 761 visit(inputs[i]); |
| 762 out(parts[i + 1]); |
| 763 } |
| 764 out(")"); |
| 765 } |
| 766 |
| 767 visitLiteralStatement(LiteralStatement node) { |
| 768 outLn(node.code); |
| 769 } |
| 770 } |
| 771 |
| 772 leg.CodeBuffer prettyPrint(Node node, |
| 773 leg.Compiler compiler, |
| 774 Dynamic positionElement) { |
| 775 Printer printer = new Printer(compiler, positionElement); |
| 776 printer.visit(node); |
| 777 return printer.outBuffer; |
| 778 } |
| OLD | NEW |