All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.cxx.preprocessor.ExpressionEvaluator Maven / Gradle / Ivy

The newest version!
/*
 * Sonar C++ Plugin (Community)
 * Copyright (C) 2011 Waleri Enns and CONTACT Software GmbH
 * [email protected]
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.cxx.preprocessor;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Token;
import com.sonar.sslr.impl.Parser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.cxx.CxxConfiguration;

import java.util.List;

public final class ExpressionEvaluator {
  public static final Logger LOG = LoggerFactory.getLogger("Evaluator");

  private Parser parser;
  private CxxPreprocessor preprocessor;

  public ExpressionEvaluator(CxxConfiguration conf, CxxPreprocessor preprocessor) {
    parser = CppParser.createConstantExpressionParser(conf);

    this.preprocessor = preprocessor;
  }

  public boolean eval(String constExpr) {
    return evalToInt(constExpr) != 0;
  }

  public boolean eval(AstNode constExpr) {
    return evalToInt(constExpr) != 0;
  }

  private long evalToInt(String constExpr) {
    AstNode constExprAst = null;
    try {
      constExprAst = parser.parse(constExpr);
    } catch (com.sonar.sslr.api.RecognitionException re) {
      LOG.warn("Error evaluating expression '{}', assuming 0", constExpr);
      return 0;
    }

    return evalToInt(constExprAst);
  }

  private long evalToInt(AstNode exprAst) {
    LOG.trace("Evaluating expression: {}", exprAst);
    
    int noChildren = exprAst.getNumberOfChildren();
    if (noChildren == 0) {
      return evalLeaf(exprAst);
    } else if (noChildren == 1) {
      return evalOneChildAst(exprAst);
    }

    return evalComplexAst(exprAst);
  }

  private long evalLeaf(AstNode exprAst) {
    // Evaluation of leafs
    //
    String nodeType = exprAst.getName();
    if ("NUMBER".equals(nodeType)) {
      return evalNumber(exprAst.getTokenValue());
    } else if ("CHARACTER".equals(nodeType)) {
      return evalCharacter(exprAst.getTokenValue());
    } else if ("IDENTIFIER".equals(nodeType)) {
      String value = preprocessor.valueOf(exprAst.getTokenValue());
      return value == null ? 0 : evalToInt(value);
    } else {
      throw new EvaluationException("Unknown expression type '" + nodeType + "'");
    }
  }

  private long evalOneChildAst(AstNode exprAst) {
    // Evaluation of booleans and 'pass-through's
    //
    String nodeType = exprAst.getName();
    if ("bool".equals(nodeType)) {
      return evalBool(exprAst.getTokenValue());
    }
    return evalToInt(exprAst.getChild(0));
  }

  private long evalComplexAst(AstNode exprAst) {
    // More complex expressions with more than one child
    //
    String nodeType = exprAst.getName();
    if ("unaryExpression".equals(nodeType)) {
      return evalUnaryExpression(exprAst);
    } else if ("conditionalExpression".equals(nodeType)) {
      return evalConditionalExpression(exprAst);
    } else if ("logicalOrExpression".equals(nodeType)) {
      return evalLogicalOrExpression(exprAst);
    } else if ("logicalAndExpression".equals(nodeType)) {
      return evalLogicalAndExpression(exprAst);
    } else if ("inclusiveOrExpression".equals(nodeType)) {
      return evalInclusiveOrExpression(exprAst);
    } else if ("exclusiveOrExpression".equals(nodeType)) {
      return evalExclusiveOrExpression(exprAst);
    } else if ("andExpression".equals(nodeType)) {
      return evalAndExpression(exprAst);
    } else if ("equalityExpression".equals(nodeType)) {
      return evalEqualityExpression(exprAst);
    } else if ("relationalExpression".equals(nodeType)) {
      return evalRelationalExpression(exprAst);
    } else if ("shiftExpression".equals(nodeType)) {
      return evalShiftExpression(exprAst);
    } else if ("additiveExpression".equals(nodeType)) {
      return evalAdditiveExpression(exprAst);
    } else if ("multiplicativeExpression".equals(nodeType)) {
      return evalMultiplicativeExpression(exprAst);
    } else if ("primaryExpression".equals(nodeType)) {
      return evalPrimaryExpression(exprAst);
    } else if ("definedExpression".equals(nodeType)) {
      return evalDefinedExpression(exprAst);
    } else if ("functionlikeMacro".equals(nodeType)) {
      return evalFunctionlikeMacro(exprAst);
    } else {
      throw new EvaluationException("Unknown expression type '" + nodeType + "'");
    }
  }

  // ///////////////// Primitives //////////////////////
  long evalBool(String boolValue) {
    return boolValue.equalsIgnoreCase("true") ? 1 : 0;
  }

  long evalNumber(String intValue) {
    // the if expressions arent allowed to contain floats
    long number = 0;
    try {
      number = Long.decode(stripSuffix(intValue)).longValue();
    } catch (java.lang.NumberFormatException nfe) {
      LOG.warn("Cannot decode the number '{}' falling back to max long ({}) instead", intValue, Long.MAX_VALUE);
      number = Long.MAX_VALUE;
    }

    return number;
  }

  long evalCharacter(String charValue) {
    // TODO: replace this simplification by something more sane
    return charValue.equals("'\0'") ? 0 : 1;
  }

  // ////////////// logical expressions ///////////////////////////
  long evalLogicalOrExpression(AstNode exprAst) {
    int noChildren = exprAst.getNumberOfChildren();
    boolean result = eval(exprAst.getChild(0));
    for(int i = 2; i < noChildren && result != true; i+=2){
      AstNode operand = exprAst.getChild(i);
      result = result || eval(operand);
    }
    
    return result ? 1 : 0;
  }

  long evalLogicalAndExpression(AstNode exprAst) {
    int noChildren = exprAst.getNumberOfChildren();
    boolean result = eval(exprAst.getChild(0));
    for(int i = 2; i < noChildren && result != false; i+=2){
      AstNode operand = exprAst.getChild(i);
      result = result && eval(operand);
    }

    return result ? 1 : 0;
  }

  long evalEqualityExpression(AstNode exprAst) {
    String operator = exprAst.getChild(1).getTokenValue();
    AstNode lhs = exprAst.getChild(0);
    AstNode rhs = exprAst.getChild(2);
    boolean result;
    if (operator.equals("==")) {
      result = evalToInt(lhs) == evalToInt(rhs);
    } else if (operator.equals("!=")) {
      result = evalToInt(lhs) != evalToInt(rhs);
    } else {
      throw new EvaluationException("Unknown equality operator '" + operator + "'");
    }
    
    int noChildren = exprAst.getNumberOfChildren();
    for(int i = 4; i < noChildren; i+=2){
      operator = exprAst.getChild(i-1).getTokenValue();
      rhs = exprAst.getChild(i);
      if (operator.equals("==")) {
        result = result == eval(rhs);
      } else if (operator.equals("!=")) {
        result = result != eval(rhs);
      } else {
        throw new EvaluationException("Unknown equality operator '" + operator + "'");
      }
    }
    
    return result ? 1 : 0;
  }

  long evalRelationalExpression(AstNode exprAst) {
    String operator = exprAst.getChild(1).getTokenValue();
    AstNode lhs = exprAst.getChild(0);
    AstNode rhs = exprAst.getChild(2);
    boolean result;
    if (operator.equals("<")) {
      result = evalToInt(lhs) < evalToInt(rhs);
    } else if (operator.equals(">")) {
      result = evalToInt(lhs) > evalToInt(rhs);
    } else if (operator.equals("<=")) {
      result = evalToInt(lhs) <= evalToInt(rhs);
    } else if (operator.equals(">=")) {
      result = evalToInt(lhs) >= evalToInt(rhs);
    } else {
      throw new EvaluationException("Unknown relational operator '" + operator + "'");
    }

    int resultAsInt;
    int noChildren = exprAst.getNumberOfChildren();
    for(int i = 4; i < noChildren; i+=2){
      operator = exprAst.getChild(i-1).getTokenValue();
      rhs = exprAst.getChild(i);
      
      resultAsInt = result ? 1 : 0;
      if (operator.equals("<")) {
        result = resultAsInt < evalToInt(rhs);
      } else if (operator.equals(">")) {
        result = resultAsInt > evalToInt(rhs);
      } else if (operator.equals("<=")) {
        result = resultAsInt <= evalToInt(rhs);
      } else if (operator.equals(">=")) {
        result = resultAsInt >= evalToInt(rhs);
      } else {
        throw new EvaluationException("Unknown relational operator '" + operator + "'");
      }
    }
    
    return result ? 1 : 0;
  }

  // ///////////////// bitwise expressions ///////////////////////
  long evalAndExpression(AstNode exprAst) {
    int noChildren = exprAst.getNumberOfChildren();
    long result = evalToInt(exprAst.getChild(0));
    for(int i = 2; i < noChildren; i+=2){
      AstNode operand = exprAst.getChild(i);
      result &= evalToInt(operand);
    }
    
    return result;
  }

  long evalInclusiveOrExpression(AstNode exprAst) {
    int noChildren = exprAst.getNumberOfChildren();
    long result = evalToInt(exprAst.getChild(0));
    for(int i = 2; i < noChildren; i+=2){
      AstNode operand = exprAst.getChild(i);
      result |= evalToInt(operand);
    }
    
    return result;
  }

  long evalExclusiveOrExpression(AstNode exprAst) {
    int noChildren = exprAst.getNumberOfChildren();
    long result = evalToInt(exprAst.getChild(0));
    for(int i = 2; i < noChildren; i+=2){
      AstNode operand = exprAst.getChild(i);
      result ^= evalToInt(operand);
    }
    
    return result;
  }

  // ///////////////// other ... ///////////////////
  long evalUnaryExpression(AstNode exprAst) {
    // only 'unary-operator cast-expression' production is allowed in #if-context

    String operator = exprAst.getChild(0).getTokenValue();
    AstNode operand = exprAst.getChild(1);
    if (operator.equals("+")) {
      return evalToInt(operand);
    } else if (operator.equals("-")) {
      return -evalToInt(operand);
    } else if (operator.equals("!")) {
      boolean result = !eval(operand);
      return result ? 1 : 0;
    } else if (operator.equals("~")) {
      return ~evalToInt(operand);
    }
    else {
      throw new EvaluationException("Unknown unary operator  '" + operator + "'");
    }
  }

  long evalShiftExpression(AstNode exprAst) {
    String operator;
    AstNode rhs;
    long result = evalToInt(exprAst.getChild(0));
    int noChildren = exprAst.getNumberOfChildren();
    
    for(int i = 2; i < noChildren; i+=2){
      operator = exprAst.getChild(i-1).getTokenValue();
      rhs = exprAst.getChild(i);
      
      if (operator.equals("<<")) {
        result = result << evalToInt(rhs);
      } else if (operator.equals(">>")) {
        result = result >> evalToInt(rhs);
      } else {
        throw new EvaluationException("Unknown shift operator '" + operator + "'");
      }
    }

    return result;
  }

  long evalAdditiveExpression(AstNode exprAst) {
    String operator;
    AstNode rhs;
    long result = evalToInt(exprAst.getChild(0));
    int noChildren = exprAst.getNumberOfChildren();
    
    for(int i = 2; i < noChildren; i+=2){
      operator = exprAst.getChild(i-1).getTokenValue();
      rhs = exprAst.getChild(i);
      
      if (operator.equals("+")) {
        result += evalToInt(rhs);
      } else if (operator.equals("-")) {
        result -= evalToInt(rhs);
      } else {
        throw new EvaluationException("Unknown additive operator '" + operator + "'");
      }
    }
    
    return result;
  }

  long evalMultiplicativeExpression(AstNode exprAst) {
    String operator;
    AstNode rhs;
    long result = evalToInt(exprAst.getChild(0));
    int noChildren = exprAst.getNumberOfChildren();
    
    for(int i = 2; i < noChildren; i+=2){
      operator = exprAst.getChild(i-1).getTokenValue();
      rhs = exprAst.getChild(i);
      
      if (operator.equals("*")) {
        result *= evalToInt(rhs);
      } else if (operator.equals("/")) {
        result /= evalToInt(rhs);
      } else if (operator.equals("%")) {
        result %= evalToInt(rhs);
      } else {
        throw new EvaluationException("Unknown multiplicative operator '" + operator + "'");
      }
    }
    
    return result;
  }

  long evalConditionalExpression(AstNode exprAst) {
    AstNode decisionOperand = exprAst.getChild(0);
    AstNode trueCaseOperand = exprAst.getChild(2);
    AstNode falseCaseOperand = exprAst.getChild(4);
    return eval(decisionOperand) ? evalToInt(trueCaseOperand) : evalToInt(falseCaseOperand);
  }

  long evalPrimaryExpression(AstNode exprAst) {
    // case "( expression )"
    return evalToInt(exprAst.getChild(1));
  }

  long evalDefinedExpression(AstNode exprAst) {
    int posOfMacroName = exprAst.getNumberOfChildren() == 2 ? 1 : 2;
    String macroName = exprAst.getChild(posOfMacroName).getTokenValue();
    String value = preprocessor.valueOf(macroName);

    LOG.trace("expanding '{}' to '{}'", macroName, value);

    return value == null ? 0 : 1;
  }

  long evalFunctionlikeMacro(AstNode exprAst) {
    String macroName = exprAst.getChild(0).getTokenValue();
    List tokens = exprAst.getTokens();
    List restTokens = tokens.subList(1, tokens.size());
    String value = preprocessor.expandFunctionLikeMacro(macroName, restTokens);

    LOG.trace("expanding '{}' to '{}'", macroName, value);
    if(value == null){
      LOG.warn("Undefined functionlike macro '{}' assuming 0", macroName);
    }
    
    return value == null ? 0 : evalToInt(value);
  }

  String stripSuffix(String number) {
    return number.replaceAll("[LlUu]", "");
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy