diff options
Diffstat (limited to 'src/condparser.cpp')
-rw-r--r-- | src/condparser.cpp | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/condparser.cpp b/src/condparser.cpp new file mode 100644 index 0000000..4920ade --- /dev/null +++ b/src/condparser.cpp @@ -0,0 +1,306 @@ +/** + * Copyright (C) 1997-2012 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.data(); // let m_e point to the start of the expression + + getToken(); + if (m_tokenType==DELIMITER && m_token.isEmpty()) + { + return "Empty expression"; + } + bool answer=FALSE; + if (m_err.isEmpty()) + { + answer = parseLevel1(); + + // check for garbage at the end of the expression + // an expression ends with a character '\0' and token_type = delimeter + 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+"'"; + } + } + } + if (m_err) + { + warn(fileName,lineNr,"Warning: problem evaluating expression '%s': %s", + expr,m_err.data()); + } + return answer; +} + + +/** + * checks if the given char c is a delimeter + * 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 (delimeters) + 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; +} + |