Doxygen
Loading...
Searching...
No Matches
condparser.cpp
Go to the documentation of this file.
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// own header
23#include "condparser.h"
24
25// standard includes
26#include <algorithm>
27
28// other includes
29#include "config.h"
30#include "configimpl.h"
31#include "configoptions.h"
32#include "message.h"
33
34// declarations
35static DString error_str = "doxyconfig_error";
36static DString resolveConfig(const DString &fileName,int lineNr, const DString &expr);
37static DString getConfig(const DString &fileName,int lineNr, const DString &expr);
38
39/**
40 * parses and evaluates the given expression.
41 * @returns
42 * - On error, an error message is returned.
43 * - On success, the result of the expression is either "1" or "0".
44 */
45bool CondParser::parse(const DString &fileName,int lineNr,const DString &expr)
46{
47 if (expr.empty()) return false;
49 m_expr = resolveConfig(fileName, lineNr, expr);
50
51 // initialize all variables
52 m_e = m_expr.data(); // let m_e point to the start of the expression
53
54 bool answer=false;
55 getToken();
57 {
58 // empty expression: answer==false
59 }
60 else if (m_err.empty())
61 {
62 answer = parseLevel1();
63 }
64 if (!m_err.empty())
65 {
66 warn(fileName,lineNr,"problem evaluating expression '{}': {}", expr, m_err);
67 }
68 //printf("expr='%s' answer=%d\n",expr,answer);
69 return answer;
70}
71
72
73/**
74 * checks if the given char c is a delimiter
75 * minus is checked apart, can be unary minus
76 */
77static bool isDelimiter(const char c)
78{
79 return c=='&' || c=='|' || c=='!';
80}
81
82/**
83 * checks if the given char c is a letter or underscore
84 */
85static bool isAlpha(const char c)
86{
87 return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_';
88}
89
90static bool isAlphaNumSpec(const char c)
91{
92 return isAlpha(c) || (c>='0' && c<='9') || c=='-' || c=='.' || (static_cast<unsigned char>(c)>=0x80);
93}
94
95/**
96 * returns the id of the given operator
97 * returns -1 if the operator is not recognized
98 */
100{
101 // level 2
102 if (opName=="&&") { return AND; }
103 if (opName=="||") { return OR; }
104
105 // not operator
106 if (opName=="!") { return NOT; }
107
108 return UNKNOWN_OP;
109}
110
111/**
112 * Get next token in the current string expr.
113 * Uses the data in m_expr pointed to by m_e to
114 * produce m_tokenType and m_token, set m_err in case of an error
115 */
117{
119 m_token.clear();
120
121 //printf("\tgetToken e:{%c}, ascii=%i, col=%i\n", *e, *e, e-expr);
122
123 // skip over whitespaces
124 while (*m_e == ' ' || *m_e == '\t' || *m_e == '\n') // space or tab or newline
125 {
126 m_e++;
127 }
128
129 // check for end of expression
130 if (*m_e=='\0')
131 {
132 // token is still empty
134 return;
135 }
136
137 // check for parentheses
138 if (*m_e == '(' || *m_e == ')')
139 {
141 m_token += *m_e++;
142 return;
143 }
144
145 // check for operators (delimiters)
146 if (isDelimiter(*m_e))
147 {
149 while (isDelimiter(*m_e))
150 {
151 m_token += *m_e++;
152 }
153 return;
154 }
155
156 // check for variables
157 if (isAlpha(*m_e))
158 {
160 while (isAlphaNumSpec(*m_e))
161 {
162 m_token += *m_e++;
163 }
164 return;
165 }
166
167 // something unknown is found, wrong characters -> a syntax error
169 while (*m_e)
170 {
171 m_token += *m_e++;
172 }
173 m_err = DString("Syntax error in part '")+m_token+"'";
174 return;
175}
176
177
178/**
179 * conditional operators AND and OR
180 */
182{
183 bool ans = parseLevel2();
184 int opId = getOperatorId(m_token);
185
186 while (opId==AND || opId==OR)
187 {
188 getToken();
189 ans = evalOperator(opId, ans, parseLevel2());
190 opId = getOperatorId(m_token);
191 }
192
193 return ans;
194}
195
196/**
197 * NOT
198 */
200{
201 int opId = getOperatorId(m_token);
202 if (opId == NOT)
203 {
204 getToken();
205 return !parseLevel3();
206 }
207 else
208 {
209 return parseLevel3();
210 }
211}
212
213
214/**
215 * parenthesized expression or variable
216 */
218{
219 // check if it is a parenthesized expression
220 if (m_tokenType == DELIMITER)
221 {
222 if (m_token=="(")
223 {
224 getToken();
225 bool ans = parseLevel1();
226 if (m_tokenType!=DELIMITER || m_token!=")")
227 {
228 m_err="Parenthesis ) missing";
229 return false;
230 }
231 getToken();
232 return ans;
233 }
234 }
235
236 // if not parenthesized then the expression is a variable
237 return parseVar();
238}
239
240
242{
243 bool ans = false;
244 switch (m_tokenType)
245 {
246 case VARIABLE:
247 // this is a variable
248 ans = evalVariable(m_token);
249 getToken();
250 break;
251
252 default:
253 // syntax error or unexpected end of expression
254 if (m_token.empty())
255 {
256 m_err="Unexpected end of expression";
257 return false;
258 }
259 else
260 {
261 m_err="Value expected";
262 return false;
263 }
264 break;
265 }
266 return ans;
267}
268
269/**
270 * evaluate an operator for given values
271 */
272bool CondParser::evalOperator(int opId, bool lhs, bool rhs)
273{
274 switch (opId)
275 {
276 // level 2
277 case AND: return lhs && rhs;
278 case OR: return lhs || rhs;
279 }
280
281 m_err = "Internal error unknown operator: id="+DString().setNum(opId);
282 return false;
283}
284
285/**
286 * evaluate a variable
287 */
289{
290 if (varName == "YES") return true;
291 if (varName == "NO") return false;
292 StringVector list = Config_getList(ENABLED_SECTIONS);
293 return std::find(list.begin(),list.end(),varName.str())!=list.end();
294}
295
296static DString getConfig(const DString &fileName,int lineNr, const DString &expr)
297{
298 if (expr.empty())
299 {
300 return error_str;
301 }
302 ConfigOption * opt = ConfigImpl::instance()->get(expr);
303 if (opt)
304 {
305 switch (opt->kind())
306 {
308 return((static_cast<ConfigBool*>(opt)->valueRef())? "YES" : "NO");
310 // due to the fact that there can be any character in the string
311 warn(fileName,lineNr,
312 "String setting '{}' not possible in conditional statement, ignored", expr);
313 return error_str;
315 return(*(static_cast<ConfigEnum*>(opt)->valueRef()));
317 return (DString().setNum(*(static_cast<ConfigInt*>(opt)->valueRef())));
319 warn(fileName,lineNr,
320 "List setting '{}' not possible in conditional statement, ignored", expr);
321 return error_str;
323 warn(fileName,lineNr,
324 "Obsolete setting '{}' not possible in conditional statement, ignored", expr);
325 return error_str;
327 warn(fileName,lineNr,
328 "Disabled setting '{}' not possible in conditional statement, ignored", expr);
329 return error_str;
331 warn(fileName,lineNr,
332 "Info setting '{}' not possible in conditional statement, ignored", expr);
333 return error_str;
334 default:
335 warn(fileName,lineNr,
336 "Unknown error occurrence '{}', ignored", expr);
337 return error_str;
338 }
339 }
340 else
341 {
342 warn(fileName,lineNr,
343 "Unknown setting '{}' not possible in conditional statement, ignored", expr);
344 return error_str;
345 }
346}
347
348static DString resolveConfig(const DString &fileName,int lineNr, const DString &expr)
349{
350 if (expr.empty())
351 {
352 return "";
353 }
354
355 DString loc_expr;
356 signed char c = 0;
357 const char *p=expr.data();
358 while ((c=*p++)!=0)
359 {
360 switch(c)
361 {
362 case '\\':
363 case '@':
364 if (*p == 'd' && DString(p).startsWith("doxyconfig"))
365 {
366 p+=10; // skip doxyconfig
367 while (*p==' ' || *p=='\t') {p++;}
368 DString bufConfig;
369 while ((c=*p++)!=0)
370 {
371 if ((c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_') bufConfig += c;
372 else
373 {
374 break;
375 }
376 }
377 p--;
378 loc_expr += getConfig(fileName, lineNr, bufConfig);
379 }
380 break;
381 default:
382 loc_expr += c;
383 break;
384 }
385 }
386 return loc_expr;
387}
DString m_token
holds the token
Definition condparser.h:59
DString m_err
error state
Definition condparser.h:55
bool parseLevel2()
NOT.
bool parse(const DString &fileName, int lineNr, const DString &expr)
parses and evaluates the given expression.
TOKENTYPE m_tokenType
type of the token
Definition condparser.h:60
DString m_expr
holds the expression
Definition condparser.h:56
int getOperatorId(const DString &opName)
returns the id of the given operator returns -1 if the operator is not recognized
bool parseLevel1()
conditional operators AND and OR
void getToken()
Get next token in the current string expr.
bool evalVariable(const DString &varName)
evaluate a variable
bool evalOperator(const int opId, bool lhs, bool rhs)
evaluate an operator for given values
const char * m_e
points to a character in expr
Definition condparser.h:57
bool parseVar()
bool parseLevel3()
parenthesized expression or variable
Class representing a Boolean type option.
Definition configimpl.h:251
Class representing an enum type option.
Definition configimpl.h:153
static ConfigImpl * instance()
Definition configimpl.h:347
ConfigOption * get(const DString &name) const
Definition configimpl.h:396
Class representing an integer type option.
Definition configimpl.h:216
Abstract base class for any configuration option.
Definition configimpl.h:35
@ O_Disabled
Disabled compile time option.
Definition configimpl.h:51
@ O_List
A list of items.
Definition configimpl.h:45
@ O_Enum
A fixed set of items.
Definition configimpl.h:46
@ O_Bool
A boolean value.
Definition configimpl.h:49
@ O_String
A single item.
Definition configimpl.h:47
@ O_Obsolete
An obsolete option.
Definition configimpl.h:50
@ O_Int
An integer value.
Definition configimpl.h:48
@ O_Info
A section header.
Definition configimpl.h:44
OptionType kind() const
Definition configimpl.h:66
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
const std::string & str() const
Definition dstring.h:645
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
static bool isDelimiter(const char c)
checks if the given char c is a delimiter minus is checked apart, can be unary minus
static bool isAlphaNumSpec(const char c)
static DString resolveConfig(const DString &fileName, int lineNr, const DString &expr)
static DString getConfig(const DString &fileName, int lineNr, const DString &expr)
static DString error_str
Copyright (C) 1997-2015 by Dimitri van Heesch.
static bool isAlpha(const char c)
checks if the given char c is a letter or underscore
#define Config_getList(name)
Definition config.h:38
std::vector< std::string > StringVector
Definition containers.h:33
#define warn(file, line, fmt,...)
Definition message.h:97