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 fctMarkers << "sin" << "cos" << "log10" << "log2" << "log" << "ceil" << "floor" << "round";
33 setExpression(_expression);
40 void ArithmeticEvaluator::setExpression(const QString& _expression) throw(int) {
49 void ArithmeticEvaluator::print() {
50 foreach(QString elt, expression) {
51 cout << qPrintable(elt) << " ";
56 double ArithmeticEvaluator::evalFunction(int indexFunc, double value) {
61 else if (indexFunc == 1) {
64 else if (indexFunc == 2) {
67 else if (indexFunc == 3) {
70 else if (indexFunc == 4) {
73 else if (indexFunc == 5) {
76 else if (indexFunc == 6) {
79 else if (indexFunc == 7) {
86 double ArithmeticEvaluator::evaluate() throw(int) {
92 foreach(QString elt, expression) {
95 \x1bn, n must correspond to the order of QString in fctMarkers.
100 int idFunc = elt.toInt(&ok);
101 if ((!ok) || (idFunc < 0) || (idFunc >= fctMarkers.size())) throw(-index);
102 stack.push(evalFunction(idFunc,value1));
104 else if (varMarkers.contains(c)) {
105 if (!varValues.contains(elt)) throw(-index);
106 stack.push(varValues.value(elt));
108 else if (opMarkers.contains(c)) {
109 value2 = stack.pop();
110 value1 = stack.pop();
112 stack.push(value1+value2);
115 stack.push(value1-value2);
118 stack.push(value1*value2);
121 stack.push(value1/value2);
125 value1 = elt.toDouble(&ok);
126 if (!ok) throw(-index);
132 value1 = stack.pop();
133 if (!stack.isEmpty()) throw(-1);
139 ([ value1 | var1 | func1 | expr1 ] op1 [ value2 | var2 | func2 | expr2 ] op2 ... [ valuen | varn | funcn | exprn ])
141 Thus, if the whole expression does not end with ), we encapsulate it with ( ).
143 If we don't consider priority among operators, then expression is converted into
144 A1 A2 op1 A3 op2 ... An opn-1
146 example : 1+2+3-4-5 -> 1 2 + 3 + 4 -
148 If there is priority : * > / > + or - or func, then it is more complex
150 example : 1+2+3/4*5-6 -> 1 2 + 3 4 5 * / + 6 -
152 with parenthesis, we can do the same recursively
154 example : 1+(2+3/4)*5-6 = 1 + expr1 * 5 - 6 -> 1 expr1 5 * + 6 -
155 but expr1 -> 2 3 4 / +, then we finally have 1 2 3 4 / + 5 * + 6 -
160 example : ((1+3-sin(5/6))*(4+(7/3)))
162 recursive cross in-depth of the expression leads to do a recursive call each time a ( is encountered.
163 Return of the recursive call is done when ) is encountered.
167 void ArithmeticEvaluator::convert(const QString& _expression) throw(int) {
169 QString expr = _expression;
170 cout << "converting " << qPrintable(expr) << endl;
172 expr.remove(QChar(' '), Qt::CaseInsensitive);
173 cout << "converting " << qPrintable(expr) << endl;
174 foreach(QString func, fctMarkers) {
175 cout << "for " << qPrintable(func) << endl;
176 QString rep = QString("\x1b%1").arg(fctMarkers.indexOf(QRegExp(func)));
178 expr.replace(QRegExp(func),rep);
180 cout << "packed expr: " << qPrintable(expr) << endl;
184 result = convertRecur(expr,&offset);
185 expression = result.split(",");
188 cerr << "error while recursive conversion: ";
194 QString ArithmeticEvaluator::convertRecur(const QString& _expression, int *offset) throw(int) {
202 QString expr = _expression;
204 // testing if it starts by a (,number, variable or function
205 if (!checkAfterOp(expr,*offset-1)) throw(*offset);
207 // testing if it starts with a number
208 if ((expr[*offset] == '-') || (expr[*offset].isDigit())) {
209 number = getNumber(expr,*offset,&size);
210 result.append(number+",");
213 // testing if it starts with a variable
214 else if (varMarkers.contains(expr[*offset])){
215 number = getVariable(expr,*offset,&size);
216 result.append(number+",");
220 while (*offset<expr.size()) {
222 if ( expr[*offset] == '\x1b') {
223 int numFunc = getFunction(expr,*offset,&size);
224 if (numFunc == -1) throw(*offset);
226 if (expr[*offset] != '(') throw(*offset);
228 result += convertRecur(expr,offset);
229 result += QString("\x1b%1,").arg(numFunc);
231 else if ( expr[*offset] == '(') {
234 result += convertRecur(expr,offset);
237 else if ( expr[*offset] == ')') {
239 if (!checkAfterPar(expr,*offset)) throw(*offset);
241 while (! pile.isEmpty()) {
243 result.append(c).append(",");
248 else if ( (expr[*offset] == '+') || (expr[*offset] == '-')) {
250 if (!checkAfterOp(expr,*offset)) throw(*offset);
252 // destack operators with equal or greater priority
253 while (!pile.isEmpty()) {
255 result.append(c).append(",");
257 pile.push(expr[*offset]);
259 // catch the function, number or variable after operator if next char is not (
260 if ( varMarkers.contains(expr[*offset+1])) {
261 number = getVariable(expr,*offset+1,&size);
262 result.append(number+",");
265 else if (expr[*offset+1].isDigit()) {
266 number = getNumber(expr,*offset+1,&size);
267 result.append(number+",");
274 else if (expr[*offset] == '/') {
276 if (!checkAfterOp(expr,*offset)) throw(*offset);
278 // destack operator with equal or greater priority (/ and *)
280 while ( (pile.isEmpty() == false) && (c != '(') && (c != '+') && (c != '-')) {
282 if ( (c=='*') || (c == '/')) {
283 result.append(c).append(",");
289 pile.push(expr[*offset]);
291 // catch the 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 /* CASES with * : a*b, (expra)*b, a*(exprb), (expra)*(exprb)
307 Since * has the most priority, then :
308 a*b and (expra)*b can be translate in ... b *
309 In the two other cases, the * is stacked.
311 else if ( expr[*offset] == '*') {
313 if (!checkAfterOp(expr,*offset)) throw(*offset);
315 // catch the number or variable after operator if next char is not (
316 if ( varMarkers.contains(expr[*offset+1])) {
317 number = getVariable(expr,*offset+1,&size);
318 result.append(number+",*,");
321 else if ( expr[*offset+1].isDigit()) {
322 number = getNumber(expr,*offset+1,&size);
323 result.append(number+",*,");
333 while (pile.isEmpty() == false) {
336 result.append(c).append(",");
342 QString ArithmeticEvaluator::getNumber(const QString& _expression, int offset, int *size) {
346 if (_expression[i] == '-') {
352 while (_expression[i].isDigit()) {
353 number.append(_expression[i]);
358 // test if it's a floatting point value
359 if (_expression[i] == '.') {
363 while (_expression[i].isDigit()) {
364 number.append(_expression[i]);
372 QString ArithmeticEvaluator::getVariable(const QString& _expression, int offset, int *size) {
376 if (varMarkers.contains(_expression[i])) {
377 number.append(_expression[i]);
382 while ((_expression[i].isLetterOrNumber()) || (_expression[i] == '-') || (_expression[i] == '_')) {
383 number.append(_expression[i]);
391 int ArithmeticEvaluator::getFunction(const QString& _expression, int offset, int *size) {
396 if (_expression[i] != '\x1b') return -1;
400 while (_expression[i].isDigit()) {
401 number.append(_expression[i]);
406 numFunc = number.toInt(&ok);
407 if ((!ok) || (numFunc >= fctMarkers.size())) return -1;
411 bool ArithmeticEvaluator::checkAfterOp(const QString& _expression, int offset) {
413 if (offset+1 >= _expression.size()) return false;
415 if (_expression[offset+1] == '(') return true;
416 else if (_expression[offset+1].isDigit()) return true;
417 else if (_expression[offset+1] == '-') {
418 if ((offset+2 < _expression.size()) && (_expression[offset+2].isDigit())) return true;
420 else if (varMarkers.contains(_expression[offset+1])) {
421 if ((offset+2 < _expression.size()) && (_expression[offset+2].isLetterOrNumber())) return true;
423 else if (getFunction(_expression, offset+1,&size) != -1) {
430 bool ArithmeticEvaluator::checkAfterPar(const QString& _expression, int offset) {
431 if (offset >= _expression.size()) return false;
432 // if ) is the last char of the expression : OK
433 if (offset == _expression.size()-1) return true;
435 if (_expression[offset+1] == ')') return true;
436 else if (_expression[offset+1] == '+') return true;
437 else if (_expression[offset+1] == '-') return true;
438 else if (_expression[offset+1] == '*') return true;
439 else if (_expression[offset+1] == '/') return true;