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