1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "binaryexpressionparser.h"
26 #include "common.h"
27
28 typedef enum
29 {
30 BEPERR_NONE, BEPERR_BRACE, BEPERR_NOEND, BEPERR_NOTFOUND
31 } binaryExpressionParserError_t;
32
33 /**
34 * @brief Evaluates stuff like this expression <pre>(x & !y) ^ z</pre>
35 */
36 class BinaryExpressionParser {
37 private:
38 binaryExpressionParserError_t binaryExpressionParserError;
39 BEPEvaluteCallback_t varFunc;
40 char varName[MAX_VAR];
41 bool result;
42 const void* userdata;
43
SkipWhiteSpaces(const char ** s) const44 inline void SkipWhiteSpaces (const char** s) const
45 {
46 while (**s == ' ' || **s == '\t')
47 (*s)++;
48 }
49
50 /**
51 * @brief Advance to the next char that is no whitespace
52 */
NextChar(const char ** s) const53 inline void NextChar (const char** s) const
54 {
55 (*s)++;
56 /* skip white-spaces too */
57 SkipWhiteSpaces(s);
58 }
59
GetSwitchName(const char ** s)60 const char* GetSwitchName (const char** s)
61 {
62 int pos = 0;
63
64 /* skip non printable chars and special chars that are used to define the binary expression */
65 while (**s > ' ' && **s != '^' && **s != '|' && **s != '&' && **s != '!' && **s != '(' && **s != ')') {
66 varName[pos++] = **s;
67 (*s)++;
68 }
69 varName[pos] = '\0';
70
71 return varName;
72 }
73
74 /**
75 * @brief Evaluates or and xor in the given string. This is the entry point, it delegates to the and checks
76 * that are done in @c CheckAnd
77 * @return The result of the evaluation
78 */
CheckOR(const char ** s)79 bool CheckOR (const char** s)
80 {
81 bool result = false;
82 enum {
83 BEP_NONE, BEP_OR, BEP_EXCLUSIVE_OR
84 };
85 int goOn = BEP_NONE;
86
87 SkipWhiteSpaces(s);
88 do {
89 if (goOn == BEP_EXCLUSIVE_OR)
90 result ^= CheckAND(s);
91 else
92 result |= CheckAND(s);
93
94 if (**s == '|') {
95 goOn = BEP_OR;
96 NextChar(s);
97 } else if (**s == '^') {
98 goOn = BEP_EXCLUSIVE_OR;
99 NextChar(s);
100 } else {
101 goOn = BEP_NONE;
102 }
103 } while (goOn != BEP_NONE && !binaryExpressionParserError);
104
105 return result;
106 }
107
CheckAND(const char ** s)108 bool CheckAND (const char** s)
109 {
110 bool result = true;
111 bool negate = false;
112 bool goOn = false;
113
114 do {
115 /* parse all negate chars and swap the flag accordingly */
116 while (**s == '!') {
117 negate ^= true;
118 NextChar(s);
119 }
120 /* handling braces */
121 if (**s == '(') {
122 NextChar(s);
123 /* and the result of the inner clause by calling the entry
124 * point again (and apply the negate flag) */
125 result &= CheckOR(s) ^ negate;
126 if (**s != ')')
127 binaryExpressionParserError = BEPERR_BRACE;
128 NextChar(s);
129 } else {
130 /* get the variable state by calling the evaluate callback */
131 const int value = varFunc(GetSwitchName(s), userdata);
132 if (value == -1)
133 binaryExpressionParserError = BEPERR_NOTFOUND;
134 else
135 result &= value ^ negate;
136 SkipWhiteSpaces(s);
137 }
138
139 /* check whether there is another and clause */
140 if (**s == '&') {
141 goOn = true;
142 NextChar(s);
143 } else {
144 goOn = false;
145 }
146 negate = false;
147 } while (goOn && !binaryExpressionParserError);
148
149 return result;
150 }
151
152 public:
BinaryExpressionParser(const char * expr,BEPEvaluteCallback_t varFuncParam,const void * userdataPtr)153 BinaryExpressionParser (const char* expr, BEPEvaluteCallback_t varFuncParam, const void* userdataPtr) :
154 binaryExpressionParserError(BEPERR_NONE), varFunc(varFuncParam), userdata(userdataPtr)
155 {
156 const char* str = expr;
157 result = CheckOR(&str);
158 /* check for no end error */
159 if (Q_strvalid(str) && !binaryExpressionParserError)
160 binaryExpressionParserError = BEPERR_NOEND;
161 }
162
getResult() const163 inline bool getResult() const
164 {
165 return result;
166 }
167
getError() const168 inline binaryExpressionParserError_t getError() const
169 {
170 return binaryExpressionParserError;
171 }
172 };
173
BEP_Evaluate(const char * expr,BEPEvaluteCallback_t varFuncParam,const void * userdata)174 bool BEP_Evaluate (const char* expr, BEPEvaluteCallback_t varFuncParam, const void* userdata)
175 {
176 if (!Q_strvalid(expr))
177 return true;
178
179 BinaryExpressionParser bep(expr, varFuncParam, userdata);
180 const bool result = bep.getResult();
181 const binaryExpressionParserError_t error = bep.getError();
182
183 switch (error) {
184 case BEPERR_NONE:
185 /* do nothing */
186 return result;
187 case BEPERR_BRACE:
188 Com_Printf("')' expected in binary expression (%s).\n", expr);
189 return true;
190 case BEPERR_NOEND:
191 Com_Printf("Unexpected end of condition in binary expression (%s).\n", expr);
192 return result;
193 case BEPERR_NOTFOUND:
194 Com_Printf("Variable not found in binary expression (%s).\n", expr);
195 return false;
196 }
197 Com_Error(ERR_FATAL, "Unknown CheckBEP error in binary expression (%s)", expr);
198 }
199