• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    */
translateToString(PExpression csExpression)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    */
translateToBoolean(PExpression csExpression)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    */
translateToNumber(PExpression csExpression)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    */
translateToData(PExpression csExpression)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    */
translateToVarName(PExpression csExpression)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    */
translateToValue(PExpression csExpression)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    */
declareAsVariable(String name, PExpression csExpression)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    */
translateUntyped(PExpression csExpression)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 
setResult(JavaExpression javaExpression)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
caseAVariableExpression(AVariableExpression node)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
caseAStringExpression(AStringExpression node)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
caseADecimalExpression(ADecimalExpression node)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
caseAHexExpression(AHexExpression node)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
caseANumericExpression(ANumericExpression node)196   public void caseANumericExpression(ANumericExpression node) {
197     setResult(cast(Type.INT, node.getExpression()));
198   }
199 
200   @Override
caseANotExpression(ANotExpression node)201   public void caseANotExpression(ANotExpression node) {
202     setResult(prefix(Type.BOOLEAN, Type.BOOLEAN, "!", node.getExpression()));
203   }
204 
205   @Override
caseAExistsExpression(AExistsExpression node)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
caseAEqExpression(AEqExpression node)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
caseANumericEqExpression(ANumericEqExpression node)232   public void caseANumericEqExpression(ANumericEqExpression node) {
233     setResult(infix(Type.BOOLEAN, Type.INT, "==", node.getLeft(), node.getRight()));
234   }
235 
236   @Override
caseANeExpression(ANeExpression node)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
caseANumericNeExpression(ANumericNeExpression node)244   public void caseANumericNeExpression(ANumericNeExpression node) {
245     setResult(infix(Type.BOOLEAN, Type.INT, "!=", node.getLeft(), node.getRight()));
246   }
247 
248   @Override
caseALtExpression(ALtExpression node)249   public void caseALtExpression(ALtExpression node) {
250     setResult(infix(Type.BOOLEAN, Type.INT, "<", node.getLeft(), node.getRight()));
251   }
252 
253   @Override
caseAGtExpression(AGtExpression node)254   public void caseAGtExpression(AGtExpression node) {
255     setResult(infix(Type.BOOLEAN, Type.INT, ">", node.getLeft(), node.getRight()));
256   }
257 
258   @Override
caseALteExpression(ALteExpression node)259   public void caseALteExpression(ALteExpression node) {
260     setResult(infix(Type.BOOLEAN, Type.INT, "<=", node.getLeft(), node.getRight()));
261   }
262 
263   @Override
caseAGteExpression(AGteExpression node)264   public void caseAGteExpression(AGteExpression node) {
265     setResult(infix(Type.BOOLEAN, Type.INT, ">=", node.getLeft(), node.getRight()));
266   }
267 
268   @Override
caseAAndExpression(AAndExpression node)269   public void caseAAndExpression(AAndExpression node) {
270     setResult(infix(Type.BOOLEAN, Type.BOOLEAN, "&&", node.getLeft(), node.getRight()));
271   }
272 
273   @Override
caseAOrExpression(AOrExpression node)274   public void caseAOrExpression(AOrExpression node) {
275     setResult(infix(Type.BOOLEAN, Type.BOOLEAN, "||", node.getLeft(), node.getRight()));
276   }
277 
278   @Override
caseAAddExpression(AAddExpression node)279   public void caseAAddExpression(AAddExpression node) {
280     setResult(infix(Type.STRING, Type.STRING, "+", node.getLeft(), node.getRight()));
281   }
282 
283   @Override
caseANumericAddExpression(ANumericAddExpression node)284   public void caseANumericAddExpression(ANumericAddExpression node) {
285     setResult(infix(Type.INT, Type.INT, "+", node.getLeft(), node.getRight()));
286   }
287 
288   @Override
caseASubtractExpression(ASubtractExpression node)289   public void caseASubtractExpression(ASubtractExpression node) {
290     setResult(infix(Type.INT, Type.INT, "-", node.getLeft(), node.getRight()));
291   }
292 
293   @Override
caseAMultiplyExpression(AMultiplyExpression node)294   public void caseAMultiplyExpression(AMultiplyExpression node) {
295     setResult(infix(Type.INT, Type.INT, "*", node.getLeft(), node.getRight()));
296   }
297 
298   @Override
caseADivideExpression(ADivideExpression node)299   public void caseADivideExpression(ADivideExpression node) {
300     setResult(infix(Type.INT, Type.INT, "/", node.getLeft(), node.getRight()));
301   }
302 
303   @Override
caseAModuloExpression(AModuloExpression node)304   public void caseAModuloExpression(AModuloExpression node) {
305     setResult(infix(Type.INT, Type.INT, "%", node.getLeft(), node.getRight()));
306   }
307 
308   @Override
caseANegativeExpression(ANegativeExpression node)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
caseAFunctionExpression(AFunctionExpression node)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    */
function(String name, PExpression... csExpressions)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 
infix(Type destType, Type srcType, String infix, PExpression leftNode, PExpression rightNode)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 
prefix(Type destType, Type srcType, String prefix, PExpression node)363   private JavaExpression prefix(Type destType, Type srcType, String prefix, PExpression node) {
364     return JavaExpression.prefix(destType, prefix, cast(srcType, node));
365   }
366 
cast(Type type, PExpression node)367   private JavaExpression cast(Type type, PExpression node) {
368     node.apply(this);
369     return currentJavaExpression.cast(type);
370   }
371 }
372