1 /*-==============================================================-
3 file : ArithmeticEvaluator.cpp
5 creation date : 19/05/2015
7 author : S. Domas (sdomas@univ-fcomte.fr)
11 supp. infos : saved in UTF-8 [éè]
13 -==============================================================-*/
14 #include "ArithmeticEvaluator.h"
17 ArithmeticEvaluator::ArithmeticEvaluator() {
20 expression = QStringList();
21 /* CAUTION : function are mandatory using ( ) to encapsulate the operand
22 since spaces are removed. Thus, an expression like sin 10 will lead to
23 sin10 after spaces removal, and thus becomes invalid.
25 fctMarkers << "sin" << "cos" << "log10" << "log2" << "log" << "ceil" << "floor" << "round";
28 ArithmeticEvaluator::ArithmeticEvaluator(const QString& _expression) throw(int) {
31 expression = QStringList();
32 /* CAUTION : function are mandatory using ( ) to encapsulate the operand
33 since spaces are removed. Thus, an expression like sin 10 will lead to
34 sin10 after spaces removal, and thus becomes invalid.
36 fctMarkers << "sin" << "cos" << "log10" << "log2" << "log" << "ceil" << "floor" << "round";
39 setExpression(_expression);
46 void ArithmeticEvaluator::setExpression(const QString& _expression) throw(int) {
48 setVariableNames(_expression);
58 void ArithmeticEvaluator::setVariablesValue(const QHash<QString,double>& _varValues) {
59 varValues = _varValues;
61 QHashIterator<QString,double> iterV(varValues);
62 while (iterV.hasNext()) {
64 cout << "var " << qPrintable(iterV.key()) << " = " << iterV.value() << endl;
69 void ArithmeticEvaluator::setVariableNames(const QString& _expression) {
71 QRegularExpression re("[$][a-zA-Z0-9_]+");
72 QRegularExpressionMatchIterator matcher = re.globalMatch(_expression);
73 while(matcher.hasNext()) {
74 QRegularExpressionMatch m = matcher.next();
75 QString var = m.captured(0);
80 void ArithmeticEvaluator::print() {
81 foreach(QString elt, expression) {
82 cout << qPrintable(elt) << " ";
87 double ArithmeticEvaluator::evalFunction(int indexFunc, double value) {
92 else if (indexFunc == 1) {
95 else if (indexFunc == 2) {
98 else if (indexFunc == 3) {
101 else if (indexFunc == 4) {
104 else if (indexFunc == 5) {
107 else if (indexFunc == 6) {
110 else if (indexFunc == 7) {
117 double ArithmeticEvaluator::evaluate() throw(int) {
119 QStack<double> stack;
121 double value1,value2;
124 foreach(QString elt, expression) {
127 \x1bn, n must correspond to the order of QString in fctMarkers.
130 value1 = stack.pop();
132 int idFunc = elt.toInt(&ok);
133 if ((!ok) || (idFunc < 0) || (idFunc >= fctMarkers.size())) throw(-index);
134 stack.push(evalFunction(idFunc,value1));
136 else if (varMarkers.contains(c)) {
137 if (!varValues.contains(elt)) {
138 errorMessage = "cannot find value of ";
139 errorMessage += qPrintable(elt);
142 stack.push(varValues.value(elt));
144 else if (opMarkers.contains(c)) {
145 value2 = stack.pop();
146 value1 = stack.pop();
148 stack.push(value1+value2);
151 stack.push(value1-value2);
154 stack.push(value1*value2);
157 stack.push(value1/value2);
161 value1 = elt.toDouble(&ok);
162 if (!ok) throw(-index);
168 value1 = stack.pop();
169 if (!stack.isEmpty()) throw(-1);
175 ([ value1 | var1 | func1 | expr1 ] op1 [ value2 | var2 | func2 | expr2 ] op2 ... [ valuen | varn | funcn | exprn ])
177 Thus, if the whole expression does not end with ), we encapsulate it with ( ).
179 If we don't consider priority among operators, then expression is converted into
180 A1 A2 op1 A3 op2 ... An opn-1
182 example : 1+2+3-4-5 -> 1 2 + 3 + 4 -
184 If there is priority : * > / > + or - or func, then it is more complex
186 example : 1+2+3/4*5-6 -> 1 2 + 3 4 5 * / + 6 -
188 with parenthesis, we can do the same recursively
190 example : 1+(2+3/4)*5-6 = 1 + expr1 * 5 - 6 -> 1 expr1 5 * + 6 -
191 but expr1 -> 2 3 4 / +, then we finally have 1 2 3 4 / + 5 * + 6 -
196 example : ((1+3-sin(5/6))*(4+(7/3)))
198 recursive cross in-depth of the expression leads to do a recursive call each time a ( is encountered.
199 Return of the recursive call is done when ) is encountered.
203 void ArithmeticEvaluator::convert(const QString& _expression) throw(int) {
205 QString expr = _expression;
207 expr.remove(QChar(' '), Qt::CaseInsensitive);
208 foreach(QString func, fctMarkers) {
209 QString rep = QString("\x1b%1").arg(fctMarkers.indexOf(QRegExp(func)));
211 expr.replace(QRegExp(func),rep);
216 result = convertRecur(expr,&offset);
217 expression = result.split(",");
220 cerr << "error while recursive conversion: ";
226 QString ArithmeticEvaluator::convertRecur(const QString& _expression, int *offset) throw(int) {
234 QString expr = _expression;
236 // testing if it starts by a (,number, variable or function
237 if (!checkAfterOp(expr,*offset-1)) throw(*offset);
239 // testing if it starts with a number
240 if ((expr[*offset] == '-') || (expr[*offset].isDigit())) {
241 number = getNumber(expr,*offset,&size);
242 result.append(number+",");
245 // testing if it starts with a variable
246 else if (varMarkers.contains(expr[*offset])){
247 number = getVariable(expr,*offset,&size);
248 result.append(number+",");
252 while (*offset<expr.size()) {
254 if ( expr[*offset] == '\x1b') {
255 int numFunc = getFunction(expr,*offset,&size);
256 if (numFunc == -1) throw(*offset);
258 if (expr[*offset] != '(') throw(*offset);
260 result += convertRecur(expr,offset);
261 result += QString("\x1b%1,").arg(numFunc);
263 else if ( expr[*offset] == '(') {
266 result += convertRecur(expr,offset);
269 else if ( expr[*offset] == ')') {
271 if (!checkAfterPar(expr,*offset)) throw(*offset);
273 while (! pile.isEmpty()) {
275 result.append(c).append(",");
280 else if ( (expr[*offset] == '+') || (expr[*offset] == '-')) {
282 if (!checkAfterOp(expr,*offset)) throw(*offset);
284 // destack operators with equal or greater priority
285 while (!pile.isEmpty()) {
287 result.append(c).append(",");
289 pile.push(expr[*offset]);
291 // catch the function, number or variable after operator if next char is not (
292 if ( varMarkers.contains(expr[*offset+1])) {
293 number = getVariable(expr,*offset+1,&size);
294 result.append(number+",");
297 else if (expr[*offset+1].isDigit()) {
298 number = getNumber(expr,*offset+1,&size);
299 result.append(number+",");
306 else if (expr[*offset] == '/') {
308 if (!checkAfterOp(expr,*offset)) throw(*offset);
310 // destack operator with equal or greater priority (/ and *)
312 while ( (pile.isEmpty() == false) && (c != '(') && (c != '+') && (c != '-')) {
314 if ( (c=='*') || (c == '/')) {
315 result.append(c).append(",");
321 pile.push(expr[*offset]);
323 // catch the number or variable after operator if next char is not (
324 if ( varMarkers.contains(expr[*offset+1])) {
325 number = getVariable(expr,*offset+1,&size);
326 result.append(number+",");
329 else if ( expr[*offset+1].isDigit()) {
330 number = getNumber(expr,*offset+1,&size);
331 result.append(number+",");
338 /* CASES with * : a*b, (expra)*b, a*(exprb), (expra)*(exprb)
339 Since * has the most priority, then :
340 a*b and (expra)*b can be translate in ... b *
341 In the two other cases, the * is stacked.
343 else if ( expr[*offset] == '*') {
345 if (!checkAfterOp(expr,*offset)) throw(*offset);
347 // catch the number or variable after operator if next char is not (
348 if ( varMarkers.contains(expr[*offset+1])) {
349 number = getVariable(expr,*offset+1,&size);
350 result.append(number+",*,");
353 else if ( expr[*offset+1].isDigit()) {
354 number = getNumber(expr,*offset+1,&size);
355 result.append(number+",*,");
365 while (pile.isEmpty() == false) {
368 result.append(c).append(",");
374 QString ArithmeticEvaluator::getNumber(const QString& _expression, int offset, int *size) {
378 if (_expression[i] == '-') {
384 while (_expression[i].isDigit()) {
385 number.append(_expression[i]);
390 // test if it's a floatting point value
391 if (_expression[i] == '.') {
395 while (_expression[i].isDigit()) {
396 number.append(_expression[i]);
404 QString ArithmeticEvaluator::getVariable(const QString& _expression, int offset, int *size) {
408 if (varMarkers.contains(_expression[i])) {
409 number.append(_expression[i]);
414 while ((_expression[i].isLetterOrNumber()) || (_expression[i] == '_')) {
415 number.append(_expression[i]);
423 int ArithmeticEvaluator::getFunction(const QString& _expression, int offset, int *size) {
428 if (_expression[i] != '\x1b') return -1;
432 while (_expression[i].isDigit()) {
433 number.append(_expression[i]);
438 numFunc = number.toInt(&ok);
439 if ((!ok) || (numFunc >= fctMarkers.size())) return -1;
443 bool ArithmeticEvaluator::checkAfterOp(const QString& _expression, int offset) {
445 if (offset+1 >= _expression.size()) return false;
447 if (_expression[offset+1] == '(') return true;
448 else if (_expression[offset+1].isDigit()) return true;
449 else if (_expression[offset+1] == '-') {
450 if ((offset+2 < _expression.size()) && (_expression[offset+2].isDigit())) return true;
452 else if (varMarkers.contains(_expression[offset+1])) {
453 if ((offset+2 < _expression.size()) && (_expression[offset+2].isLetterOrNumber())) return true;
455 else if (getFunction(_expression, offset+1,&size) != -1) {
462 bool ArithmeticEvaluator::checkAfterPar(const QString& _expression, int offset) {
463 if (offset >= _expression.size()) return false;
464 // if ) is the last char of the expression : OK
465 if (offset == _expression.size()-1) return true;
467 if (_expression[offset+1] == ')') return true;
468 else if (_expression[offset+1] == '+') return true;
469 else if (_expression[offset+1] == '-') return true;
470 else if (_expression[offset+1] == '*') return true;
471 else if (_expression[offset+1] == '/') return true;