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.interpreter;
18
19import com.google.clearsilver.jsilver.autoescape.EscapeMode;
20import com.google.clearsilver.jsilver.data.DataContext;
21import com.google.clearsilver.jsilver.functions.FunctionExecutor;
22import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
23import com.google.clearsilver.jsilver.syntax.node.AAddExpression;
24import com.google.clearsilver.jsilver.syntax.node.AAndExpression;
25import com.google.clearsilver.jsilver.syntax.node.ADecimalExpression;
26import com.google.clearsilver.jsilver.syntax.node.ADescendVariable;
27import com.google.clearsilver.jsilver.syntax.node.ADivideExpression;
28import com.google.clearsilver.jsilver.syntax.node.AEqExpression;
29import com.google.clearsilver.jsilver.syntax.node.AExistsExpression;
30import com.google.clearsilver.jsilver.syntax.node.AFunctionExpression;
31import com.google.clearsilver.jsilver.syntax.node.AGtExpression;
32import com.google.clearsilver.jsilver.syntax.node.AGteExpression;
33import com.google.clearsilver.jsilver.syntax.node.AHexExpression;
34import com.google.clearsilver.jsilver.syntax.node.ALtExpression;
35import com.google.clearsilver.jsilver.syntax.node.ALteExpression;
36import com.google.clearsilver.jsilver.syntax.node.AModuloExpression;
37import com.google.clearsilver.jsilver.syntax.node.AMultiplyExpression;
38import com.google.clearsilver.jsilver.syntax.node.ANameVariable;
39import com.google.clearsilver.jsilver.syntax.node.ANeExpression;
40import com.google.clearsilver.jsilver.syntax.node.ANegativeExpression;
41import com.google.clearsilver.jsilver.syntax.node.ANotExpression;
42import com.google.clearsilver.jsilver.syntax.node.ANumericAddExpression;
43import com.google.clearsilver.jsilver.syntax.node.ANumericEqExpression;
44import com.google.clearsilver.jsilver.syntax.node.ANumericExpression;
45import com.google.clearsilver.jsilver.syntax.node.ANumericNeExpression;
46import com.google.clearsilver.jsilver.syntax.node.AOrExpression;
47import com.google.clearsilver.jsilver.syntax.node.AStringExpression;
48import com.google.clearsilver.jsilver.syntax.node.ASubtractExpression;
49import com.google.clearsilver.jsilver.syntax.node.AVariableExpression;
50import com.google.clearsilver.jsilver.syntax.node.PExpression;
51import com.google.clearsilver.jsilver.values.Value;
52import static com.google.clearsilver.jsilver.values.Value.literalValue;
53
54import java.util.LinkedList;
55
56/**
57 * Walks the tree of a PExpression node and evaluates the expression.
58 * @see #evaluate(PExpression)
59 */
60public 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