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