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