Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(850)

Side by Side Diff: lib/compiler/implementation/js/printer.dart

Issue 10825180: Add JavaScript AST. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: . Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 ObjectInitializer));
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 visitArrayInitializer(ArrayInitializer 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 visitObjectInitializer(ObjectInitializer 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698