1 /***************************************************************************
2 * Copyright 2014 Marcelo Y. Matuda *
3 * *
4 * This program is free software: you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation, either version 3 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
18 #include "Equation.h"
19
20 #include <cctype> /* isspace */
21
22 #include "Exception.h"
23 #include "Log.h"
24 #include "Text.h"
25
26
27
28 namespace {
29
30 using namespace GS::TRMControlModel;
31
32 const char addChar = '+';
33 const char subChar = '-';
34 const char multChar = '*';
35 const char divChar = '/';
36 const char rightParenChar = ')';
37 const char leftParenChar = '(';
38
39 FormulaSymbol formulaSymbol;
40
41 class FormulaNodeParser {
42 public:
FormulaNodeParser(const std::string & s)43 FormulaNodeParser(const std::string& s)
44 : formulaSymbolMap_(formulaSymbol.codeMap)
45 , s_(GS::Text::trim(s))
46 , pos_(0)
47 , symbolType_(SYMBOL_TYPE_INVALID) {
48 if (s_.empty()) {
49 THROW_EXCEPTION(GS::TRMControlModelException, "Formula expression parser error: Empty string.");
50 }
51 nextSymbol();
52 }
53
54 FormulaNode_ptr parse();
55 private:
56 enum SymbolType {
57 SYMBOL_TYPE_INVALID,
58 SYMBOL_TYPE_ADD,
59 SYMBOL_TYPE_SUB,
60 SYMBOL_TYPE_MULT,
61 SYMBOL_TYPE_DIV,
62 SYMBOL_TYPE_RIGHT_PAREN,
63 SYMBOL_TYPE_LEFT_PAREN,
64 SYMBOL_TYPE_STRING
65 };
66
67 void throwException(const char* errorDescription) const;
68 template<typename T> void throwException(const char* errorDescription, const T& complement) const;
finished() const69 bool finished() const {
70 return pos_ >= s_.size();
71 }
72 void nextSymbol();
73 FormulaNode_ptr parseFactor();
74 FormulaNode_ptr parseTerm();
75 FormulaNode_ptr parseExpression();
76 void skipSpaces();
77
isSeparator(char c)78 static bool isSeparator(char c) {
79 switch (c) {
80 case rightParenChar: return true;
81 case leftParenChar: return true;
82 default: return std::isspace(c) != 0;
83 }
84 }
85
86 const FormulaSymbol::CodeMap& formulaSymbolMap_;
87 const std::string s_;
88 std::string::size_type pos_;
89 std::string symbol_;
90 SymbolType symbolType_;
91 };
92
93 void
throwException(const char * errorDescription) const94 FormulaNodeParser::throwException(const char* errorDescription) const
95 {
96 THROW_EXCEPTION(GS::TRMControlModelException, "Formula expression parser error: "
97 << errorDescription
98 << " at position " << (pos_ - symbol_.size()) << " of string [" << s_ << "].");
99 }
100
101 template<typename T>
102 void
throwException(const char * errorDescription,const T & complement) const103 FormulaNodeParser::throwException(const char* errorDescription, const T& complement) const
104 {
105 THROW_EXCEPTION(GS::TRMControlModelException, "Formula expression parser error: "
106 << errorDescription << complement
107 << " at position " << (pos_ - symbol_.size()) << " of string [" << s_ << "].");
108 }
109
110 void
skipSpaces()111 FormulaNodeParser::skipSpaces()
112 {
113 while (!finished() && std::isspace(s_[pos_])) ++pos_;
114 }
115
116 void
nextSymbol()117 FormulaNodeParser::nextSymbol()
118 {
119 skipSpaces();
120
121 symbol_.resize(0);
122
123 if (finished()) {
124 symbolType_ = SYMBOL_TYPE_INVALID;
125 return;
126 }
127
128 char c = s_[pos_++];
129 symbol_ = c;
130 switch (c) {
131 case addChar:
132 symbolType_ = SYMBOL_TYPE_ADD;
133 break;
134 case subChar:
135 symbolType_ = SYMBOL_TYPE_SUB;
136 break;
137 case multChar:
138 symbolType_ = SYMBOL_TYPE_MULT;
139 break;
140 case divChar:
141 symbolType_ = SYMBOL_TYPE_DIV;
142 break;
143 case rightParenChar:
144 symbolType_ = SYMBOL_TYPE_RIGHT_PAREN;
145 break;
146 case leftParenChar:
147 symbolType_ = SYMBOL_TYPE_LEFT_PAREN;
148 break;
149 default:
150 symbolType_ = SYMBOL_TYPE_STRING;
151 while ( !finished() && !isSeparator(c = s_[pos_]) ) {
152 symbol_ += c;
153 ++pos_;
154 }
155 }
156 }
157
158 /*******************************************************************************
159 * FACTOR -> "(" EXPRESSION ")" | SYMBOL | CONST | ADD_OP FACTOR
160 */
161 FormulaNode_ptr
parseFactor()162 FormulaNodeParser::parseFactor()
163 {
164 switch (symbolType_) {
165 case SYMBOL_TYPE_LEFT_PAREN: // (expression)
166 {
167 nextSymbol();
168 FormulaNode_ptr res(parseExpression());
169 if (symbolType_ != SYMBOL_TYPE_RIGHT_PAREN) {
170 throwException("Right parenthesis not found");
171 }
172 nextSymbol();
173 return res;
174 }
175 case SYMBOL_TYPE_ADD: // unary plus
176 nextSymbol();
177 return parseFactor();
178 case SYMBOL_TYPE_SUB: // unary minus
179 nextSymbol();
180 return FormulaNode_ptr(new FormulaMinusUnaryOp(parseFactor()));
181 case SYMBOL_TYPE_STRING: // const / symbol
182 {
183 std::string symbolTmp = symbol_;
184 nextSymbol();
185 FormulaSymbol::CodeMap::const_iterator iter = formulaSymbolMap_.find(symbolTmp);
186 if (iter == formulaSymbolMap_.end()) {
187 // It's not a symbol.
188 return FormulaNode_ptr(new FormulaConst(GS::Text::parseString<float>(symbolTmp)));
189 } else {
190 return FormulaNode_ptr(new FormulaSymbolValue(iter->second));
191 }
192 }
193 case SYMBOL_TYPE_RIGHT_PAREN:
194 throwException("Unexpected symbol: ", rightParenChar);
195 case SYMBOL_TYPE_MULT:
196 throwException("Unexpected symbol: ", multChar);
197 case SYMBOL_TYPE_DIV:
198 throwException("Unexpected symbol: ", divChar);
199 default:
200 throwException("Invalid symbol");
201 }
202 return FormulaNode_ptr(); // unreachable
203 }
204
205 /*******************************************************************************
206 * TERM -> FACTOR { MULT_OP FACTOR }
207 */
parseTerm()208 FormulaNode_ptr FormulaNodeParser::parseTerm()
209 {
210 FormulaNode_ptr term1 = parseFactor();
211
212 SymbolType type = symbolType_;
213 while (type == SYMBOL_TYPE_MULT || type == SYMBOL_TYPE_DIV) {
214 nextSymbol();
215 FormulaNode_ptr term2 = parseFactor();
216 FormulaNode_ptr expr;
217 if (type == SYMBOL_TYPE_MULT) {
218 expr.reset(new FormulaMultBinaryOp(std::move(term1), std::move(term2)));
219 } else {
220 expr.reset(new FormulaDivBinaryOp(std::move(term1), std::move(term2)));
221 }
222 term1.swap(expr);
223 type = symbolType_;
224 }
225
226 return term1;
227 }
228
229 /*******************************************************************************
230 * EXPRESSION -> TERM { ADD_OP TERM }
231 */
232 FormulaNode_ptr
parseExpression()233 FormulaNodeParser::parseExpression()
234 {
235 FormulaNode_ptr term1 = parseTerm();
236
237 SymbolType type = symbolType_;
238 while (type == SYMBOL_TYPE_ADD || type == SYMBOL_TYPE_SUB) {
239
240 nextSymbol();
241 FormulaNode_ptr term2 = parseTerm();
242
243 FormulaNode_ptr expr;
244 if (type == SYMBOL_TYPE_ADD) {
245 expr.reset(new FormulaAddBinaryOp(std::move(term1), std::move(term2)));
246 } else {
247 expr.reset(new FormulaSubBinaryOp(std::move(term1), std::move(term2)));
248 }
249
250 term1 = std::move(expr);
251 type = symbolType_;
252 }
253
254 return term1;
255 }
256
257 /*******************************************************************************
258 *
259 */
260 FormulaNode_ptr
parse()261 FormulaNodeParser::parse()
262 {
263 FormulaNode_ptr formulaRoot = parseExpression();
264 if (symbolType_ != SYMBOL_TYPE_INVALID) { // there is a symbol available
265 throwException("Invalid text");
266 }
267 return formulaRoot;
268 }
269
270 } /* namespace */
271
272 //==============================================================================
273
274 namespace GS {
275 namespace TRMControlModel {
276
277 float
eval(const FormulaSymbolList & symbolList) const278 FormulaMinusUnaryOp::eval(const FormulaSymbolList& symbolList) const
279 {
280 return -(child_->eval(symbolList));
281 }
282
283 void
print(std::ostream & out,int level) const284 FormulaMinusUnaryOp::print(std::ostream& out, int level) const
285 {
286 std::string prefix(level * 8, ' ');
287 out << prefix << "- [\n";
288
289 child_->print(out, level + 1);
290
291 out << prefix << "]" << std::endl;
292 }
293
294 float
eval(const FormulaSymbolList & symbolList) const295 FormulaAddBinaryOp::eval(const FormulaSymbolList& symbolList) const
296 {
297 return child1_->eval(symbolList) + child2_->eval(symbolList);
298 }
299
300 void
print(std::ostream & out,int level) const301 FormulaAddBinaryOp::print(std::ostream& out, int level) const
302 {
303 std::string prefix(level * 8, ' ');
304 out << prefix << "+ [\n";
305
306 child1_->print(out, level + 1);
307 child2_->print(out, level + 1);
308
309 out << prefix << "]" << std::endl;
310 }
311
312 float
eval(const FormulaSymbolList & symbolList) const313 FormulaSubBinaryOp::eval(const FormulaSymbolList& symbolList) const
314 {
315 return child1_->eval(symbolList) - child2_->eval(symbolList);
316 }
317
318 void
print(std::ostream & out,int level) const319 FormulaSubBinaryOp::print(std::ostream& out, int level) const
320 {
321 std::string prefix(level * 8, ' ');
322 out << prefix << "- [\n";
323
324 child1_->print(out, level + 1);
325 child2_->print(out, level + 1);
326
327 out << prefix << "]" << std::endl;
328 }
329
330 float
eval(const FormulaSymbolList & symbolList) const331 FormulaMultBinaryOp::eval(const FormulaSymbolList& symbolList) const
332 {
333 return child1_->eval(symbolList) * child2_->eval(symbolList);
334 }
335
336 void
print(std::ostream & out,int level) const337 FormulaMultBinaryOp::print(std::ostream& out, int level) const
338 {
339 std::string prefix(level * 8, ' ');
340 out << prefix << "* [\n";
341
342 child1_->print(out, level + 1);
343 child2_->print(out, level + 1);
344
345 out << prefix << "]" << std::endl;
346 }
347
348 float
eval(const FormulaSymbolList & symbolList) const349 FormulaDivBinaryOp::eval(const FormulaSymbolList& symbolList) const
350 {
351 return child1_->eval(symbolList) / child2_->eval(symbolList);
352 }
353
354 void
print(std::ostream & out,int level) const355 FormulaDivBinaryOp::print(std::ostream& out, int level) const
356 {
357 std::string prefix(level * 8, ' ');
358 out << prefix << "/ [\n";
359
360 child1_->print(out, level + 1);
361 child2_->print(out, level + 1);
362
363 out << prefix << "]" << std::endl;
364 }
365
366 float
eval(const FormulaSymbolList &) const367 FormulaConst::eval(const FormulaSymbolList& /*symbolList*/) const
368 {
369 return value_;
370 }
371
372 void
print(std::ostream & out,int level) const373 FormulaConst::print(std::ostream& out, int level) const
374 {
375 out << std::string(level * 8, ' ') << "const=" << value_ << std::endl;
376 }
377
378 float
eval(const FormulaSymbolList & symbolList) const379 FormulaSymbolValue::eval(const FormulaSymbolList& symbolList) const
380 {
381 return symbolList[symbol_];
382 }
383
384 void
print(std::ostream & out,int level) const385 FormulaSymbolValue::print(std::ostream& out, int level) const
386 {
387 out << std::string(level * 8, ' ') << "symbol=" << symbol_ << std::endl;
388 }
389
390 /*******************************************************************************
391 *
392 */
393 void
setFormula(const std::string & formula)394 Equation::setFormula(const std::string& formula)
395 {
396 FormulaNodeParser p(formula);
397 FormulaNode_ptr tempFormulaRoot = p.parse();
398
399 formula_ = formula;
400 std::swap(tempFormulaRoot, formulaRoot_);
401 }
402
403 /*******************************************************************************
404 *
405 */
406 float
evalFormula(const FormulaSymbolList & symbolList) const407 Equation::evalFormula(const FormulaSymbolList& symbolList) const
408 {
409 if (!formulaRoot_) {
410 THROW_EXCEPTION(InvalidStateException, "Empty formula.");
411 }
412
413 return formulaRoot_->eval(symbolList);
414 }
415
416 /*******************************************************************************
417 *
418 */
419 std::ostream&
operator <<(std::ostream & out,const Equation & equation)420 operator<<(std::ostream& out, const Equation& equation)
421 {
422 if (equation.formulaRoot_) {
423 equation.formulaRoot_->print(out);
424 }
425 return out;
426 }
427
428 } /* namespace TRMControlModel */
429 } /* namespace GS */
430