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) {
168 QString expr = _expression;
170 expr.remove(QChar(' '), Qt::CaseInsensitive);
171 foreach(QString func, fctMarkers) {
172 QString rep = QString("\x1b%1").arg(fctMarkers.indexOf(QRegExp(func)));
174 expr.replace(QRegExp(func),rep);
176 //cout << "packed expr: " << qPrintable(expr) << endl;
180 result = convertRecur(expr,&offset);
181 expression = result.split(",");
184 cerr << "error while recursive conversion: ";
190 QString ArithmeticEvaluator::convertRecur(const QString& _expression, int *offset) throw(int) {
198 QString expr = _expression;
200 // testing if it starts by a (,number, variable or function
201 if (!checkAfterOp(expr,*offset-1)) throw(*offset);
203 // testing if it starts with a number
204 if ((expr[*offset] == '-') || (expr[*offset].isDigit())) {
205 number = getNumber(expr,*offset,&size);
206 result.append(number+",");
209 // testing if it starts with a variable
210 else if (varMarkers.contains(expr[*offset])){
211 number = getVariable(expr,*offset,&size);
212 result.append(number+",");
216 while (*offset<expr.size()) {
218 if ( expr[*offset] == '\x1b') {
219 int numFunc = getFunction(expr,*offset,&size);
220 if (numFunc == -1) throw(*offset);
222 if (expr[*offset] != '(') throw(*offset);
224 result += convertRecur(expr,offset);
225 result += QString("\x1b%1,").arg(numFunc);
227 else if ( expr[*offset] == '(') {
230 result += convertRecur(expr,offset);
233 else if ( expr[*offset] == ')') {
235 if (!checkAfterPar(expr,*offset)) throw(*offset);
237 while (! pile.isEmpty()) {
239 result.append(c).append(",");
244 else if ( (expr[*offset] == '+') || (expr[*offset] == '-')) {
246 if (!checkAfterOp(expr,*offset)) throw(*offset);
248 // destack operators with equal or greater priority
249 while (!pile.isEmpty()) {
251 result.append(c).append(",");
253 pile.push(expr[*offset]);
255 // catch the function, number or variable after operator if next char is not (
256 if ( varMarkers.contains(expr[*offset+1])) {
257 number = getVariable(expr,*offset+1,&size);
258 result.append(number+",");
261 else if (expr[*offset+1].isDigit()) {
262 number = getNumber(expr,*offset+1,&size);
263 result.append(number+",");
270 else if (expr[*offset] == '/') {
272 if (!checkAfterOp(expr,*offset)) throw(*offset);
274 // destack operator with equal or greater priority (/ and *)
276 while ( (pile.isEmpty() == false) && (c != '(') && (c != '+') && (c != '-')) {
278 if ( (c=='*') || (c == '/')) {
279 result.append(c).append(",");
285 pile.push(expr[*offset]);
287 // catch the number or variable after operator if next char is not (
288 if ( varMarkers.contains(expr[*offset+1])) {
289 number = getVariable(expr,*offset+1,&size);
290 result.append(number+",");
293 else if ( expr[*offset+1].isDigit()) {
294 number = getNumber(expr,*offset+1,&size);
295 result.append(number+",");
302 /* CASES with * : a*b, (expra)*b, a*(exprb), (expra)*(exprb)
303 Since * has the most priority, then :
304 a*b and (expra)*b can be translate in ... b *
305 In the two other cases, the * is stacked.
307 else if ( expr[*offset] == '*') {
309 if (!checkAfterOp(expr,*offset)) throw(*offset);
311 // catch the number or variable after operator if next char is not (
312 if ( varMarkers.contains(expr[*offset+1])) {
313 number = getVariable(expr,*offset+1,&size);
314 result.append(number+",*,");
317 else if ( expr[*offset+1].isDigit()) {
318 number = getNumber(expr,*offset+1,&size);
319 result.append(number+",*,");
329 while (pile.isEmpty() == false) {
332 result.append(c).append(",");
338 QString ArithmeticEvaluator::getNumber(const QString& _expression, int offset, int *size) {
342 if (_expression[i] == '-') {
348 while (_expression[i].isDigit()) {
349 number.append(_expression[i]);
354 // test if it's a floatting point value
355 if (_expression[i] == '.') {
359 while (_expression[i].isDigit()) {
360 number.append(_expression[i]);
368 QString ArithmeticEvaluator::getVariable(const QString& _expression, int offset, int *size) {
372 if (varMarkers.contains(_expression[i])) {
373 number.append(_expression[i]);
378 while ((_expression[i].isLetterOrNumber()) || (_expression[i] == '-') || (_expression[i] == '_')) {
379 number.append(_expression[i]);
387 int ArithmeticEvaluator::getFunction(const QString& _expression, int offset, int *size) {
392 if (_expression[i] != '\x1b') return -1;
396 while (_expression[i].isDigit()) {
397 number.append(_expression[i]);
402 numFunc = number.toInt(&ok);
403 if ((!ok) || (numFunc >= fctMarkers.size())) return -1;
407 bool ArithmeticEvaluator::checkAfterOp(const QString& _expression, int offset) {
409 if (offset+1 >= _expression.size()) return false;
411 if (_expression[offset+1] == '(') return true;
412 else if (_expression[offset+1].isDigit()) return true;
413 else if (_expression[offset+1] == '-') {
414 if ((offset+2 < _expression.size()) && (_expression[offset+2].isDigit())) return true;
416 else if (varMarkers.contains(_expression[offset+1])) {
417 if ((offset+2 < _expression.size()) && (_expression[offset+2].isLetterOrNumber())) return true;
419 else if (getFunction(_expression, offset+1,&size) != -1) {
426 bool ArithmeticEvaluator::checkAfterPar(const QString& _expression, int offset) {
427 if (offset >= _expression.size()) return false;
428 // if ) is the last char of the expression : OK
429 if (offset == _expression.size()-1) return true;
431 if (_expression[offset+1] == ')') return true;
432 else if (_expression[offset+1] == '+') return true;
433 else if (_expression[offset+1] == '-') return true;
434 else if (_expression[offset+1] == '*') return true;
435 else if (_expression[offset+1] == '/') return true;