Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.clearsilver.jsilver.compiler;
     18 
     19 import com.google.clearsilver.jsilver.compiler.JavaExpression.Type;
     20 import static com.google.clearsilver.jsilver.compiler.JavaExpression.bool;
     21 import static com.google.clearsilver.jsilver.compiler.JavaExpression.call;
     22 import static com.google.clearsilver.jsilver.compiler.JavaExpression.callFindVariable;
     23 import static com.google.clearsilver.jsilver.compiler.JavaExpression.callOn;
     24 import static com.google.clearsilver.jsilver.compiler.JavaExpression.declare;
     25 import static com.google.clearsilver.jsilver.compiler.JavaExpression.integer;
     26 import static com.google.clearsilver.jsilver.compiler.JavaExpression.string;
     27 import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
     28 import com.google.clearsilver.jsilver.syntax.node.AAddExpression;
     29 import com.google.clearsilver.jsilver.syntax.node.AAndExpression;
     30 import com.google.clearsilver.jsilver.syntax.node.ADecimalExpression;
     31 import com.google.clearsilver.jsilver.syntax.node.ADescendVariable;
     32 import com.google.clearsilver.jsilver.syntax.node.ADivideExpression;
     33 import com.google.clearsilver.jsilver.syntax.node.AEqExpression;
     34 import com.google.clearsilver.jsilver.syntax.node.AExistsExpression;
     35 import com.google.clearsilver.jsilver.syntax.node.AFunctionExpression;
     36 import com.google.clearsilver.jsilver.syntax.node.AGtExpression;
     37 import com.google.clearsilver.jsilver.syntax.node.AGteExpression;
     38 import com.google.clearsilver.jsilver.syntax.node.AHexExpression;
     39 import com.google.clearsilver.jsilver.syntax.node.ALtExpression;
     40 import com.google.clearsilver.jsilver.syntax.node.ALteExpression;
     41 import com.google.clearsilver.jsilver.syntax.node.AModuloExpression;
     42 import com.google.clearsilver.jsilver.syntax.node.AMultiplyExpression;
     43 import com.google.clearsilver.jsilver.syntax.node.ANameVariable;
     44 import com.google.clearsilver.jsilver.syntax.node.ANeExpression;
     45 import com.google.clearsilver.jsilver.syntax.node.ANegativeExpression;
     46 import com.google.clearsilver.jsilver.syntax.node.ANotExpression;
     47 import com.google.clearsilver.jsilver.syntax.node.ANumericAddExpression;
     48 import com.google.clearsilver.jsilver.syntax.node.ANumericEqExpression;
     49 import com.google.clearsilver.jsilver.syntax.node.ANumericExpression;
     50 import com.google.clearsilver.jsilver.syntax.node.ANumericNeExpression;
     51 import com.google.clearsilver.jsilver.syntax.node.AOrExpression;
     52 import com.google.clearsilver.jsilver.syntax.node.AStringExpression;
     53 import com.google.clearsilver.jsilver.syntax.node.ASubtractExpression;
     54 import com.google.clearsilver.jsilver.syntax.node.AVariableExpression;
     55 import com.google.clearsilver.jsilver.syntax.node.PExpression;
     56 
     57 import java.util.LinkedList;
     58 
     59 /**
     60  * Translates a CS expression (from the AST) into an equivalent Java expression.
     61  *
     62  * In order to optimize the expressions nicely this class emits code using a series of wrapper
     63  * functions for casting to/from various types. Rather than the old style of saying:
     64  *
     65  * <pre>ValueX.asFoo()</pre>
     66  *
     67  * we now write:
     68  *
     69  * <pre>asFoo(ValueX)</pre>
     70  *
     71  * This is actually very important because it means that as we optimize the expressions to return
     72  * fundamental types, we just have different versions of the {@code asFoo()} methods that take the
     73  * appropriate types. The user of the expression is responsible for casting it and the producer of
     74  * the expression is now free to produce optimized expressions.
     75  */
     76 public class ExpressionTranslator extends DepthFirstAdapter {
     77 
     78   private JavaExpression currentJavaExpression;
     79 
     80   /**
     81    * Translate a template AST expression into a Java String expression.
     82    */
     83   public JavaExpression translateToString(PExpression csExpression) {
     84     return translateUntyped(csExpression).cast(Type.STRING);
     85   }
     86 
     87   /**
     88    * Translate a template AST expression into a Java boolean expression.
     89    */
     90   public JavaExpression translateToBoolean(PExpression csExpression) {
     91     return translateUntyped(csExpression).cast(Type.BOOLEAN);
     92   }
     93 
     94   /**
     95    * Translate a template AST expression into a Java integer expression.
     96    */
     97   public JavaExpression translateToNumber(PExpression csExpression) {
     98     return translateUntyped(csExpression).cast(Type.INT);
     99   }
    100 
    101   /**
    102    * Translate a template AST expression into a Java Data expression.
    103    */
    104   public JavaExpression translateToData(PExpression csExpression) {
    105     return translateUntyped(csExpression).cast(Type.DATA);
    106   }
    107 
    108   /**
    109    * Translate a template AST expression into a Java Data expression.
    110    */
    111   public JavaExpression translateToVarName(PExpression csExpression) {
    112     return translateUntyped(csExpression).cast(Type.VAR_NAME);
    113   }
    114 
    115   /**
    116    * Translate a template AST expression into a Java Value expression.
    117    */
    118   public JavaExpression translateToValue(PExpression csExpression) {
    119     return translateUntyped(csExpression).cast(Type.VALUE);
    120   }
    121 
    122   /**
    123    * Declares the (typed) expression as a variable with the given name. (e.g. "int foo = 5" or
    124    * "Data foo = Data.getChild("a.b")"
    125    */
    126   public JavaExpression declareAsVariable(String name, PExpression csExpression) {
    127     JavaExpression expression = translateUntyped(csExpression);
    128     Type type = expression.getType();
    129     assert type != null : "all subexpressions should be typed";
    130     return declare(type, name, expression);
    131   }
    132 
    133   /**
    134    * Translate a template AST expression into an untyped expression.
    135    */
    136   public JavaExpression translateUntyped(PExpression csExpression) {
    137     try {
    138       assert currentJavaExpression == null : "Not reentrant";
    139       csExpression.apply(this);
    140       assert currentJavaExpression != null : "No expression created";
    141       return currentJavaExpression;
    142     } finally {
    143       currentJavaExpression = null;
    144     }
    145   }
    146 
    147   private void setResult(JavaExpression javaExpression) {
    148     this.currentJavaExpression = javaExpression;
    149   }
    150 
    151   /**
    152    * Process AST node for a variable (e.g. a.b.c).
    153    */
    154   @Override
    155   public void caseAVariableExpression(AVariableExpression node) {
    156     JavaExpression varName = new VariableTranslator(this).translate(node.getVariable());
    157     setResult(varName);
    158   }
    159 
    160   /**
    161    * Process AST node for a string (e.g. "hello").
    162    */
    163   @Override
    164   public void caseAStringExpression(AStringExpression node) {
    165     String value = node.getValue().getText();
    166     value = value.substring(1, value.length() - 1); // Remove enclosing quotes.
    167     setResult(string(value));
    168   }
    169 
    170   /**
    171    * Process AST node for a decimal integer (e.g. 123).
    172    */
    173   @Override
    174   public void caseADecimalExpression(ADecimalExpression node) {
    175     String value = node.getValue().getText();
    176     setResult(integer(value));
    177   }
    178 
    179   /**
    180    * Process AST node for a hex integer (e.g. 0x1AB).
    181    */
    182   @Override
    183   public void caseAHexExpression(AHexExpression node) {
    184     String value = node.getValue().getText();
    185     // Luckily ClearSilver hex representation is a subset of the Java hex
    186     // representation so we can just use the literal directly.
    187     // TODO: add well-formedness checks whenever literals are used
    188     setResult(integer(value));
    189   }
    190 
    191   /*
    192    * The next block of functions all convert CS operators into dynamically looked up functions.
    193    */
    194 
    195   @Override
    196   public void caseANumericExpression(ANumericExpression node) {
    197     setResult(cast(Type.INT, node.getExpression()));
    198   }
    199 
    200   @Override
    201   public void caseANotExpression(ANotExpression node) {
    202     setResult(prefix(Type.BOOLEAN, Type.BOOLEAN, "!", node.getExpression()));
    203   }
    204 
    205   @Override
    206   public void caseAExistsExpression(AExistsExpression node) {
    207     // Special case. Exists is only ever an issue for variables, all
    208     // other expressions unconditionally exist.
    209     PExpression expression = node.getExpression();
    210     if (expression instanceof AVariableExpression) {
    211       expression.apply(this);
    212       if (currentJavaExpression.getType() == Type.VAR_NAME) {
    213         currentJavaExpression = callFindVariable(currentJavaExpression, false);
    214       }
    215       setResult(call(Type.BOOLEAN, "exists", currentJavaExpression));
    216     } else {
    217       // If it's not a variable, it always exists
    218       // NOTE: It's not clear if we must evaluate the sub-expression
    219       // here (is there anything that can have side effects??)
    220       setResult(bool(true));
    221     }
    222   }
    223 
    224   @Override
    225   public void caseAEqExpression(AEqExpression node) {
    226     JavaExpression left = cast(Type.STRING, node.getLeft());
    227     JavaExpression right = cast(Type.STRING, node.getRight());
    228     setResult(callOn(Type.BOOLEAN, left, "equals", right));
    229   }
    230 
    231   @Override
    232   public void caseANumericEqExpression(ANumericEqExpression node) {
    233     setResult(infix(Type.BOOLEAN, Type.INT, "==", node.getLeft(), node.getRight()));
    234   }
    235 
    236   @Override
    237   public void caseANeExpression(ANeExpression node) {
    238     JavaExpression left = cast(Type.STRING, node.getLeft());
    239     JavaExpression right = cast(Type.STRING, node.getRight());
    240     setResult(JavaExpression.prefix(Type.BOOLEAN, "!", callOn(Type.BOOLEAN, left, "equals", right)));
    241   }
    242 
    243   @Override
    244   public void caseANumericNeExpression(ANumericNeExpression node) {
    245     setResult(infix(Type.BOOLEAN, Type.INT, "!=", node.getLeft(), node.getRight()));
    246   }
    247 
    248   @Override
    249   public void caseALtExpression(ALtExpression node) {
    250     setResult(infix(Type.BOOLEAN, Type.INT, "<", node.getLeft(), node.getRight()));
    251   }
    252 
    253   @Override
    254   public void caseAGtExpression(AGtExpression node) {
    255     setResult(infix(Type.BOOLEAN, Type.INT, ">", node.getLeft(), node.getRight()));
    256   }
    257 
    258   @Override
    259   public void caseALteExpression(ALteExpression node) {
    260     setResult(infix(Type.BOOLEAN, Type.INT, "<=", node.getLeft(), node.getRight()));
    261   }
    262 
    263   @Override
    264   public void caseAGteExpression(AGteExpression node) {
    265     setResult(infix(Type.BOOLEAN, Type.INT, ">=", node.getLeft(), node.getRight()));
    266   }
    267 
    268   @Override
    269   public void caseAAndExpression(AAndExpression node) {
    270     setResult(infix(Type.BOOLEAN, Type.BOOLEAN, "&&", node.getLeft(), node.getRight()));
    271   }
    272 
    273   @Override
    274   public void caseAOrExpression(AOrExpression node) {
    275     setResult(infix(Type.BOOLEAN, Type.BOOLEAN, "||", node.getLeft(), node.getRight()));
    276   }
    277 
    278   @Override
    279   public void caseAAddExpression(AAddExpression node) {
    280     setResult(infix(Type.STRING, Type.STRING, "+", node.getLeft(), node.getRight()));
    281   }
    282 
    283   @Override
    284   public void caseANumericAddExpression(ANumericAddExpression node) {
    285     setResult(infix(Type.INT, Type.INT, "+", node.getLeft(), node.getRight()));
    286   }
    287 
    288   @Override
    289   public void caseASubtractExpression(ASubtractExpression node) {
    290     setResult(infix(Type.INT, Type.INT, "-", node.getLeft(), node.getRight()));
    291   }
    292 
    293   @Override
    294   public void caseAMultiplyExpression(AMultiplyExpression node) {
    295     setResult(infix(Type.INT, Type.INT, "*", node.getLeft(), node.getRight()));
    296   }
    297 
    298   @Override
    299   public void caseADivideExpression(ADivideExpression node) {
    300     setResult(infix(Type.INT, Type.INT, "/", node.getLeft(), node.getRight()));
    301   }
    302 
    303   @Override
    304   public void caseAModuloExpression(AModuloExpression node) {
    305     setResult(infix(Type.INT, Type.INT, "%", node.getLeft(), node.getRight()));
    306   }
    307 
    308   @Override
    309   public void caseANegativeExpression(ANegativeExpression node) {
    310     setResult(prefix(Type.INT, Type.INT, "-", node.getExpression()));
    311   }
    312 
    313   /**
    314    * Process AST node for a function (e.g. dosomething(...)).
    315    */
    316   @Override
    317   public void caseAFunctionExpression(AFunctionExpression node) {
    318     LinkedList<PExpression> argsList = node.getArgs();
    319     PExpression[] args = argsList.toArray(new PExpression[argsList.size()]);
    320 
    321     // Because the function name may have dots in, the parser would have broken
    322     // it into a little node tree which we need to walk to reconstruct the
    323     // full name.
    324     final StringBuilder fullFunctionName = new StringBuilder();
    325     node.getName().apply(new DepthFirstAdapter() {
    326 
    327       @Override
    328       public void caseANameVariable(ANameVariable node11) {
    329         fullFunctionName.append(node11.getWord().getText());
    330       }
    331 
    332       @Override
    333       public void caseADescendVariable(ADescendVariable node12) {
    334         node12.getParent().apply(this);
    335         fullFunctionName.append('.');
    336         node12.getChild().apply(this);
    337       }
    338     });
    339 
    340     setResult(function(fullFunctionName.toString(), args));
    341   }
    342 
    343   /**
    344    * Generate a JavaExpression for calling a function.
    345    */
    346   private JavaExpression function(String name, PExpression... csExpressions) {
    347     // Outputs: context.executeFunction("myfunc", args...);
    348     JavaExpression[] args = new JavaExpression[1 + csExpressions.length];
    349     args[0] = string(name);
    350     for (int i = 0; i < csExpressions.length; i++) {
    351       args[i + 1] = cast(Type.VALUE, csExpressions[i]);
    352     }
    353     return callOn(Type.VALUE, TemplateTranslator.CONTEXT, "executeFunction", args);
    354   }
    355 
    356   private JavaExpression infix(Type destType, Type srcType, String infix, PExpression leftNode,
    357       PExpression rightNode) {
    358     JavaExpression left = cast(srcType, leftNode);
    359     JavaExpression right = cast(srcType, rightNode);
    360     return JavaExpression.infix(destType, infix, left, right);
    361   }
    362 
    363   private JavaExpression prefix(Type destType, Type srcType, String prefix, PExpression node) {
    364     return JavaExpression.prefix(destType, prefix, cast(srcType, node));
    365   }
    366 
    367   private JavaExpression cast(Type type, PExpression node) {
    368     node.apply(this);
    369     return currentJavaExpression.cast(type);
    370   }
    371 }
    372