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
17package com.google.clearsilver.jsilver.compiler;
18
19import com.google.clearsilver.jsilver.compiler.JavaExpression.Type;
20import static com.google.clearsilver.jsilver.compiler.JavaExpression.bool;
21import static com.google.clearsilver.jsilver.compiler.JavaExpression.call;
22import static com.google.clearsilver.jsilver.compiler.JavaExpression.callFindVariable;
23import static com.google.clearsilver.jsilver.compiler.JavaExpression.callOn;
24import static com.google.clearsilver.jsilver.compiler.JavaExpression.declare;
25import static com.google.clearsilver.jsilver.compiler.JavaExpression.integer;
26import static com.google.clearsilver.jsilver.compiler.JavaExpression.string;
27import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
28import com.google.clearsilver.jsilver.syntax.node.AAddExpression;
29import com.google.clearsilver.jsilver.syntax.node.AAndExpression;
30import com.google.clearsilver.jsilver.syntax.node.ADecimalExpression;
31import com.google.clearsilver.jsilver.syntax.node.ADescendVariable;
32import com.google.clearsilver.jsilver.syntax.node.ADivideExpression;
33import com.google.clearsilver.jsilver.syntax.node.AEqExpression;
34import com.google.clearsilver.jsilver.syntax.node.AExistsExpression;
35import com.google.clearsilver.jsilver.syntax.node.AFunctionExpression;
36import com.google.clearsilver.jsilver.syntax.node.AGtExpression;
37import com.google.clearsilver.jsilver.syntax.node.AGteExpression;
38import com.google.clearsilver.jsilver.syntax.node.AHexExpression;
39import com.google.clearsilver.jsilver.syntax.node.ALtExpression;
40import com.google.clearsilver.jsilver.syntax.node.ALteExpression;
41import com.google.clearsilver.jsilver.syntax.node.AModuloExpression;
42import com.google.clearsilver.jsilver.syntax.node.AMultiplyExpression;
43import com.google.clearsilver.jsilver.syntax.node.ANameVariable;
44import com.google.clearsilver.jsilver.syntax.node.ANeExpression;
45import com.google.clearsilver.jsilver.syntax.node.ANegativeExpression;
46import com.google.clearsilver.jsilver.syntax.node.ANotExpression;
47import com.google.clearsilver.jsilver.syntax.node.ANumericAddExpression;
48import com.google.clearsilver.jsilver.syntax.node.ANumericEqExpression;
49import com.google.clearsilver.jsilver.syntax.node.ANumericExpression;
50import com.google.clearsilver.jsilver.syntax.node.ANumericNeExpression;
51import com.google.clearsilver.jsilver.syntax.node.AOrExpression;
52import com.google.clearsilver.jsilver.syntax.node.AStringExpression;
53import com.google.clearsilver.jsilver.syntax.node.ASubtractExpression;
54import com.google.clearsilver.jsilver.syntax.node.AVariableExpression;
55import com.google.clearsilver.jsilver.syntax.node.PExpression;
56
57import 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 */
76public 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