Blame src/condparser.cpp

Packit 1c1d7e
/**
Packit 1c1d7e
 * Copyright (C) 1997-2015 by Dimitri van Heesch.
Packit 1c1d7e
 *
Packit 1c1d7e
 * Permission to use, copy, modify, and distribute this software and its
Packit 1c1d7e
 * documentation under the terms of the GNU General Public License is hereby 
Packit 1c1d7e
 * granted. No representations are made about the suitability of this software 
Packit 1c1d7e
 * for any purpose. It is provided "as is" without express or implied warranty.
Packit 1c1d7e
 * See the GNU General Public License for more details.
Packit 1c1d7e
 *
Packit 1c1d7e
 * Documents produced by Doxygen are derivative works derived from the
Packit 1c1d7e
 * input used in their production; they are not affected by this license.
Packit 1c1d7e
 *
Packit 1c1d7e
 * C++ Expression parser for ENABLED_SECTIONS in Doxygen
Packit 1c1d7e
 *
Packit 1c1d7e
 * Features used:
Packit 1c1d7e
 *     Operators:
Packit 1c1d7e
 *         &&    AND operator
Packit 1c1d7e
 *         ||    OR  operator
Packit 1c1d7e
 *         !     NOT operator
Packit 1c1d7e
 */
Packit 1c1d7e
Packit 1c1d7e
#include "condparser.h"
Packit 1c1d7e
#include "config.h"
Packit 1c1d7e
#include "message.h"
Packit 1c1d7e
Packit 1c1d7e
// declarations
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * parses and evaluates the given expression.
Packit 1c1d7e
 * @returns 
Packit 1c1d7e
 * - On error, an error message is returned.
