Home | History | Annotate | Download | only in interpreter
      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