001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
004: * Copyright 2001-2008 Laszlo Systems, Inc. All Rights Reserved. *
005: * Use is subject to license terms. *
006: * J_LZ_COPYRIGHT_END *********************************************************/
007:
008: /***
009: * ParseTreePrinter.java
010: * Author: Oliver Steele, P T Withington
011: * Description: unparses the AST into Javascript.
012: */package org.openlaszlo.sc;
013:
014: import java.io.*;
015: import java.util.*;
016: import java.util.regex.Pattern;
017: import java.text.SimpleDateFormat;
018: import java.text.DecimalFormat;
019:
020: import org.openlaszlo.server.LPS;
021: import org.openlaszlo.sc.parser.*;
022: import org.openlaszlo.sc.Translator;
023: import org.openlaszlo.sc.Compiler.Ops;
024: import org.openlaszlo.sc.Compiler.PassThroughNode;
025:
026: // Values
027: import org.openlaszlo.sc.Values;
028:
029: // Instructions
030: import org.openlaszlo.sc.Instructions;
031: import org.openlaszlo.sc.Instructions.Instruction;
032: import org.openlaszlo.sc.InstructionPrinter;
033:
034: // This class supports the Javascript translator
035: public class ParseTreePrinter {
036:
037: boolean compress;
038: String SPACE;
039: String NEWLINE;
040: String COMMA;
041: String COLON;
042: String ASSIGN;
043: String CONDITIONAL;
044: String ALTERNATIVE;
045: String OPENPAREN;
046: String CLOSEPAREN;
047: String SEMI;
048: String OPTIONAL_SEMI;
049: String OPENCURLY;
050: String CLOSECURLY;
051:
052: public ParseTreePrinter() {
053: this (false, false);
054: }
055:
056: public ParseTreePrinter(boolean compress) {
057: this (compress, false);
058: }
059:
060: public ParseTreePrinter(boolean compress, boolean obfuscate) {
061: this .compress = compress;
062: // Set whitespace
063: this .SPACE = compress ? "" : " ";
064: this .NEWLINE = obfuscate ? "" : "\n";
065: // Set punctuation
066: this .COMMA = "," + SPACE;
067: this .COLON = ":" + SPACE;
068: this .ASSIGN = SPACE + "=" + SPACE;
069: this .CONDITIONAL = SPACE + "?" + SPACE;
070: this .ALTERNATIVE = SPACE + ":" + SPACE;
071: this .OPENPAREN = SPACE + "(";
072: this .CLOSEPAREN = ")" + SPACE;
073: this .SEMI = ";";
074: this .OPTIONAL_SEMI = (compress && "\n".equals(NEWLINE)) ? NEWLINE
075: : SEMI;
076: }
077:
078: public void print(SimpleNode node) {
079: print(node, System.out);
080: }
081:
082: public void print(SimpleNode node, OutputStream output) {
083: PrintStream where = new PrintStream(output);
084: print(node, where);
085: }
086:
087: public void print(SimpleNode node, PrintStream where) {
088: where.println(visit(node));
089: }
090:
091: public String delimit(String phrase, boolean force) {
092: if (phrase.length() > 0) {
093: return ((('(' != phrase.charAt(0)) && force) ? " " : SPACE)
094: + phrase;
095: }
096: return phrase;
097: }
098:
099: public String delimit(String phrase) {
100: return delimit(phrase, true);
101: }
102:
103: public String elideSemi(String phrase) {
104: if (phrase.endsWith(SEMI)) {
105: return phrase.substring(0, phrase.length() - SEMI.length());
106: }
107: return phrase;
108: }
109:
110: public String makeBlock(String body) {
111: body = elideSemi(body);
112: // NEWLINE is for debug/readability, so our code is not _all_ on
113: // one line
114: return "{" + NEWLINE + elideSemi(body)
115: + (body.endsWith("}") ? "" : NEWLINE) + "}";
116: }
117:
118: public static String join(String token, String[] strings) {
119: StringBuffer sb = new StringBuffer();
120: int l = strings.length - 1;
121: for (int x = 0; x < l; x++) {
122: sb.append(strings[x]);
123: sb.append(token);
124: }
125: if (l >= 0) {
126: sb.append(strings[l]);
127: }
128: return (sb.toString());
129: }
130:
131: public String visit(SimpleNode node) {
132: if (node instanceof PassThroughNode) {
133: node = ((PassThroughNode) node).realNode;
134: }
135:
136: int size = node.size();
137: SimpleNode[] childnodes = node.getChildren();
138: String[] children = new String[size];
139: for (int i = 0; i < size; i++) {
140: SimpleNode n = childnodes[i];
141: if (n instanceof PassThroughNode) {
142: n = childnodes[i] = ((PassThroughNode) n).realNode;
143: }
144: children[i] = visit(n);
145: }
146:
147: Class nt = node.getClass();
148:
149: // Are we doing OOP yet?
150: if (node instanceof ASTProgram
151: || node instanceof ASTStatementList
152: || node instanceof ASTDirectiveBlock) {
153: // Conditional join
154: StringBuffer sb = new StringBuffer();
155: String sep = "";
156: int l = children.length;
157: for (int x = 0; x < l; x++) {
158: String child = children[x];
159: // Elide empty nodes
160: if (!"".equals(child)) {
161: sb.append(sep);
162: sb.append(child);
163: if (!child.endsWith(SEMI)) {
164: sep = SEMI + (compress ? SPACE : NEWLINE);
165: } else {
166: sep = (compress ? SPACE : NEWLINE);
167: }
168: }
169: }
170: return sb.toString();
171: }
172: if (node instanceof ASTStatement) {
173: assert children.length == 1;
174: String child = children[0];
175: // Ensure an expression becomes a statement by appending an
176: // explicit semicolon
177: if ((!"".equals(child)) && (!child.endsWith(SEMI))) {
178: return child + SEMI;
179: } else {
180: return child;
181: }
182: }
183: if (node instanceof ASTAssignmentExpression) {
184: return visitAssignmentExpression(node, children);
185: }
186: if (node instanceof ASTCallExpression) {
187: return visitCallExpression(node, children);
188: }
189: if (node instanceof ASTSuperCallExpression) {
190: return visitSuperCallExpression(node, children);
191: }
192: if (node instanceof ASTConditionalExpression) {
193: return visitConditionalExpression(node, children);
194: }
195: if (node instanceof ASTEmptyExpression) {
196: return visitEmptyExpression(node, children);
197: }
198: if (node instanceof ASTForVarInStatement) {
199: return visitForVarInStatement(node, children);
200: }
201: if (node instanceof ASTForInStatement) {
202: return visitForInStatement(node, children);
203: }
204: if (node instanceof ASTForVarStatement
205: || node instanceof ASTForStatement) {
206: return visitForVarStatement(node, children);
207: }
208: if (node instanceof ASTNewExpression) {
209: return visitNewExpression(node, children);
210: }
211: if (node instanceof ASTIfStatement
212: || node instanceof ASTIfDirective) {
213: return visitIfStatement(node, children);
214: }
215: if (node instanceof ASTPragmaDirective) {
216: return visitPragmaDirective(node, children);
217: }
218: if (node instanceof ASTPostfixExpression) {
219: return visitPostfixExpression(node, children);
220: }
221: if (node instanceof ASTPropertyIdentifierReference) {
222: return visitPropertyIdentifierReference(node, children);
223: }
224: if (node instanceof ASTPropertyValueReference) {
225: return visitPropertyValueReference(node, children);
226: }
227: if (node instanceof ASTReturnStatement) {
228: return visitReturnStatement(node, children);
229: }
230: if (node instanceof ASTThisReference) {
231: return visitThisReference(node, children);
232: }
233: if (node instanceof ASTContinueStatement) {
234: return visitContinueStatement(node, children);
235: }
236: if (node instanceof ASTBreakStatement) {
237: return visitBreakStatement(node, children);
238: }
239: if (node instanceof ASTUnaryExpression) {
240: return visitUnaryExpression(node, children);
241: }
242: if (node instanceof ASTWithStatement) {
243: return visitWithStatement(node, children);
244: }
245: if (node instanceof ASTDoWhileStatement) {
246: return visitDoWhileStatement(node, children);
247: }
248: if (node instanceof ASTWhileStatement) {
249: return visitWhileStatement(node, children);
250: }
251: if (node instanceof ASTSwitchStatement) {
252: return visitSwitchStatement(node, children);
253: }
254: if (node instanceof ASTCaseClause) {
255: return visitCaseClause(node, children);
256: }
257: if (node instanceof ASTDefaultClause) {
258: return visitDefaultClause(node, children);
259: }
260: if (node instanceof ASTArrayLiteral) {
261: return visitArrayLiteral(node, children);
262: }
263: if (node instanceof ASTBinaryExpressionSequence) {
264: return visitBinaryExpressionSequence(node, children);
265: }
266: if (node instanceof ASTExpressionList
267: || node instanceof ASTFunctionCallParameters
268: || node instanceof ASTFormalParameterList) {
269: return visitExpressionList(node, children);
270: }
271: if (node instanceof ASTAndExpressionSequence) {
272: return visitAndOrExpressionSequence(true, node, children);
273: }
274: if (node instanceof ASTOrExpressionSequence) {
275: return visitAndOrExpressionSequence(false, node, children);
276: }
277: if (node instanceof ASTFunctionDeclaration) {
278: return visitFunctionDeclaration(node, children);
279: }
280: if (node instanceof ASTFunctionExpression) {
281: return visitFunctionExpression(node, children);
282: }
283: if (node instanceof ASTIdentifier) {
284: return visitIdentifier(node, children);
285: }
286: if (node instanceof ASTLiteral) {
287: return visitLiteral(node, children);
288: }
289: if (node instanceof ASTObjectLiteral) {
290: return visitObjectLiteral(node, children);
291: }
292: if (node instanceof ASTOperator) {
293: return visitOperator(node, children);
294: }
295: if (node instanceof ASTVariableStatement) {
296: return visitVariableStatement(node, children);
297: }
298: if (node instanceof ASTVariableDeclaration) {
299: return visitVariableDeclaration(node, children);
300: }
301: if (node instanceof ASTVariableDeclarationList) {
302: return visitVariableDeclarationList(node, children);
303: }
304: if (node instanceof ASTTryStatement) {
305: return visitTryStatement(node, children);
306: }
307: if (node instanceof ASTCatchClause) {
308: return visitCatchClause(node, children);
309: }
310: if (node instanceof ASTFinallyClause) {
311: return visitFinallyClause(node, children);
312: }
313: if (node instanceof ASTThrowStatement) {
314: return visitThrowStatement(node, children);
315: }
316: return defaultVisitor(node, children);
317: }
318:
319: public String defaultVisitor(SimpleNode node, String[] children) {
320: return "//\u00AB" + node.toString() + "("
321: + join(COMMA, children) + ")\u00BB";
322: }
323:
324: // Copied (and massaged) from Parser.jjt
325: public static String[] OperatorNames = {};
326: static {
327: ArrayList on = new ArrayList();
328: // TODO: [2005-11-17 ptw] Not quite right, but javacc doesn't
329: // tell us the range of its Ops
330: for (int i = 0; i < 256; i++) {
331: on.add("<" + Integer.toString(i) + ">");
332: }
333: on.set(Ops.LPAREN, "(");
334: on.set(Ops.LBRACKET, "[");
335: on.set(Ops.DOT, ".");
336: on.set(Ops.ASSIGN, "=");
337: on.set(Ops.COMMA, ",");
338: on.set(Ops.GT, ">");
339: on.set(Ops.LT, "<");
340: on.set(Ops.BANG, "!");
341: on.set(Ops.TILDE, "~");
342: on.set(Ops.HOOK, "?");
343: on.set(Ops.COLON, ":");
344: on.set(Ops.EQ, "==");
345: on.set(Ops.LE, "<=");
346: on.set(Ops.GE, ">=");
347: on.set(Ops.NE, "!=");
348: on.set(Ops.SEQ, "===");
349: on.set(Ops.SNE, "!==");
350: on.set(Ops.SC_OR, "||");
351: on.set(Ops.SC_AND, "&&");
352: on.set(Ops.INCR, "++");
353: on.set(Ops.DECR, "--");
354: on.set(Ops.PLUS, "+");
355: on.set(Ops.MINUS, "-");
356: on.set(Ops.STAR, "*");
357: on.set(Ops.SLASH, "/");
358: on.set(Ops.BIT_AND, "&");
359: on.set(Ops.BIT_OR, "|");
360: on.set(Ops.XOR, "^");
361: on.set(Ops.REM, "%");
362: on.set(Ops.LSHIFT, "<<");
363: on.set(Ops.RSIGNEDSHIFT, ">>");
364: on.set(Ops.RUNSIGNEDSHIFT, ">>>");
365: on.set(Ops.PLUSASSIGN, "+=");
366: on.set(Ops.MINUSASSIGN, "-=");
367: on.set(Ops.STARASSIGN, "*=");
368: on.set(Ops.SLASHASSIGN, "/=");
369: on.set(Ops.ANDASSIGN, "&=");
370: on.set(Ops.ORASSIGN, "|=");
371: on.set(Ops.XORASSIGN, "^=");
372: on.set(Ops.REMASSIGN, "%=");
373: on.set(Ops.LSHIFTASSIGN, "<<=");
374: on.set(Ops.RSIGNEDSHIFTASSIGN, ">>=");
375: on.set(Ops.RUNSIGNEDSHIFTASSIGN, ">>>=");
376:
377: on.set(Ops.IN, "in");
378: on.set(Ops.INSTANCEOF, "instanceof");
379: on.set(Ops.TYPEOF, "typeof");
380: on.set(Ops.DELETE, "delete");
381: on.set(Ops.VOID, "void");
382: on.set(Ops.NEW, "new");
383:
384: OperatorNames = (String[]) on.toArray(OperatorNames);
385: }
386:
387: public String visitAssignmentExpression(SimpleNode node,
388: String[] children) {
389: int this Prec = prec(((ASTOperator) node.get(1)).getOperator(),
390: false);
391: assert children.length == 3;
392: children[2] = maybeAddParens(this Prec, node.get(2),
393: children[2], true);
394: return children[0] + SPACE + children[1]
395: + delimit(children[2], false);
396: }
397:
398: public String visitCallExpression(SimpleNode node, String[] children) {
399: int this Prec = prec(Ops.LPAREN, true);
400: children[0] = maybeAddParens(this Prec, node.get(0),
401: children[0], true);
402: return children[0] + "(" + children[1] + ")";
403: }
404:
405: public String visitSuperCallExpression(SimpleNode node,
406: String[] children) {
407: // Same as above
408: return "super." + children[0]
409: + ("".equals(children[1]) ? "" : ("." + children[1]))
410: + "(" + children[2] + ")";
411: }
412:
413: public String visitConditionalExpression(SimpleNode node,
414: String[] children) {
415: int this Prec = prec(Ops.COLON, false);
416: for (int i = 0; i < children.length; i++) {
417: children[i] = maybeAddParens(this Prec, node.get(i),
418: children[i]);
419: }
420: return children[0] + CONDITIONAL + children[1] + ALTERNATIVE
421: + children[2];
422: }
423:
424: public String visitEmptyExpression(SimpleNode node,
425: String[] children) {
426: return "";
427: }
428:
429: public String visitForVarInStatement(SimpleNode node,
430: String[] children) {
431: return "for" + OPENPAREN + "var " + children[0] + " in "
432: + children[2] + CLOSEPAREN + makeBlock(children[3]);
433: }
434:
435: public String visitForInStatement(SimpleNode node, String[] children) {
436: return "for" + OPENPAREN + children[0] + " in " + children[1]
437: + CLOSEPAREN + makeBlock(children[2]);
438: }
439:
440: public String visitForVarStatement(SimpleNode node,
441: String[] children) {
442: // Need explicit semi because init clause may be empty
443: return "for" + OPENPAREN + elideSemi(children[0]) + SEMI
444: + children[1] + SEMI + children[2] + CLOSEPAREN
445: + makeBlock(children[3]);
446: }
447:
448: public String visitIfStatement(SimpleNode node, String[] children) {
449: if (children.length == 2) {
450: return "if" + OPENPAREN + children[0] + CLOSEPAREN
451: + makeBlock(children[1]);
452: } else if (children.length == 3) {
453: return "if" + OPENPAREN + children[0] + CLOSEPAREN
454: + makeBlock(children[1]) + SPACE + "else" + SPACE
455: + makeBlock(children[2]);
456: }
457: return defaultVisitor(node, children);
458: }
459:
460: public String visitNewExpression(SimpleNode node, String[] children) {
461: int this Prec = prec(Ops.NEW, true);
462: SimpleNode c = node.get(0);
463: children[0] = maybeAddParens(this Prec, c, children[0]);
464: return "new " + children[0] + "(" + children[1] + ")";
465: }
466:
467: public String visitPragmaDirective(SimpleNode node,
468: String[] children) {
469: return "#pragma " + children[0];
470: }
471:
472: public String visitPostfixExpression(SimpleNode node,
473: String[] children) {
474: int op = ((ASTOperator) node.get(1)).getOperator();
475: int this Prec = prec(op, true);
476: children[0] = maybeAddParens(this Prec, node.get(0), children[0]);
477: return children[0] + children[1];
478: }
479:
480: public String visitPropertyIdentifierReference(SimpleNode node,
481: String[] children) {
482: // These have prec of 0 even though they don't have ops
483: int this Prec = 0;
484: for (int i = 0; i < children.length; i++) {
485: children[i] = maybeAddParens(this Prec, node.get(i),
486: children[i], true);
487: }
488: return children[0] + "." + children[1];
489: }
490:
491: public String visitPropertyValueReference(SimpleNode node,
492: String[] children) {
493: // These have prec of 0 even though they don't have ops
494: int this Prec = 0;
495: children[0] = maybeAddParens(this Prec, node.get(0),
496: children[0], true);
497: return children[0] + "[" + children[1] + "]";
498: }
499:
500: public String visitReturnStatement(SimpleNode node,
501: String[] children) {
502: return "return" + delimit(children[0]);
503: }
504:
505: public String visitThisReference(SimpleNode node, String[] children) {
506: return "this";
507: }
508:
509: public String visitContinueStatement(SimpleNode node,
510: String[] children) {
511: return "continue"
512: + (children.length > 0 ? delimit(children[0]) : "");
513: }
514:
515: public String visitBreakStatement(SimpleNode node, String[] children) {
516: return "break"
517: + (children.length > 0 ? delimit(children[0]) : "");
518: }
519:
520: public String visitUnaryExpression(SimpleNode node,
521: String[] children) {
522: // Prefix and Unary are the same node
523: int op = ((ASTOperator) node.get(0)).getOperator();
524: boolean letter = java.lang.Character.isLetter(OperatorNames[op]
525: .charAt(0));
526: int this Prec = prec(op, true);
527: children[1] = maybeAddParens(this Prec, node.get(1), children[1]);
528: return children[0] + (letter ? " " : "") + children[1];
529: }
530:
531: public String visitWithStatement(SimpleNode node, String[] children) {
532: return "with" + OPENPAREN + children[0] + CLOSEPAREN
533: + makeBlock(children[1]);
534: }
535:
536: public String visitWhileStatement(SimpleNode node, String[] children) {
537: return "while" + OPENPAREN + children[0] + CLOSEPAREN
538: + makeBlock(children[1]);
539: }
540:
541: public String visitDoWhileStatement(SimpleNode node,
542: String[] children) {
543: return "do" + makeBlock(children[0]) + SPACE + "while"
544: + OPENPAREN + children[1] + ")";
545: }
546:
547: public String visitDefaultClause(SimpleNode node, String[] children) {
548: return "default:"
549: + NEWLINE
550: + (children.length > 0 ? (children[0] + OPTIONAL_SEMI)
551: : "");
552: }
553:
554: public String visitCaseClause(SimpleNode node, String[] children) {
555: return "case"
556: + delimit(children[0])
557: + ":"
558: + NEWLINE
559: + (children.length > 1 ? (children[1] + OPTIONAL_SEMI)
560: : "");
561: }
562:
563: public String visitSwitchStatement(SimpleNode node,
564: String[] children) {
565: String body = "";
566: for (int i = 1, len = children.length; i < len; i++) {
567: body += children[i];
568: }
569: return "switch" + OPENPAREN + children[0] + CLOSEPAREN
570: + makeBlock(body);
571: }
572:
573: // TODO: [2005-11-15 ptw] Make this a simple lookup table based on
574: // the operator
575: public int prec(int op, boolean unary) {
576: String n = OperatorNames[op];
577: String classes[][] = {
578: { "(", "[", ".", "new" },
579: { "!", "~", "-", "+", "--", "++", "typeof", "void",
580: "delete" },
581: { "*", "/", "%" },
582: { "+", "-" },
583: { "<<", ">>", ">>>" },
584: { "<", "<=", ">", ">=", "instanceof", "in", "is",
585: "cast" },
586: { "==", "!=", "===", "!==" },
587: { "&" },
588: { "^" },
589: { "|" },
590: { "&&" },
591: { "||" },
592: { "?", ":" },
593: { "=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=",
594: ">>>=", "&=", "^=", "|=" }, { "," } };
595: for (int i = (unary ? 0 : 2), il = classes.length; i < il; i++) {
596: for (int j = 0, jl = classes[i].length; j < jl; j++) {
597: if (classes[i][j].equals(n)) {
598: return -i;
599: }
600: }
601: }
602: assert false : "unknown operator: " + n;
603: return 1;
604: }
605:
606: public String visitArrayLiteral(SimpleNode node, String[] children) {
607: int this Prec = prec(Ops.COMMA, false);
608: for (int i = 0; i < children.length; i++) {
609: children[i] = maybeAddParens(this Prec, node.get(i),
610: children[i], false);
611: }
612: return "[" + join(COMMA, children) + "]";
613: }
614:
615: public String maybeAddParens(int parentPrec, SimpleNode node,
616: String nodeRep) {
617: return maybeAddParens(parentPrec, node, nodeRep, false);
618: }
619:
620: // Set assoc to true if the sub-expression appears in a place
621: // where operator associativity implies the parens, e.g. on the
622: // left operand of a binary operator that is left-to-right
623: // associative. (It is always safe to leave it false, you will
624: // just end up with extra parens where you don't need them, which
625: // will impact compression but not correctness.)
626: public String maybeAddParens(int parentPrec, SimpleNode node,
627: String nodeRep, boolean assoc) {
628: int this Prec = Integer.MAX_VALUE;
629: if (node instanceof ASTBinaryExpressionSequence
630: || node instanceof ASTAssignmentExpression) {
631: this Prec = prec(((ASTOperator) node.get(1)).getOperator(),
632: false);
633: } else if (node instanceof ASTUnaryExpression) {
634: this Prec = prec(((ASTOperator) node.get(0)).getOperator(),
635: true);
636: } else if (node instanceof ASTPostfixExpression) {
637: this Prec = prec(((ASTOperator) node.get(1)).getOperator(),
638: true);
639: } else if (node instanceof ASTAndExpressionSequence) {
640: this Prec = prec(Ops.SC_AND, false);
641: } else if (node instanceof ASTOrExpressionSequence) {
642: this Prec = prec(Ops.SC_OR, false);
643: } else if (node instanceof ASTConditionalExpression) {
644: this Prec = prec(Ops.COLON, false);
645: } else if (node instanceof ASTNewExpression) {
646: this Prec = prec(Ops.NEW, true);
647: } else if (node instanceof ASTCallExpression
648: || node instanceof ASTSuperCallExpression) {
649: this Prec = prec(Ops.LPAREN, true);
650: } else if (node instanceof ASTPropertyValueReference) {
651: this Prec = prec(Ops.LBRACKET, true);
652: } else if (node instanceof ASTPropertyIdentifierReference) {
653: this Prec = prec(Ops.DOT, true);
654: } else if (node instanceof ASTExpressionList) {
655: this Prec = prec(Ops.COMMA, false);
656: } else if (// Our compiler is broken -- if one of these shows up
657: // in an expression, it had to have been in an
658: // expression list initially
659: node instanceof ASTFunctionExpression
660: || node instanceof ASTFunctionDeclaration) {
661: this Prec = prec(Ops.ASSIGN, false);
662: } else if (node instanceof ASTObjectLiteral
663: || node instanceof ASTArrayLiteral
664: || node instanceof ASTIdentifier
665: || node instanceof ASTThisReference
666: || node instanceof ASTLiteral) {
667: ;
668: } else {
669: System.err.println("No prec for " + node + " in "
670: + Compiler.nodeString(node));
671: (new CompilerException()).printStackTrace();
672: }
673:
674: if (assoc ? (this Prec < parentPrec) : (this Prec <= parentPrec)) {
675: nodeRep = "(" + nodeRep + ")";
676: }
677: return nodeRep;
678: }
679:
680: public String visitAndOrExpressionSequence(boolean isAnd,
681: SimpleNode node, String[] children) {
682: int this Prec = prec(isAnd ? Ops.SC_AND : Ops.SC_OR, false);
683: children[0] = maybeAddParens(this Prec, node.get(0),
684: children[0], true);
685: for (int i = 1; i < children.length; i++) {
686: children[i] = delimit(maybeAddParens(this Prec, node.get(i),
687: children[i]), false);
688: }
689: return join(isAnd ? (SPACE + "&&") : (SPACE + "||"), children);
690: }
691:
692: public String visitExpressionList(SimpleNode node, String[] children) {
693: int this Prec = prec(Ops.COMMA, false);
694: for (int i = 0; i < children.length; i++) {
695: children[i] = maybeAddParens(this Prec, node.get(i),
696: children[i]);
697: }
698: return join(COMMA, children);
699: }
700:
701: public String visitBinaryExpressionSequence(SimpleNode node,
702: String[] children) {
703: int this Prec = prec(((ASTOperator) node.get(1)).getOperator(),
704: false);
705: for (int i = 0; i < children.length; i += (i == 0 ? 2 : 1)) {
706: children[i] = maybeAddParens(this Prec, node.get(i),
707: children[i], i == 0);
708: }
709:
710: String op = children[1];
711: char opChar = op.charAt(op.length() - 1);
712: StringBuffer sb = new StringBuffer();
713: boolean required = java.lang.Character.isLetter(op.charAt(0));
714: String space = required ? " " : SPACE;
715: sb.append(children[0]);
716: for (int x = 2; x < (children.length); x++) {
717: String child = children[x];
718: sb.append(space);
719: sb.append(op);
720: // Disambiguate `a + ++b`, `a++ + b` etc.
721: sb.append(delimit(child, required
722: || opChar == child.charAt(0)));
723: }
724: return (sb.toString());
725: }
726:
727: public String visitFunctionDeclaration(SimpleNode node,
728: String[] children) {
729: return doFunctionDeclaration(node, children, true);
730: }
731:
732: public String visitFunctionExpression(SimpleNode node,
733: String[] children) {
734: // Elide optional name if compressing, otherwise leave it for debugging
735: return doFunctionDeclaration(node, children,
736: this .compress ? false : true);
737: }
738:
739: String doFunctionDeclaration(SimpleNode node, String[] children,
740: boolean useName) {
741: String name, args, body;
742: if (children.length == 2) {
743: name = "";
744: args = children[0];
745: body = children[1];
746: } else if (children.length == 3) {
747: name = children[0];
748: args = children[1];
749: body = children[2];
750: } else {
751: return defaultVisitor(node, children);
752: }
753: String loc = "";
754: // Add location information if not compressing
755: if ((!this .compress) && (node.filename != null)
756: && (node.beginLine != 0)) {
757: loc = ("\n/* -*- file: " + Compiler.getLocationString(node) + " -*- */\n");
758: }
759: return loc + "function" + (useName ? (" " + name) : "")
760: + OPENPAREN + args + CLOSEPAREN + makeBlock(body);
761: }
762:
763: public String visitIdentifier(SimpleNode node, String[] children) {
764: return ((ASTIdentifier) node).getName();
765: }
766:
767: static Double zero = new Double(0);
768:
769: public String visitLiteral(SimpleNode node, String[] children) {
770: Object value = ((ASTLiteral) node).getValue();
771: if (value instanceof String) {
772: return ScriptCompiler.quote((String) value);
773: }
774: if (value instanceof Double) {
775: // Make integers compact
776: Double n = (Double) value;
777: long l = n.longValue();
778: if ((double) l == n.doubleValue()) {
779: if (l == 0) {
780: return "0";
781: } else {
782: String d = Long.toString(l);
783: if (compress && l > 0) {
784: String h = "0x" + Long.toHexString(l);
785: if (h.length() <= d.length()) {
786: return h;
787: }
788: }
789: return d;
790: }
791: }
792: }
793: return "" + value;
794: }
795:
796: public String visitObjectLiteral(SimpleNode node, String[] children) {
797: StringBuffer s = new StringBuffer("{");
798: int len = children.length - 1;
799: int this Prec = prec(Ops.COMMA, false);
800: for (int i = 0; i < len; i++) {
801: if (i % 2 != 0) {
802: children[i] = maybeAddParens(this Prec, node.get(i),
803: children[i], false);
804: s.append(children[i]);
805: s.append(COMMA);
806: } else {
807: s.append(children[i]);
808: s.append(COLON);
809: }
810: }
811: if (len > 0) {
812: children[len] = maybeAddParens(this Prec, node.get(len),
813: children[len], false);
814: s.append(children[len]);
815: }
816: s.append("}");
817: return s.toString();
818: }
819:
820: public String visitOperator(SimpleNode op, String[] children) {
821: int operator = ((ASTOperator) op).getOperator();
822: return OperatorNames[operator];
823: }
824:
825: public String visitVariableStatement(SimpleNode node,
826: String[] children) {
827: assert children.length == 1;
828: // Ensure an expression becomes a statement by appending an
829: // explicit semicolon
830: return "var " + children[0] + SEMI;
831: }
832:
833: public String visitVariableDeclaration(SimpleNode node,
834: String[] children) {
835: if (children.length > 1) {
836: int this Prec = prec(Ops.ASSIGN, false);
837: assert children.length == 2;
838: children[1] = maybeAddParens(this Prec, node.get(1),
839: children[1], true);
840: return children[0] + ASSIGN + children[1];
841: } else {
842: return children[0];
843: }
844: }
845:
846: public String visitVariableDeclarationList(SimpleNode node,
847: String[] children) {
848: return join(COMMA, children);
849: }
850:
851: public String visitTryStatement(SimpleNode node, String[] children) {
852: if (children.length == 2) {
853: return "try" + SPACE + makeBlock(children[0]) + NEWLINE
854: + children[1];
855: } else if (children.length == 3) {
856: return "try" + SPACE + makeBlock(children[0]) + NEWLINE
857: + children[1] + NEWLINE + children[2];
858: }
859: return defaultVisitor(node, children);
860: }
861:
862: public String visitCatchClause(SimpleNode node, String[] children) {
863: return "catch" + OPENPAREN + children[0] + CLOSEPAREN
864: + makeBlock(children[1]);
865: }
866:
867: public String visitFinallyClause(SimpleNode node, String[] children) {
868: return "finally" + SPACE + makeBlock(children[0]);
869: }
870:
871: public String visitThrowStatement(SimpleNode node, String[] children) {
872: return "throw" + delimit(children[0]);
873: }
874: }
|