Chromium Code Reviews| Index: lib/compiler/implementation/js/printer.dart |
| diff --git a/lib/compiler/implementation/js/printer.dart b/lib/compiler/implementation/js/printer.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b1d8b472ef23accc82dc6a9e87a453098c09227f |
| --- /dev/null |
| +++ b/lib/compiler/implementation/js/printer.dart |
| @@ -0,0 +1,792 @@ |
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +class Printer implements NodeVisitor { |
| + final bool shouldCompressOutput = false; |
| + Compiler compiler; |
| + var positionElement; |
| + CodeBuffer outBuffer; |
| + int indentLevel = 0; |
| + bool inForInit = false; |
| + bool atStatementBegin = false; |
| + |
| + Printer(this.compiler, this.positionElement) : outBuffer = new CodeBuffer(); |
| + |
| + void spaceOut() { |
| + if (!shouldCompressOutput) out(" "); |
| + } |
| + void lineOut() { |
| + if (!shouldCompressOutput) out("\n"); |
| + } |
| + int lastCharCode = 0; |
| + void out(String str) { |
| + 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.
|
| + if (str != "") { |
| + lastCharCode = str.charCodeAt(str.length - 1); |
| + } |
| + } |
| + void outLn(String str) { |
| + out(str); |
| + lineOut(); |
| + } |
| + void outIndent(String str) { indent(); out(str); } |
| + void outIndentLn(String str) { indent(); outLn(str); } |
| + void indent() { |
| + if (!shouldCompressOutput) { |
| + for (int i = 0; i < indentLevel; i++) out(" "); |
| + } |
| + } |
| + |
| + 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.
|
| + if (position != null) { |
| + outBuffer.setSourceLocation(positionElement, position); |
| + } |
| + } |
| + |
| + visit(Node node) { |
| + recordSourcePosition(node.sourcePosition); |
| + node.accept(this); |
| + recordSourcePosition(node.endSourcePosition); |
| + } |
| + |
| + visitCommaSeparated(List<Node> nodes, int hasRequiredType, |
| + [bool newInForInit, bool newAtStatementBegin]) { |
| + for (int i = 0; i < nodes.length; i++) { |
| + if (i != 0) { |
| + atStatementBegin = false; |
| + out(","); |
| + spaceOut(); |
| + } |
| + visitNestedExpression(nodes[i], hasRequiredType, |
| + newInForInit, newAtStatementBegin); |
| + } |
| + } |
| + |
| + visitAll(List<Node> nodes) { |
| + nodes.forEach(visit); |
| + } |
| + |
| + visitProgram(Program program) { |
| + visitAll(program.body); |
| + } |
| + |
| + bool blockBody(Node body, [bool needsSeparation, bool needsNewline]) { |
| + if (body is Block) { |
| + spaceOut(); |
| + blockOut(body, false, needsNewline); |
| + return true; |
| + } |
| + if (shouldCompressOutput && needsSeparation) { |
| + // If [shouldCompressOutput] is false, then the 'lineOut' will insert |
| + // the separation. |
| + out(" "); |
| + } else { |
| + lineOut(); |
| + } |
| + indentLevel++; |
| + visit(body); |
| + indentLevel--; |
| + return false; |
| + } |
| + |
| + void blockOutWithoutBraces(Node node) { |
| + if (node is Block) { |
| + node.elements.forEach(blockOutWithoutBraces); |
| + } else { |
| + visit(node); |
| + } |
| + } |
| + |
| + void blockOut(Block node, bool shouldIndent, bool needsNewline) { |
| + if (shouldIndent) indent(); |
| + out("{"); |
| + lineOut(); |
| + indentLevel++; |
| + node.elements.forEach(blockOutWithoutBraces); |
| + indentLevel--; |
| + indent(); |
| + out("}"); |
| + if (needsNewline) lineOut(); |
| + } |
| + |
| + visitBlock(Block block) { |
| + blockOut(block, true, true); |
| + } |
| + |
| + visitExpressionStatement(ExpressionStatement expressionStatement) { |
| + indent(); |
| + visitNestedExpression(expressionStatement.expr, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: true); |
| + outLn(";"); |
| + } |
| + |
| + visitNOP(NOP nop) { |
| + outIndentLn(";"); |
| + } |
| + |
| + void ifOut(If node, bool shouldIndent) { |
| + Node then = node.then; |
| + Node elsePart = node.otherwise; |
| + bool hasElse = node.hasElse; |
| + // Handle dangling elses. |
| + // If the then-branch is an if, which has no else-branch, but we do |
| + // have one, then we need to put the nested if into braces. |
| + if (hasElse && then is If) { |
| + Node nested = then; |
| + do { |
| + If nestedIf = nested; |
| + if (!nestedIf.hasElse) { |
| + then = new Block(<Statement>[then]); |
| + break; |
| + } |
| + nested = nestedIf.otherwise; |
| + } while (nested is If); |
| + } |
| + if (then is If && !(then as If).hasElse && hasElse) { |
| + then = new Block(<Statement>[then]); |
| + } |
| + if (shouldIndent) indent(); |
| + out("if"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(node.test, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + bool thenWasBlock = |
| + blockBody(then, needsSeparation: false, needsNewline: !hasElse); |
| + if (hasElse) { |
| + if (thenWasBlock) { |
| + spaceOut(); |
| + } else { |
| + indent(); |
| + } |
| + out("else"); |
| + if (elsePart is If) { |
| + out(" "); |
| + ifOut(elsePart, false); |
| + } else { |
| + blockBody(elsePart, needsSeparation: true, needsNewline: true); |
| + } |
| + } |
| + } |
| + |
| + visitIf(If node) { |
| + ifOut(node, true); |
| + } |
| + |
| + visitFor(For loop) { |
| + outIndent("for"); |
| + spaceOut(); |
| + out("("); |
| + if (loop.init !== null) { |
| + visitNestedExpression(loop.init, EXPRESSION, |
| + newInForInit: true, newAtStatementBegin: false); |
| + } |
| + out(";"); |
| + if (loop.test !== null) { |
| + spaceOut(); |
| + visitNestedExpression(loop.test, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + } |
| + out(";"); |
| + if (loop.incr !== null) { |
| + spaceOut(); |
| + visitNestedExpression(loop.incr, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + } |
| + out(")"); |
| + blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + visitForIn(ForIn loop) { |
| + outIndent("for"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(loop.lhs, EXPRESSION, |
| + newInForInit: true, newAtStatementBegin: false); |
| + out(" in "); |
| + visitNestedExpression(loop.obj, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + visitWhile(While loop) { |
| + outIndent("while"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(loop.test, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + blockBody(loop.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + visitDo(Do loop) { |
| + outIndent("do"); |
| + if (blockBody(loop.body, needsSeparation: true, needsNewline: false)) { |
| + spaceOut(); |
| + } else { |
| + indent(); |
| + } |
| + out("while"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(loop.test, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + outLn(");"); |
| + } |
| + |
| + visitContinue(Continue node) { |
| + if (node.id == null) { |
| + outIndentLn("continue;"); |
| + } else { |
| + outIndentLn("continue ${node.id};"); |
| + } |
| + } |
| + |
| + visitBreak(Break node) { |
| + if (node.id == null) { |
| + outIndentLn("break;"); |
| + } else { |
| + outIndentLn("break ${node.id};"); |
| + } |
| + } |
| + |
| + visitReturn(Return node) { |
| + if (node.expr == null) { |
| + outIndentLn("return;"); |
| + } else { |
| + outIndent("return "); |
| + visitNestedExpression(node.expr, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + outLn(";"); |
| + } |
| + } |
| + |
| + visitThrow(Throw node) { |
| + outIndent("throw "); |
| + visitNestedExpression(node.expr, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + outLn(";"); |
| + } |
| + |
| + visitTry(Try node) { |
| + outIndent("try"); |
| + blockBody(node.body, needsSeparation: true, needsNewline: false); |
| + spaceOut(); |
| + if (node.catchPart !== null) { |
| + visit(node.catchPart); |
| + } |
| + if (node.finallyPart !== null) { |
| + spaceOut(); |
| + out("finally"); |
| + blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); |
| + } else { |
| + lineOut(); |
| + } |
| + } |
| + |
| + visitCatch(Catch node) { |
| + spaceOut(); |
| + out("catch"); |
| + spaceOut(); |
| + out("("); |
| + // Must be a reference, so just test for a primary. |
| + 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.
|
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + blockBody(node.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + visitWith(With node) { |
| + outIndent("with"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(node.object, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + blockBody(node.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + visitSwitch(Switch node) { |
| + outIndent("switch"); |
| + spaceOut(); |
| + out("("); |
| + visitNestedExpression(node.key, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + spaceOut(); |
| + outLn("{"); |
| + indentLevel++; |
| + visitAll(node.cases); |
| + indentLevel--; |
| + outIndentLn("}"); |
| + } |
| + |
| + visitCase(Case node) { |
| + outIndent("case "); |
| + 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.
|
| + newInForInit: false, newAtStatementBegin: false); |
| + outLn(":"); |
| + if (!node.body.elements.isEmpty()) { |
| + indentLevel++; |
| + blockOutWithoutBraces(node.body); |
| + indentLevel--; |
| + } |
| + } |
| + |
| + visitDefault(Default node) { |
| + outIndentLn("default:"); |
| + if (!node.body.elements.isEmpty()) { |
| + indentLevel++; |
| + blockOutWithoutBraces(node.body); |
| + indentLevel--; |
| + } |
| + } |
| + |
| + visitLabeled(Labeled node) { |
| + outIndent("${node.id}:"); |
| + blockBody(node.body, needsSeparation: false, needsNewline: true); |
| + } |
| + |
| + void functionOut(Fun fun, Node name) { |
| + out("function"); |
| + if (name != null) { |
| + out(" "); |
| + // Name must be a [Decl]. Therefore only test for primary expressions. |
| + visitNestedExpression(name, PRIMARY, |
| + newInForInit: false, newAtStatementBegin: false); |
| + } |
| + out("("); |
| + if (fun.params != null) { |
| + visitCommaSeparated(fun.params, PRIMARY, |
| + newInForInit: false, newAtStatementBegin: false); |
| + } |
| + out(")"); |
| + blockBody(fun.body, needsSeparation: false, needsNewline: false); |
| + } |
| + |
| + visitFunctionDeclaration(FunctionDeclaration declaration) { |
| + indent(); |
| + functionOut(declaration.fun, declaration.id); |
| + lineOut(); |
| + } |
| + |
| + visitNestedExpression(Expression node, int requiredPrecedence, |
| + [bool newInForInit, bool newAtStatementBegin]) { |
| + bool needsParentheses = |
| + // a - (b + c). |
| + (requiredPrecedence != EXPRESSION && |
| + node.precedenceLevel < requiredPrecedence) || |
| + // for (a = (x in o); ... ; ... ) { ... } |
| + (newInForInit && node is Binary && (node as Binary).op == "in") || |
| + // (function() { ... })(). |
| + // ({a: 2, b: 3}.toString()). |
| + (newAtStatementBegin && (node is NamedFunction || |
| + node is Fun || |
| + node is ObjectLiteral)); |
| + if (needsParentheses) { |
| + inForInit = false; |
| + atStatementBegin = false; |
| + out("("); |
| + visit(node); |
| + out(")"); |
| + } else { |
| + inForInit = newInForInit; |
| + atStatementBegin = newAtStatementBegin; |
| + visit(node); |
| + } |
| + } |
| + |
| + visitVariableDeclarationList(VariableDeclarationList list) { |
| + out("var "); |
| + visitCommaSeparated(list.declarations, ASSIGNMENT, |
| + newInForInit: inForInit, newAtStatementBegin: false); |
| + } |
| + |
| + visitSequence(Sequence sequence) { |
| + // Note that we only require that the entries are expressions and not |
| + // assignments. This means that nested sequences are not put into |
| + // parenthesis. |
| + visitCommaSeparated(sequence.expressions, EXPRESSION, |
| + newInForInit: false, |
| + newAtStatementBegin: atStatementBegin); |
| + } |
| + |
| + outAssignment(Node lhs, Node value, [String op = ""]) { |
| + visitNestedExpression(lhs, LHS, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + if (value !== null) { |
| + spaceOut(); |
| + out(op); |
| + out("="); |
| + spaceOut(); |
| + visitNestedExpression(value, ASSIGNMENT, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: false); |
| + } |
| + } |
| + |
| + visitVassign(Vassign vassign) { |
| + outAssignment(vassign.lhs, vassign.value); |
| + } |
| + visitInit(Init init) { |
| + outAssignment(init.decl, init.value); |
| + } |
| + visitAccsign(Accsign accsign) { |
| + outAssignment(accsign.lhs, accsign.value); |
| + } |
| + |
| + visitVassignOp(VassignOp vassignOp) { |
| + outAssignment(vassignOp.lhs, vassignOp.value, vassignOp.op); |
| + } |
| + |
| + visitAccsignOp(AccsignOp accsignOp) { |
| + outAssignment(accsignOp.lhs, accsignOp.value, accsignOp.op); |
| + } |
| + |
| + visitConditional(Conditional cond) { |
| + visitNestedExpression(cond.test, LOGICAL_OR, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + spaceOut(); |
| + out("?"); |
| + spaceOut(); |
| + // The then part is allowed to have an 'in'. |
| + visitNestedExpression(cond.then, ASSIGNMENT, |
| + newInForInit: false, newAtStatementBegin: false); |
| + spaceOut(); |
| + out(":"); |
| + spaceOut(); |
| + visitNestedExpression(cond.otherwise, ASSIGNMENT, |
| + newInForInit: inForInit, newAtStatementBegin: false); |
| + } |
| + |
| + visitNew(New node) { |
| + out("new "); |
| + visitNestedExpression(node.cls, CALL, |
| + newInForInit: inForInit, newAtStatementBegin: false); |
| + out("("); |
| + visitCommaSeparated(node.arguments, ASSIGNMENT, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + } |
| + |
| + visitCall(Call call) { |
| + visitNestedExpression(call.target, LHS, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + out("("); |
| + visitCommaSeparated(call.arguments, ASSIGNMENT, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out(")"); |
| + } |
| + |
| + visitBinary(Binary binary) { |
| + Expression left = binary.left; |
| + Expression right = binary.right; |
| + String op = binary.op; |
| + int leftPrecedenceRequirement; |
| + int rightPrecedenceRequirement; |
| + switch (op) { |
| + case "||": |
| + leftPrecedenceRequirement = LOGICAL_OR; |
| + // x || (y || z) <=> (x || y) || z. |
| + rightPrecedenceRequirement = LOGICAL_OR; |
| + break; |
| + case "&&": |
| + leftPrecedenceRequirement = LOGICAL_AND; |
| + // x && (y && z) <=> (x && y) && z. |
| + rightPrecedenceRequirement = LOGICAL_AND; |
| + break; |
| + case "|": |
| + leftPrecedenceRequirement = BIT_OR; |
| + // x | (y | z) <=> (x | y) | z. |
| + rightPrecedenceRequirement = BIT_OR; |
| + break; |
| + case "^": |
| + leftPrecedenceRequirement = BIT_XOR; |
| + // x ^ (y ^ z) <=> (x ^ y) ^ z. |
| + rightPrecedenceRequirement = BIT_XOR; |
| + break; |
| + case "&": |
| + leftPrecedenceRequirement = BIT_AND; |
| + // x & (y & z) <=> (x & y) & z. |
| + rightPrecedenceRequirement = BIT_AND; |
| + break; |
| + case "==": |
| + case "!=": |
| + case "===": |
| + case "!==": |
| + leftPrecedenceRequirement = EQUALITY; |
| + rightPrecedenceRequirement = RELATIONAL; |
| + break; |
| + case "<": |
| + case ">": |
| + case "<=": |
| + case ">=": |
| + case "instanceof": |
| + case "in": |
| + leftPrecedenceRequirement = RELATIONAL; |
| + rightPrecedenceRequirement = SHIFT; |
| + break; |
| + case ">>": |
| + case "<<": |
| + case ">>>": |
| + leftPrecedenceRequirement = SHIFT; |
| + rightPrecedenceRequirement = ADDITIVE; |
| + break; |
| + case "+": |
| + case "-": |
| + leftPrecedenceRequirement = ADDITIVE; |
| + // We cannot remove parenthesis for "+" because |
| + // x + (y + z) <!=> (x + y) + z: |
| + // Example: |
| + // "a" + (1 + 2) => "a3"; |
| + // ("a" + 1) + 2 => "a12"; |
| + rightPrecedenceRequirement = MULTIPLICATIVE; |
| + break; |
| + case "*": |
| + case "/": |
| + case "%": |
| + leftPrecedenceRequirement = MULTIPLICATIVE; |
| + // We cannot remove parenthesis for "*" because of precision issues. |
| + rightPrecedenceRequirement = UNARY; |
| + break; |
| + default: |
| + compiler.internalError("Forgot operator: $op"); |
| + } |
| + |
| + visitNestedExpression(left, leftPrecedenceRequirement, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + |
| + if (op == "in" || op == "instanceof") { |
| + // There are cases where the space is not required but without further |
| + // analysis we cannot know. |
| + out(" "); |
| + out(op); |
| + out(" "); |
| + } else { |
| + spaceOut(); |
| + out(op); |
| + spaceOut(); |
| + } |
| + visitNestedExpression(right, rightPrecedenceRequirement, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: false); |
| + } |
| + |
| + visitUnary(Unary unary) { |
| + String op = unary.op; |
| + switch (op) { |
| + case "delete": |
| + case "void": |
| + case "typeof": |
| + // There are cases where the space is not required but without further |
| + // analysis we cannot know. |
| + out(op); |
| + out(" "); |
| + break; |
| + case "+": |
| + case "++": |
| + 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
|
| + out(op); |
| + break; |
| + case "-": |
| + case "--": |
| + if (lastCharCode == "-".charCodeAt(0)) out(" "); |
| + out(op); |
| + break; |
| + default: |
| + out(op); |
| + } |
| + visitNestedExpression(unary.arg, UNARY, |
| + newInForInit: inForInit, newAtStatementBegin: false); |
| + } |
| + |
| + visitPostfix(Postfix postfix) { |
| + visitNestedExpression(postfix.arg, LHS, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + out(postfix.op); |
| + } |
| + |
| + visitRef(Ref ref) { |
| + out(ref.id); |
| + } |
| + |
| + visitThis(This node) { |
| + out("this"); |
| + } |
| + |
| + visitDecl(Decl decl) { |
| + out(decl.id); |
| + } |
| + |
| + visitParam(Param param) { |
| + out(param.id); |
| + } |
| + |
| + bool isDigit(int charCode) { |
| + return '0'.charCodeAt(0) <= charCode && charCode <= '9'.charCodeAt(0); |
| + } |
| + |
| + bool isValidJavaScriptId(String field) { |
| + if (field.length < 3) return false; |
| + // Ignore the leading and trailing string-delimiter. |
| + for (int i = 1; i < field.length - 1; i++) { |
| + // TODO(floitsch): allow more characters. |
| + int charCode = field.charCodeAt(i); |
| + if (!('a'.charCodeAt(0) <= charCode && charCode <= 'z'.charCodeAt(0) || |
| + 'A'.charCodeAt(0) <= charCode && charCode <= 'Z'.charCodeAt(0) || |
| + charCode == @'$'.charCodeAt(0) || |
| + charCode == '_'.charCodeAt(0) || |
| + i != 1 && isDigit(charCode))) { |
| + return false; |
| + } |
| + } |
| + // TODO(floitsch): normally we should also check that the field is not |
| + // a reserved word. |
| + return true; |
| + } |
| + |
| + visitAccess(Access access) { |
| + visitNestedExpression(access.receiver, CALL, |
| + newInForInit: inForInit, |
| + newAtStatementBegin: atStatementBegin); |
| + Node selector = access.selector; |
| + if (selector is StringLiteral) { |
| + String fieldWithQuotes = (selector as StringLiteral).value; |
| + if (isValidJavaScriptId(fieldWithQuotes)) { |
| + 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.
|
| + out("."); |
| + out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); |
| + return; |
| + } |
| + } |
| + out("["); |
| + visitNestedExpression(selector, EXPRESSION, |
| + newInForInit: false, newAtStatementBegin: false); |
| + out("]"); |
| + } |
| + |
| + visitNamedFunction(NamedFunction namedFunction) { |
| + functionOut(namedFunction.fun, namedFunction.id); |
| + } |
| + |
| + visitFun(Fun fun) { |
| + functionOut(fun, null); |
| + } |
| + |
| + visitBoolLiteral(BoolLiteral node) { |
| + out(node.value ? "true" : "false"); |
| + } |
| + |
| + visitStringLiteral(StringLiteral node) { |
| + out(node.value); |
| + } |
| + |
| + visitNumberLiteral(NumberLiteral node) { |
| + int charCode = node.value.charCodeAt(0); |
| + if (charCode == '-'.charCodeAt(0) && lastCharCode == "-".charCodeAt(0)) { |
| + out(" "); |
| + } |
| + out(node.value); |
| + } |
| + |
| + visitNullLiteral(NullLiteral node) { |
| + out("null"); |
| + } |
| + |
| + visitArrayLiteral(ArrayLiteral node) { |
| + out("["); |
| + List<ArrayElement> elements = node.elements; |
| + int elementIndex = 0; |
| + for (int i = 0; i < node.length; i++) { |
| + if (elementIndex < elements.length && |
| + elements[elementIndex].index == i) { |
| + visitNestedExpression(elements[elementIndex].value, ASSIGNMENT, |
| + newInForInit: false, newAtStatementBegin: false); |
| + elementIndex++; |
| + // We can avoid a trailing "," if there was an element just before. So |
| + // `[1]` and `[1,]` are the same, but `[,]` and `[]` are not. |
| + if (i != node.length - 1) { |
| + out(","); |
| + spaceOut(); |
| + } |
| + } else { |
| + out(","); |
| + } |
| + } |
| + out("]"); |
| + } |
| + |
| + visitArrayElement(ArrayElement node) { |
| + throw "Unreachable"; |
| + } |
| + |
| + visitObjectLiteral(ObjectLiteral node) { |
| + out("{"); |
| + List<PropertyInit> properties = node.properties; |
| + for (int i = 0; i < properties.length; i++) { |
| + if (i != 0) { |
| + out(","); |
| + spaceOut(); |
| + } |
| + visitPropertyInit(properties[i]); |
| + } |
| + out("}"); |
| + } |
| + |
| + visitPropertyInit(PropertyInit node) { |
| + if (node.name is StringLiteral) { |
| + String name = (node.name as StringLiteral).value; |
| + if (isValidJavaScriptId(name)) { |
| + out(name.substring(1, name.length - 1)); |
| + } else { |
| + out(name); |
| + } |
| + } else { |
| + assert(node.name is NumberLiteral); |
| + out((node.name as NumberLiteral).value); |
| + } |
| + out(":"); |
| + spaceOut(); |
| + visitNestedExpression(node.value, ASSIGNMENT, |
| + newInForInit: false, newAtStatementBegin: false); |
| + } |
| + |
| + visitRegExpLiteral(RegExpLiteral node) { |
| + out(node.pattern); |
| + } |
| + |
| + visitExpressionBlob(ExpressionBlob node) { |
| + String blob = node.blob; |
| + List<Expression> data = node.data; |
| + |
| + 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.
|
| + if (parts.length != data.length + 1) { |
| + compiler.internalError('Wrong number of arguments for JS: $blob'); |
| + } |
| + out("("); |
| + out(parts[0]); |
| + for (int i = 0; i < data.length; i++) { |
| + visit(data[i]); |
| + out(parts[i + 1]); |
| + } |
| + out(")"); |
| + } |
| + |
| + visitStatementBlob(StatementBlob node) { |
| + outLn(node.blob); |
| + } |
| +} |
| + |
| +CodeBuffer prettyPrint(Node node, Compiler compiler, Dynamic positionElement) { |
| + Printer printer = new Printer(compiler, positionElement); |
| + printer.visit(node); |
| + return printer.outBuffer; |
| +} |