|
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 |
|