001 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
002 // for details. All rights reserved. Use of this source code is governed by a
003 // BSD-style license that can be found in the LICENSE file.
004
005 package com.google.dart.compiler.backend.js;
006
007 import com.google.dart.compiler.backend.js.ast.*;
008 import com.google.dart.compiler.backend.js.ast.JsExpressionStatement;
009
010 /**
011 * Determines if an expression statement needs to be surrounded by parentheses.
012 * <p/>
013 * The statement or the left-most expression needs to be surrounded by
014 * parentheses if the left-most expression is an object literal or a function
015 * object. Function declarations do not need parentheses.
016 * <p/>
017 * For example the following require parentheses:<br>
018 * <ul>
019 * <li>{ key : 'value'}</li>
020 * <li>{ key : 'value'}.key</li>
021 * <li>function () {return 1;}()</li>
022 * <li>function () {return 1;}.prototype</li>
023 * </ul>
024 * <p/>
025 * The following do not require parentheses:<br>
026 * <ul>
027 * <li>var x = { key : 'value'}</li>
028 * <li>"string" + { key : 'value'}.key</li>
029 * <li>function func() {}</li>
030 * <li>function() {}</li>
031 * </ul>
032 */
033 public class JsFirstExpressionVisitor extends RecursiveJsVisitor {
034 public static boolean exec(JsExpressionStatement statement) {
035 JsExpression expression = statement.getExpression();
036 // Pure function declarations do not need parentheses
037 if (expression instanceof JsFunction) {
038 return false;
039 }
040
041 JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor();
042 visitor.accept(statement.getExpression());
043 return visitor.needsParentheses;
044 }
045
046 private boolean needsParentheses = false;
047
048 private JsFirstExpressionVisitor() {
049 }
050
051 @Override
052 public void visitArrayAccess(JsArrayAccess x) {
053 accept(x.getArrayExpression());
054 }
055
056 @Override
057 public void visitArray(JsArrayLiteral x) {
058 }
059
060 @Override
061 public void visitBinaryExpression(JsBinaryOperation x) {
062 accept(x.getArg1());
063 }
064
065 @Override
066 public void visitConditional(JsConditional x) {
067 accept(x.getTestExpression());
068 }
069
070 @Override
071 public void visitFunction(JsFunction x) {
072 needsParentheses = true;
073 }
074
075 @Override
076 public void visitInvocation(JsInvocation invocation) {
077 accept(invocation.getQualifier());
078 }
079
080 @Override
081 public void visitNameRef(JsNameRef nameRef) {
082 if (!nameRef.isLeaf()) {
083 accept(nameRef.getQualifier());
084 }
085 }
086
087 @Override
088 public void visitNew(JsNew x) {
089 }
090
091 @Override
092 public void visitObjectLiteral(JsObjectLiteral x) {
093 needsParentheses = true;
094 }
095
096 @Override
097 public void visitPostfixOperation(JsPostfixOperation x) {
098 accept(x.getArg());
099 }
100
101 @Override
102 public void visitPrefixOperation(JsPrefixOperation x) {
103 }
104 }