Packit 1c1d7e
 * - On success, the result of the expression is either "1" or "0".
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::parse(const char *fileName,int lineNr,const char *expr)
Packit 1c1d7e
{
Packit 1c1d7e
  m_expr      = expr;
Packit 1c1d7e
  m_tokenType = NOTHING;
Packit 1c1d7e
Packit 1c1d7e
  // initialize all variables
Packit 1c1d7e
  m_e = m_expr;    // let m_e point to the start of the expression
Packit 1c1d7e
Packit 1c1d7e
  bool answer=FALSE;
Packit 1c1d7e
  getToken();
Packit 1c1d7e
  if (m_tokenType==DELIMITER && m_token.isEmpty())
Packit 1c1d7e
  {
Packit 1c1d7e
    // empty expression: answer==FALSE
Packit 1c1d7e
  }
Packit 1c1d7e
  else if (m_err.isEmpty())
Packit 1c1d7e
  {
Packit 1c1d7e
    answer = parseLevel1();
Packit 1c1d7e
Packit 1c1d7e
#if 0
Packit 1c1d7e
    // check for garbage at the end of the expression
Packit 1c1d7e
    // an expression ends with a character '\0' and token_type = delimiter
Packit 1c1d7e
    if (m_tokenType!=DELIMITER || !m_token.isEmpty())
Packit 1c1d7e
    {
Packit 1c1d7e
      if (m_tokenType == DELIMITER)
Packit 1c1d7e
      {
Packit 1c1d7e
        if (m_token=="(" || m_token==")")
Packit 1c1d7e
        {
Packit 1c1d7e
          m_err=QCString("Unexpected parenthesis ")+m_token+"'";
Packit 1c1d7e
        }
Packit 1c1d7e
        else
Packit 1c1d7e
        {
Packit 1c1d7e
          // user entered a not existing operator like "//"
Packit 1c1d7e
          m_err=QCString("Unexpected operator ")+m_token+"'";
Packit 1c1d7e
        }
Packit 1c1d7e
      }
Packit 1c1d7e
      else
Packit 1c1d7e
      {
Packit 1c1d7e
        m_err=QCString("Unexpected part '")+m_token+"'";
Packit 1c1d7e
      }
Packit 1c1d7e
    }
Packit 1c1d7e
#endif
Packit 1c1d7e
  }
Packit 1c1d7e
  if (m_err)
Packit 1c1d7e
  {
Packit 1c1d7e
    warn(fileName,lineNr,"problem evaluating expression '%s': %s",
Packit 1c1d7e
        expr,m_err.data());
Packit 1c1d7e
  }
Packit 1c1d7e
  //printf("expr='%s' answer=%d\n",expr,answer);
Packit 1c1d7e
  return answer;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * checks if the given char c is a delimiter
Packit 1c1d7e
 * minus is checked apart, can be unary minus
Packit 1c1d7e
 */
Packit 1c1d7e
static bool isDelimiter(const char c)
Packit 1c1d7e
{
Packit 1c1d7e
  return c=='&' || c=='|' || c=='!';
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * checks if the given char c is a letter or underscore
Packit 1c1d7e
 */
Packit 1c1d7e
static bool isAlpha(const char c)
Packit 1c1d7e
{
Packit 1c1d7e
  return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_';
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
static bool isAlphaNum(const char c)
Packit 1c1d7e
{
Packit 1c1d7e
  return isAlpha(c) || (c>='0' && c<='9');
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * returns the id of the given operator
Packit 1c1d7e
 * returns -1 if the operator is not recognized
Packit 1c1d7e
 */
Packit 1c1d7e
int CondParser::getOperatorId(const QCString &opName)
Packit 1c1d7e
{
Packit 1c1d7e
  // level 2
Packit 1c1d7e
  if (opName=="&&") { return AND; }
Packit 1c1d7e
  if (opName=="||") { return OR;  }
Packit 1c1d7e
Packit 1c1d7e
  // not operator
Packit 1c1d7e
  if (opName=="!")  { return NOT; }
Packit 1c1d7e
Packit 1c1d7e
  return UNKNOWN_OP;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * Get next token in the current string expr.
Packit 1c1d7e
 * Uses the data in m_expr pointed to by m_e to 
Packit 1c1d7e
 * produce m_tokenType and m_token, set m_err in case of an error
Packit 1c1d7e
 */
Packit 1c1d7e
void CondParser::getToken()
Packit 1c1d7e
{
Packit 1c1d7e
  m_tokenType = NOTHING;
Packit 1c1d7e
  m_token.resize(0);     
Packit 1c1d7e
Packit 1c1d7e
  //printf("\tgetToken e:{%c}, ascii=%i, col=%i\n", *e, *e, e-expr);
Packit 1c1d7e
Packit 1c1d7e
  // skip over whitespaces
Packit 1c1d7e
  while (*m_e == ' ' || *m_e == '\t')     // space or tab
Packit 1c1d7e
  {
Packit 1c1d7e
    m_e++;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // check for end of expression
Packit 1c1d7e
  if (*m_e=='\0')
Packit 1c1d7e
  {
Packit 1c1d7e
    // token is still empty
Packit 1c1d7e
    m_tokenType = DELIMITER;
Packit 1c1d7e
    return;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // check for parentheses
Packit 1c1d7e
  if (*m_e == '(' || *m_e == ')')
Packit 1c1d7e
  {
Packit 1c1d7e
    m_tokenType = DELIMITER;
Packit 1c1d7e
    m_token += *m_e++;
Packit 1c1d7e
    return;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // check for operators (delimiters)
Packit 1c1d7e
  if (isDelimiter(*m_e))
Packit 1c1d7e
  {
Packit 1c1d7e
    m_tokenType = DELIMITER;
Packit 1c1d7e
    while (isDelimiter(*m_e))
Packit 1c1d7e
    {
Packit 1c1d7e
      m_token += *m_e++;
Packit 1c1d7e
    }
Packit 1c1d7e
    return;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // check for variables
Packit 1c1d7e
  if (isAlpha(*m_e))
Packit 1c1d7e
  {
Packit 1c1d7e
    m_tokenType = VARIABLE;
Packit 1c1d7e
    while (isAlphaNum(*m_e))
Packit 1c1d7e
    {
Packit 1c1d7e
      m_token += *m_e++;
Packit 1c1d7e
    }
Packit 1c1d7e
    return;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // something unknown is found, wrong characters -> a syntax error
Packit 1c1d7e
  m_tokenType = UNKNOWN;
Packit 1c1d7e
  while (*m_e)
Packit 1c1d7e
  {
Packit 1c1d7e
    m_token += *m_e++;
Packit 1c1d7e
  }
Packit 1c1d7e
  m_err = QCString("Syntax error in part '")+m_token+"'";
Packit 1c1d7e
  return;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * conditional operators AND and OR
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::parseLevel1()
Packit 1c1d7e
{
Packit 1c1d7e
  bool ans = parseLevel2();
Packit 1c1d7e
  int opId = getOperatorId(m_token);
Packit 1c1d7e
Packit 1c1d7e
  while (opId==AND || opId==OR)
Packit 1c1d7e
  {
Packit 1c1d7e
    getToken();
Packit 1c1d7e
    ans = evalOperator(opId, ans, parseLevel2());
Packit 1c1d7e
    opId = getOperatorId(m_token);
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  return ans;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * NOT
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::parseLevel2()
Packit 1c1d7e
{
Packit 1c1d7e
  bool ans;
Packit 1c1d7e
  int opId = getOperatorId(m_token);
Packit 1c1d7e
  if (opId == NOT)
Packit 1c1d7e
  {
Packit 1c1d7e
    getToken();
Packit 1c1d7e
    ans = !parseLevel3();
Packit 1c1d7e
  }
Packit 1c1d7e
  else
Packit 1c1d7e
  {
Packit 1c1d7e
    ans = parseLevel3();
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  return ans;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * parenthesized expression or variable
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::parseLevel3()
Packit 1c1d7e
{
Packit 1c1d7e
  // check if it is a parenthesized expression
Packit 1c1d7e
  if (m_tokenType == DELIMITER)
Packit 1c1d7e
  {
Packit 1c1d7e
    if (m_token=="(")
Packit 1c1d7e
    {
Packit 1c1d7e
      getToken();
Packit 1c1d7e
      int ans = parseLevel1();
Packit 1c1d7e
      if (m_tokenType!=DELIMITER || m_token!=")")
Packit 1c1d7e
      {
Packit 1c1d7e
        m_err="Parenthesis ) missing";
Packit 1c1d7e
        return FALSE;
Packit 1c1d7e
      }
Packit 1c1d7e
      getToken();
Packit 1c1d7e
      return ans;
Packit 1c1d7e
    }
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  // if not parenthesized then the expression is a variable
Packit 1c1d7e
  return parseVar();
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
Packit 1c1d7e
bool CondParser::parseVar()
Packit 1c1d7e
{
Packit 1c1d7e
  bool ans = 0;
Packit 1c1d7e
  switch (m_tokenType)
Packit 1c1d7e
  {
Packit 1c1d7e
    case VARIABLE:
Packit 1c1d7e
      // this is a variable
Packit 1c1d7e
      ans = evalVariable(m_token);
Packit 1c1d7e
      getToken();
Packit 1c1d7e
      break;
Packit 1c1d7e
Packit 1c1d7e
    default:
Packit 1c1d7e
      // syntax error or unexpected end of expression
Packit 1c1d7e
      if (m_token.isEmpty())
Packit 1c1d7e
      {
Packit 1c1d7e
        m_err="Unexpected end of expression";
Packit 1c1d7e
        return FALSE;
Packit 1c1d7e
      }
Packit 1c1d7e
      else
Packit 1c1d7e
      {
Packit 1c1d7e
        m_err="Value expected";
Packit 1c1d7e
        return FALSE;
Packit 1c1d7e
      }
Packit 1c1d7e
      break;
Packit 1c1d7e
  }
Packit 1c1d7e
  return ans;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * evaluate an operator for given valuess
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::evalOperator(int opId, bool lhs, bool rhs)
Packit 1c1d7e
{
Packit 1c1d7e
  switch (opId)
Packit 1c1d7e
  {
Packit 1c1d7e
    // level 2
Packit 1c1d7e
    case AND: return lhs && rhs;
Packit 1c1d7e
    case OR:  return lhs || rhs;
Packit 1c1d7e
  }
Packit 1c1d7e
Packit 1c1d7e
  m_err = "Internal error unknown operator: id="+QCString().setNum(opId);
Packit 1c1d7e
  return FALSE;
Packit 1c1d7e
}
Packit 1c1d7e
Packit 1c1d7e
/**
Packit 1c1d7e
 * evaluate a variable
Packit 1c1d7e
 */
Packit 1c1d7e
bool CondParser::evalVariable(const char *varName)
Packit 1c1d7e
{
Packit 1c1d7e
  if (Config_getList(ENABLED_SECTIONS).find(varName)==-1) return FALSE;
Packit 1c1d7e
  return TRUE;
Packit 1c1d7e
}
Packit 1c1d7e