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