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