Imported Upstream version 1.9.8
[platform/upstream/doxygen.git] / src / condparser.cpp
1 /**
2  * Copyright (C) 1997-2015 by Dimitri van Heesch.
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation under the terms of the GNU General Public License is hereby
6  * granted. No representations are made about the suitability of this software
7  * for any purpose. It is provided "as is" without express or implied warranty.
8  * See the GNU General Public License for more details.
9  *
10  * Documents produced by Doxygen are derivative works derived from the
11  * input used in their production; they are not affected by this license.
12  *
13  * C++ Expression parser for ENABLED_SECTIONS in Doxygen
14  *
15  * Features used:
16  *     Operators:
17  *         &&    AND operator
18  *         ||    OR  operator
19  *         !     NOT operator
20  */
21
22 #include <algorithm>
23
24 #include "condparser.h"
25 #include "config.h"
26 #include "message.h"
27
28 // declarations
29
30 /**
31  * parses and evaluates the given expression.
32  * @returns
33  * - On error, an error message is returned.
34  * - On success, the result of the expression is either "1" or "0".
35  */
36 bool CondParser::parse(const QCString &fileName,int lineNr,const QCString &expr)
37 {
38   if (expr.isEmpty()) return false;
39   m_expr      = expr;
40   m_tokenType = NOTHING;
41
42   // initialize all variables
43   m_e = m_expr.data();    // let m_e point to the start of the expression
44
45   bool answer=false;
46   getToken();
47   if (m_tokenType==DELIMITER && m_token.isEmpty())
48   {
49     // empty expression: answer==FALSE
50   }
51   else if (m_err.isEmpty())
52   {
53     answer = parseLevel1();
54   }
55   if (!m_err.isEmpty())
56   {
57     warn(fileName,lineNr,"problem evaluating expression '%s': %s",
58         qPrint(expr),qPrint(m_err));
59   }
60   //printf("expr='%s' answer=%d\n",expr,answer);
61   return answer;
62 }
63
64
65 /**
66  * checks if the given char c is a delimiter
67  * minus is checked apart, can be unary minus
68  */
69 static bool isDelimiter(const char c)
70 {
71   return c=='&' || c=='|' || c=='!';
72 }
73
74 /**
75  * checks if the given char c is a letter or underscore
76  */
77 static bool isAlpha(const char c)
78 {
79   return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_';
80 }
81
82 static bool isAlphaNumSpec(const char c)
83 {
84   return isAlpha(c) || (c>='0' && c<='9') || c=='-' || c=='.' || (static_cast<unsigned char>(c)>=0x80);
85 }
86
87 /**
88  * returns the id of the given operator
89  * returns -1 if the operator is not recognized
90  */
91 int CondParser::getOperatorId(const QCString &opName)
92 {
93   // level 2
94   if (opName=="&&") { return AND; }
95   if (opName=="||") { return OR;  }
96
97   // not operator
98   if (opName=="!")  { return NOT; }
99
100   return UNKNOWN_OP;
101 }
102
103 /**
104  * Get next token in the current string expr.
105  * Uses the data in m_expr pointed to by m_e to
106  * produce m_tokenType and m_token, set m_err in case of an error
107  */
108 void CondParser::getToken()
109 {
110   m_tokenType = NOTHING;
111   m_token.resize(0);
112
113   //printf("\tgetToken e:{%c}, ascii=%i, col=%i\n", *e, *e, e-expr);
114
115   // skip over whitespaces
116   while (*m_e == ' ' || *m_e == '\t' || *m_e == '\n')     // space or tab or newline
117   {
118     m_e++;
119   }
120
121   // check for end of expression
122   if (*m_e=='\0')
123   {
124     // token is still empty
125     m_tokenType = DELIMITER;
126     return;
127   }
128
129   // check for parentheses
130   if (*m_e == '(' || *m_e == ')')
131   {
132     m_tokenType = DELIMITER;
133     m_token += *m_e++;
134     return;
135   }
136
137   // check for operators (delimiters)
138   if (isDelimiter(*m_e))
139   {
140     m_tokenType = DELIMITER;
141     while (isDelimiter(*m_e))
142     {
143       m_token += *m_e++;
144     }
145     return;
146   }
147
148   // check for variables
149   if (isAlpha(*m_e))
150   {
151     m_tokenType = VARIABLE;
152     while (isAlphaNumSpec(*m_e))
153     {
154       m_token += *m_e++;
155     }
156     return;
157   }
158
159   // something unknown is found, wrong characters -> a syntax error
160   m_tokenType = UNKNOWN;
161   while (*m_e)
162   {
163     m_token += *m_e++;
164   }
165   m_err = QCString("Syntax error in part '")+m_token+"'";
166   return;
167 }
168
169
170 /**
171  * conditional operators AND and OR
172  */
173 bool CondParser::parseLevel1()
174 {
175   bool ans = parseLevel2();
176   int opId = getOperatorId(m_token);
177
178   while (opId==AND || opId==OR)
179   {
180     getToken();
181     ans = evalOperator(opId, ans, parseLevel2());
182     opId = getOperatorId(m_token);
183   }
184
185   return ans;
186 }
187
188 /**
189  * NOT
190  */
191 bool CondParser::parseLevel2()
192 {
193   bool ans;
194   int opId = getOperatorId(m_token);
195   if (opId == NOT)
196   {
197     getToken();
198     ans = !parseLevel3();
199   }
200   else
201   {
202     ans = parseLevel3();
203   }
204
205   return ans;
206 }
207
208
209 /**
210  * parenthesized expression or variable
211  */
212 bool CondParser::parseLevel3()
213 {
214   // check if it is a parenthesized expression
215   if (m_tokenType == DELIMITER)
216   {
217     if (m_token=="(")
218     {
219       getToken();
220       bool ans = parseLevel1();
221       if (m_tokenType!=DELIMITER || m_token!=")")
222       {
223         m_err="Parenthesis ) missing";
224         return FALSE;
225       }
226       getToken();
227       return ans;
228     }
229   }
230
231   // if not parenthesized then the expression is a variable
232   return parseVar();
233 }
234
235
236 bool CondParser::parseVar()
237 {
238   bool ans = false;
239   switch (m_tokenType)
240   {
241     case VARIABLE:
242       // this is a variable
243       ans = evalVariable(m_token);
244       getToken();
245       break;
246
247     default:
248       // syntax error or unexpected end of expression
249       if (m_token.isEmpty())
250       {
251         m_err="Unexpected end of expression";
252         return FALSE;
253       }
254       else
255       {
256         m_err="Value expected";
257         return FALSE;
258       }
259       break;
260   }
261   return ans;
262 }
263
264 /**
265  * evaluate an operator for given values
266  */
267 bool CondParser::evalOperator(int opId, bool lhs, bool rhs)
268 {
269   switch (opId)
270   {
271     // level 2
272     case AND: return lhs && rhs;
273     case OR:  return lhs || rhs;
274   }
275
276   m_err = "Internal error unknown operator: id="+QCString().setNum(opId);
277   return FALSE;
278 }
279
280 /**
281  * evaluate a variable
282  */
283 bool CondParser::evalVariable(const QCString &varName)
284 {
285   const StringVector &list = Config_getList(ENABLED_SECTIONS);
286   return std::find(list.begin(),list.end(),varName.str())!=list.end();
287 }
288