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() {
18 opMarkers = "+-*/|%"; // | is for euclidan division
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) {
29 opMarkers = "+-*/|%"; // | is for euclidan division
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);
160 int val1 = (int)value1;
161 int val2 = (int)value2;
162 stack.push(val1/val2);
165 int val1 = (int)value1;
166 int val2 = (int)value2;
167 stack.push(val1%val2);
171 value1 = elt.toDouble(&ok);
172 if (!ok) throw(-index);
178 value1 = stack.pop();
179 if (!stack.isEmpty()) throw(-1);
185 ([ value1 | var1 | func1 | expr1 ] op1 [ value2 | var2 | func2 | expr2 ] op2 ... [ valuen | varn | funcn | exprn ])
187 Thus, if the whole expression does not end with ), we encapsulate it with ( ).
189 If we don't consider priority among operators, then expression is converted into
190 A1 A2 op1 A3 op2 ... An opn-1
192 example : 1+2+3-4-5 -> 1 2 + 3 + 4 -
194 If there is priority : * > / > + or - or func, then it is more complex
196 example : 1+2+3/4*5-6 -> 1 2 + 3 4 5 * / + 6 -
198 with parenthesis, we can do the same recursively
200 example : 1+(2+3/4)*5-6 = 1 + expr1 * 5 - 6 -> 1 expr1 5 * + 6 -
201 but expr1 -> 2 3 4 / +, then we finally have 1 2 3 4 / + 5 * + 6 -
206 example : ((1+3-sin(5/6))*(4+(7/3)))
208 recursive cross in-depth of the expression leads to do a recursive call each time a ( is encountered.
209 Return of the recursive call is done when ) is encountered.
213 void ArithmeticEvaluator::convert(const QString& _expression) throw(int) {
215 QString expr = _expression;
217 expr.remove(QChar(' '), Qt::CaseInsensitive);
218 foreach(QString func, fctMarkers) {
219 QString rep = QString("\x1b%1").arg(fctMarkers.indexOf(QRegExp(func)));
221 expr.replace(QRegExp(func),rep);
226 result = convertRecur(expr,&offset);
227 expression = result.split(",");
230 cerr << "error while recursive conversion: ";
236 QString ArithmeticEvaluator::convertRecur(const QString& _expression, int *offset) throw(int) {
244 QString expr = _expression;
246 // testing if it starts by a (,number, variable or function
247 if (!checkAfterOp(expr,*offset-1)) throw(*offset);
249 // testing if it starts with a number
250 if ((expr[*offset] == '-') || (expr[*offset].isDigit())) {
251 number = getNumber(expr,*offset,&size);
252 result.append(number+",");
255 // testing if it starts with a variable
256 else if (varMarkers.contains(expr[*offset])){
257 number = getVariable(expr,*offset,&size);
258 result.append(number+",");
262 while (*offset<expr.size()) {
264 if ( expr[*offset] == '\x1b') {
265 int numFunc = getFunction(expr,*offset,&size);
266 if (numFunc == -1) throw(*offset);
268 if (expr[*offset] != '(') throw(*offset);
270 result += convertRecur(expr,offset);
271 result += QString("\x1b%1,").arg(numFunc);
273 else if ( expr[*offset] == '(') {
276 result += convertRecur(expr,offset);
279 else if ( expr[*offset] == ')') {
281 if (!checkAfterPar(expr,*offset)) throw(*offset);
283 while (! pile.isEmpty()) {
285 result.append(c).append(",");
290 else if ( (expr[*offset] == '+') || (expr[*offset] == '-')) {
292 if (!checkAfterOp(expr,*offset)) throw(*offset);
294 // destack operators with equal or greater priority
295 while (!pile.isEmpty()) {
297 result.append(c).append(",");
299 pile.push(expr[*offset]);
301 // catch the function, number or variable after operator if next char is not (
302 if ( varMarkers.contains(expr[*offset+1])) {
303 number = getVariable(expr,*offset+1,&size);
304 result.append(number+",");
307 else if (expr[*offset+1].isDigit()) {
308 number = getNumber(expr,*offset+1,&size);
309 result.append(number+",");
316 else if ((expr[*offset] == '/')||(expr[*offset] == '|')||(expr[*offset] == '%')) {
318 if (!checkAfterOp(expr,*offset)) throw(*offset);
320 // destack operator with equal or greater priority (/ and *)
322 while ( (pile.isEmpty() == false) && (c != '(') && (c != '+') && (c != '-')) {
324 if ( (c=='*') || (c == '/') || (c == '|') || (c=='%')) {
325 result.append(c).append(",");
331 pile.push(expr[*offset]);
333 // catch the number or variable after operator if next char is not (
334 if ( varMarkers.contains(expr[*offset+1])) {
335 number = getVariable(expr,*offset+1,&size);
336 result.append(number+",");
339 else if ( expr[*offset+1].isDigit()) {
340 number = getNumber(expr,*offset+1,&size);
341 result.append(number+",");
348 /* CASES with * : a*b, (expra)*b, a*(exprb), (expra)*(exprb)
349 Since * has the most priority, then :
350 a*b and (expra)*b can be translate in ... b *
351 In the two other cases, the * is stacked.
353 else if ( expr[*offset] == '*') {
355 if (!checkAfterOp(expr,*offset)) throw(*offset);
357 // catch the number or variable after operator if next char is not (
358 if ( varMarkers.contains(expr[*offset+1])) {
359 number = getVariable(expr,*offset+1,&size);
360 result.append(number+",*,");
363 else if ( expr[*offset+1].isDigit()) {
364 number = getNumber(expr,*offset+1,&size);
365 result.append(number+",*,");
375 while (pile.isEmpty() == false) {
378 result.append(c).append(",");
384 QString ArithmeticEvaluator::getNumber(const QString& _expression, int offset, int *size) {
388 if (_expression[i] == '-') {
394 while (_expression[i].isDigit()) {
395 number.append(_expression[i]);
400 // test if it's a floatting point value
401 if (_expression[i] == '.') {
405 while (_expression[i].isDigit()) {
406 number.append(_expression[i]);
414 QString ArithmeticEvaluator::getVariable(const QString& _expression, int offset, int *size) {
418 if (varMarkers.contains(_expression[i])) {
419 number.append(_expression[i]);
424 while ((_expression[i].isLetterOrNumber()) || (_expression[i] == '_')) {
425 number.append(_expression[i]);
433 int ArithmeticEvaluator::getFunction(const QString& _expression, int offset, int *size) {
438 if (_expression[i] != '\x1b') return -1;
442 while (_expression[i].isDigit()) {
443 number.append(_expression[i]);
448 numFunc = number.toInt(&ok);
449 if ((!ok) || (numFunc >= fctMarkers.size())) return -1;
453 bool ArithmeticEvaluator::checkAfterOp(const QString& _expression, int offset) {
455 if (offset+1 >= _expression.size()) return false;
457 if (_expression[offset+1] == '(') return true;
458 else if (_expression[offset+1].isDigit()) return true;
459 else if (_expression[offset+1] == '-') {
460 if ((offset+2 < _expression.size()) && (_expression[offset+2].isDigit())) return true;
462 else if (varMarkers.contains(_expression[offset+1])) {
463 if ((offset+2 < _expression.size()) && (_expression[offset+2].isLetterOrNumber())) return true;
465 else if (getFunction(_expression, offset+1,&size) != -1) {
472 bool ArithmeticEvaluator::checkAfterPar(const QString& _expression, int offset) {
473 if (offset >= _expression.size()) return false;
474 // if ) is the last char of the expression : OK
475 if (offset == _expression.size()-1) return true;
477 if (_expression[offset+1] == ')') return true;
478 else if (_expression[offset+1] == '+') return true;
479 else if (_expression[offset+1] == '-') return true;
480 else if (_expression[offset+1] == '*') return true;
481 else if (_expression[offset+1] == '/') return true;
482 else if (_expression[offset+1] == '|') return true;
483 else if (_expression[offset+1] == '%') return true;