Imported Upstream version 1.8.15
[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 "condparser.h"
23 #include "config.h"
24 #include "message.h"
25
26 // declarations
27
28 /**
29  * parses and evaluates the given expression.
30  * @returns 
31  * - On error, an error message is returned.
32  * - On success, the result of the expression is either "1" or "0".
33  */
34 bool CondParser::parse(const char *fileName,int lineNr,const char *expr)
35 {
36   m_expr      = expr;
37   m_tokenType = NOTHING;
38
39   // initialize all variables
40   m_e = m_expr;    // let m_e point to the start of the expression
41
42   bool answer=FALSE;
43   getToken();
44   if (m_tokenType==DELIMITER && m_token.isEmpty())
45   {
46     // empty expression: answer==FALSE
47   }
48   else if (m_err.isEmpty())
49   {
50     answer = parseLevel1();
51
52 #if 0
53     // check for garbage at the end of the expression
54     // an expression ends with a character '\0' and token_type = delimiter
55     if (m_tokenType!=DELIMITER || !m_token.isEmpty())
56     {
57       if (m_tokenType == DELIMITER)
58       {
59         if (m_token=="(" || m_token==")")
60         {
61           m_err=QCString("Unexpected parenthesis ")+m_token+"'";
62         }
63         else
64         {
65           // user entered a not existing operator like "//"
66           m_err=QCString("Unexpected operator ")+m_token+"'";
67         }
68       }
69       else
70       {
71         m_err=QCString("Unexpected part '")+m_token+"'";
72       }
73     }
74 #endif
75   }
76   if (m_err)
77   {
78     warn(fileName,lineNr,"problem evaluating expression '%s': %s",
79         expr,m_err.data());
80   }
81   //printf("expr='%s' answer=%d\n",expr,answer);
82   return answer;
83 }
84
85
86 /**
87  * checks if the given char c is a delimiter
88  * minus is checked apart, can be unary minus
89  */
90 static bool isDelimiter(const char c)
91 {
92   return c=='&' || c=='|' || c=='!';
93 }
94
95 /**
96  * checks if the given char c is a letter or underscore
97  */
98 static bool isAlpha(const char c)
99 {
100   return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_';
101 }
102
103 static bool isAlphaNumSpec(const char c)
104 {
105   return isAlpha(c) || (c>='0' && c<='9') || c=='-' || c=='.' ||
106     (((unsigned char)c)>=0x80 && ((unsigned char)c)<=0xFF);
107 }
108
109 /**
110  * returns the id of the given operator
111  * returns -1 if the operator is not recognized
112  */
113 int CondParser::getOperatorId(const QCString &opName)
114 {
115   // level 2
116   if (opName=="&&") { return AND; }
117   if (opName=="||") { return OR;  }
118
119   // not operator
120   if (opName=="!")  { return NOT; }
121
122   return UNKNOWN_OP;
123 }
124
125 /**
126  * Get next token in the current string expr.
127  * Uses the data in m_expr pointed to by m_e to 
128  * produce m_tokenType and m_token, set m_err in case of an error
129  */
130 void CondParser::getToken()
131 {
132   m_tokenType = NOTHING;
133   m_token.resize(0);     
134
135   //printf("\tgetToken e:{%c}, ascii=%i, col=%i\n", *e, *e, e-expr);
136
137   // skip over whitespaces
138   while (*m_e == ' ' || *m_e == '\t')     // space or tab
139   {
140     m_e++;
141   }
142
143   // check for end of expression
144   if (*m_e=='\0')
145   {
146     // token is still empty
147     m_tokenType = DELIMITER;
148     return;
149   }
150
151   // check for parentheses
152   if (*m_e == '(' || *m_e == ')')
153   {
154     m_tokenType = DELIMITER;
155     m_token += *m_e++;
156     return;
157   }
158
159   // check for operators (delimiters)
160   if (isDelimiter(*m_e))
161   {
162     m_tokenType = DELIMITER;
163     while (isDelimiter(*m_e))
164     {
165       m_token += *m_e++;
166     }
167     return;
168   }
169
170   // check for variables
171   if (isAlpha(*m_e))
172   {
173     m_tokenType = VARIABLE;
174     while (isAlphaNumSpec(*m_e))
175     {
176       m_token += *m_e++;
177     }
178     return;
179   }
180
181   // something unknown is found, wrong characters -> a syntax error
182   m_tokenType = UNKNOWN;
183   while (*m_e)
184   {
185     m_token += *m_e++;
186   }
187   m_err = QCString("Syntax error in part '")+m_token+"'";
188   return;
189 }
190
191
192 /**
193  * conditional operators AND and OR
194  */
195 bool CondParser::parseLevel1()
196 {
197   bool ans = parseLevel2();
198   int opId = getOperatorId(m_token);
199
200   while (opId==AND || opId==OR)
201   {
202     getToken();
203     ans = evalOperator(opId, ans, parseLevel2());
204     opId = getOperatorId(m_token);
205   }
206
207   return ans;
208 }
209
210 /**
211  * NOT
212  */
213 bool CondParser::parseLevel2()
214 {
215   bool ans;
216   int opId = getOperatorId(m_token);
217   if (opId == NOT)
218   {
219     getToken();
220     ans = !parseLevel3();
221   }
222   else
223   {
224     ans = parseLevel3();
225   }
226
227   return ans;
228 }
229
230
231 /**
232  * parenthesized expression or variable
233  */
234 bool CondParser::parseLevel3()
235 {
236   // check if it is a parenthesized expression
237   if (m_tokenType == DELIMITER)
238   {
239     if (m_token=="(")
240     {
241       getToken();
242       int ans = parseLevel1();
243       if (m_tokenType!=DELIMITER || m_token!=")")
244       {
245         m_err="Parenthesis ) missing";
246         return FALSE;
247       }
248       getToken();
249       return ans;
250     }
251   }
252
253   // if not parenthesized then the expression is a variable
254   return parseVar();
255 }
256
257
258 bool CondParser::parseVar()
259 {
260   bool ans = 0;
261   switch (m_tokenType)
262   {
263     case VARIABLE:
264       // this is a variable
265       ans = evalVariable(m_token);
266       getToken();
267       break;
268
269     default:
270       // syntax error or unexpected end of expression
271       if (m_token.isEmpty())
272       {
273         m_err="Unexpected end of expression";
274         return FALSE;
275       }
276       else
277       {
278         m_err="Value expected";
279         return FALSE;
280       }
281       break;
282   }
283   return ans;
284 }
285
286 /**
287  * evaluate an operator for given values
288  */
289 bool CondParser::evalOperator(int opId, bool lhs, bool rhs)
290 {
291   switch (opId)
292   {
293     // level 2
294     case AND: return lhs && rhs;
295     case OR:  return lhs || rhs;
296   }
297
298   m_err = "Internal error unknown operator: id="+QCString().setNum(opId);
299   return FALSE;
300 }
301
302 /**
303  * evaluate a variable
304  */
305 bool CondParser::evalVariable(const char *varName)
306 {
307   if (Config_getList(ENABLED_SECTIONS).find(varName)==-1) return FALSE;
308   return TRUE;
309 }
310