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.interpreter; 18 19 import com.google.clearsilver.jsilver.autoescape.EscapeMode; 20 import com.google.clearsilver.jsilver.data.DataContext; 21 import com.google.clearsilver.jsilver.functions.FunctionExecutor; 22 import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter; 23 import com.google.clearsilver.jsilver.syntax.node.AAddExpression; 24 import com.google.clearsilver.jsilver.syntax.node.AAndExpression; 25 import com.google.clearsilver.jsilver.syntax.node.ADecimalExpression; 26 import com.google.clearsilver.jsilver.syntax.node.ADescendVariable; 27 import com.google.clearsilver.jsilver.syntax.node.ADivideExpression; 28 import com.google.clearsilver.jsilver.syntax.node.AEqExpression; 29 import com.google.clearsilver.jsilver.syntax.node.AExistsExpression; 30 import com.google.clearsilver.jsilver.syntax.node.AFunctionExpression; 31 import com.google.clearsilver.jsilver.syntax.node.AGtExpression; 32 import com.google.clearsilver.jsilver.syntax.node.AGteExpression; 33 import com.google.clearsilver.jsilver.syntax.node.AHexExpression; 34 import com.google.clearsilver.jsilver.syntax.node.ALtExpression; 35 import com.google.clearsilver.jsilver.syntax.node.ALteExpression; 36 import com.google.clearsilver.jsilver.syntax.node.AModuloExpression; 37 import com.google.clearsilver.jsilver.syntax.node.AMultiplyExpression; 38 import com.google.clearsilver.jsilver.syntax.node.ANameVariable; 39 import com.google.clearsilver.jsilver.syntax.node.ANeExpression; 40 import com.google.clearsilver.jsilver.syntax.node.ANegativeExpression; 41 import com.google.clearsilver.jsilver.syntax.node.ANotExpression; 42 import com.google.clearsilver.jsilver.syntax.node.ANumericAddExpression; 43 import com.google.clearsilver.jsilver.syntax.node.ANumericEqExpression; 44 import com.google.clearsilver.jsilver.syntax.node.ANumericExpression; 45 import com.google.clearsilver.jsilver.syntax.node.ANumericNeExpression; 46 import com.google.clearsilver.jsilver.syntax.node.AOrExpression; 47 import com.google.clearsilver.jsilver.syntax.node.AStringExpression; 48 import com.google.clearsilver.jsilver.syntax.node.ASubtractExpression; 49 import com.google.clearsilver.jsilver.syntax.node.AVariableExpression; 50 import com.google.clearsilver.jsilver.syntax.node.PExpression; 51 import com.google.clearsilver.jsilver.values.Value; 52 import static com.google.clearsilver.jsilver.values.Value.literalValue; 53 54 import java.util.LinkedList; 55 56 /** 57 * Walks the tree of a PExpression node and evaluates the expression. 58 * @see #evaluate(PExpression) 59 */ 60 public class ExpressionEvaluator extends DepthFirstAdapter { 61 62 private Value currentValue; 63 64 private final DataContext context; 65 66 private final FunctionExecutor functionExecutor; 67 68 /** 69 * @param context 70 * @param functionExecutor Used for executing functions in expressions. As well as looking up 71 * named functions (e.g. html_escape), it also uses 72 */ 73 public ExpressionEvaluator(DataContext context, FunctionExecutor functionExecutor) { 74 this.context = context; 75 this.functionExecutor = functionExecutor; 76 } 77 78 /** 79 * Evaluate an expression into a single value. 80 */ 81 public Value evaluate(PExpression expression) { 82 assert currentValue == null; 83 84 expression.apply(this); 85 Value result = currentValue; 86 currentValue = null; 87 88 assert result != null : "No result set from " + expression.getClass(); 89 return result; 90 } 91 92 @Override 93 public void caseAVariableExpression(AVariableExpression node) { 94 VariableLocator variableLocator = new VariableLocator(this); 95 String variableName = variableLocator.getVariableName(node.getVariable()); 96 setResult(Value.variableValue(variableName, context)); 97 } 98 99 @Override 100 public void caseAStringExpression(AStringExpression node) { 101 String value = node.getValue().getText(); 102 value = value.substring(1, value.length() - 1); // Remove enclosing quotes. 103 // The expression was a constant string literal. Does not 104 // need to be autoescaped, as it was created by the template developer. 105 Value result = literalValue(value, EscapeMode.ESCAPE_IS_CONSTANT, false); 106 setResult(result); 107 } 108 109 @Override 110 public void caseADecimalExpression(ADecimalExpression node) { 111 String value = node.getValue().getText(); 112 setResult(literalValue(Integer.parseInt(value), EscapeMode.ESCAPE_IS_CONSTANT, false)); 113 } 114 115 @Override 116 public void caseAHexExpression(AHexExpression node) { 117 String value = node.getValue().getText(); 118 value = value.substring(2); // Remove 0x prefix. 119 setResult(literalValue(Integer.parseInt(value, 16), EscapeMode.ESCAPE_IS_CONSTANT, false)); 120 } 121 122 @Override 123 public void caseANumericExpression(ANumericExpression node) { 124 executeFunction("#", node.getExpression()); 125 } 126 127 @Override 128 public void caseANotExpression(ANotExpression node) { 129 executeFunction("!", node.getExpression()); 130 } 131 132 @Override 133 public void caseAExistsExpression(AExistsExpression node) { 134 executeFunction("?", node.getExpression()); 135 } 136 137 @Override 138 public void caseAEqExpression(AEqExpression node) { 139 executeFunction("==", node.getLeft(), node.getRight()); 140 } 141 142 @Override 143 public void caseANumericEqExpression(ANumericEqExpression node) { 144 executeFunction("#==", node.getLeft(), node.getRight()); 145 } 146 147 @Override 148 public void caseANeExpression(ANeExpression node) { 149 executeFunction("!=", node.getLeft(), node.getRight()); 150 } 151 152 @Override 153 public void caseANumericNeExpression(ANumericNeExpression node) { 154 executeFunction("#!=", node.getLeft(), node.getRight()); 155 } 156 157 @Override 158 public void caseALtExpression(ALtExpression node) { 159 executeFunction("<", node.getLeft(), node.getRight()); 160 } 161 162 @Override 163 public void caseAGtExpression(AGtExpression node) { 164 executeFunction(">", node.getLeft(), node.getRight()); 165 } 166 167 @Override 168 public void caseALteExpression(ALteExpression node) { 169 executeFunction("<=", node.getLeft(), node.getRight()); 170 } 171 172 @Override 173 public void caseAGteExpression(AGteExpression node) { 174 executeFunction(">=", node.getLeft(), node.getRight()); 175 } 176 177 @Override 178 public void caseAAndExpression(AAndExpression node) { 179 executeFunction("&&", node.getLeft(), node.getRight()); 180 } 181 182 @Override 183 public void caseAOrExpression(AOrExpression node) { 184 executeFunction("||", node.getLeft(), node.getRight()); 185 } 186 187 @Override 188 public void caseAAddExpression(AAddExpression node) { 189 executeFunction("+", node.getLeft(), node.getRight()); 190 } 191 192 @Override 193 public void caseANumericAddExpression(ANumericAddExpression node) { 194 executeFunction("#+", node.getLeft(), node.getRight()); 195 } 196 197 @Override 198 public void caseASubtractExpression(ASubtractExpression node) { 199 executeFunction("-", node.getLeft(), node.getRight()); 200 } 201 202 @Override 203 public void caseAMultiplyExpression(AMultiplyExpression node) { 204 executeFunction("*", node.getLeft(), node.getRight()); 205 } 206 207 @Override 208 public void caseADivideExpression(ADivideExpression node) { 209 executeFunction("/", node.getLeft(), node.getRight()); 210 } 211 212 @Override 213 public void caseAModuloExpression(AModuloExpression node) { 214 executeFunction("%", node.getLeft(), node.getRight()); 215 } 216 217 @Override 218 public void caseANegativeExpression(ANegativeExpression node) { 219 executeFunction("-", node.getExpression()); 220 } 221 222 @Override 223 public void caseAFunctionExpression(AFunctionExpression node) { 224 LinkedList<PExpression> argsList = node.getArgs(); 225 PExpression[] args = argsList.toArray(new PExpression[argsList.size()]); 226 227 executeFunction(getFullFunctionName(node), args); 228 } 229 230 private void executeFunction(String name, PExpression... expressions) { 231 Value[] args = new Value[expressions.length]; 232 for (int i = 0; i < args.length; i++) { 233 args[i] = evaluate(expressions[i]); 234 } 235 236 setResult(functionExecutor.executeFunction(name, args)); 237 } 238 239 /** 240 * Sets a result from inside an expression. 241 */ 242 private void setResult(Value value) { 243 assert value != null; 244 245 currentValue = value; 246 } 247 248 private String getFullFunctionName(AFunctionExpression node) { 249 final StringBuilder result = new StringBuilder(); 250 node.getName().apply(new DepthFirstAdapter() { 251 252 @Override 253 public void caseANameVariable(ANameVariable node) { 254 result.append(node.getWord().getText()); 255 } 256 257 @Override 258 public void caseADescendVariable(ADescendVariable node) { 259 node.getParent().apply(this); 260 result.append('.'); 261 node.getChild().apply(this); 262 } 263 }); 264 return result.toString(); 265 } 266 267 } 268