File indexing completed on 2024-05-12 16:02:27
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_num_parser.h" 0008 0009 #include <qnumeric.h> // for qIsNaN 0010 #include <qmath.h> 0011 #include <QVector> 0012 #include <QRegExp> 0013 #include <QStringList> 0014 #include <QVariant> 0015 #include <QLocale> 0016 0017 #include <iostream> 0018 0019 using namespace std; 0020 0021 const QVector<char> opLevel1 = {'+', '-'}; 0022 const QVector<char> opLevel2 = {'*', '/'}; 0023 0024 const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"}; 0025 0026 const QRegExp funcExpr("(-)?([a-zA-Z]*[0-9]*)?\\((.+)\\)"); 0027 const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)"); 0028 0029 const QRegExp funcExprInteger("(-)?\\((.+)\\)"); 0030 const QRegExp integerExpr("(-)?([0-9]+)"); 0031 0032 //double functions 0033 double treatFuncs(QString const& expr, bool & noProblem); 0034 double treatLevel1(QString const& expr, bool & noProblem); 0035 double treatLevel2(QString const& expr, bool & noProblem); 0036 double treatLevel3(QString const& expr, bool & noProblem); 0037 0038 //int functions 0039 double treatLevel1Int(QString const& expr, bool & noProblem); 0040 double treatLevel2Int(QString const& expr, bool & noProblem); 0041 double treatFuncsInt(QString const& expr, bool & noProblem); 0042 0043 namespace KisNumericParser { 0044 0045 /*! 0046 * \param expr the expression to parse 0047 * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. 0048 * \return the numerical value the expression eval to (or 0 in case of error). 0049 */ 0050 double parseSimpleMathExpr(const QString &expr, bool *noProblem) 0051 { 0052 0053 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). 0054 0055 //then go down each 3 levels of operation priority. 0056 if (noProblem != nullptr) { 0057 return treatLevel1(expr, *noProblem); 0058 } 0059 0060 return treatLevel1(expr, ok); 0061 0062 } 0063 0064 /*! 0065 * \param expr the expression to parse 0066 * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. 0067 * \return the numerical value the expression eval to (or 0 in case of error). 0068 */ 0069 int parseIntegerMathExpr(QString const& expr, bool* noProblem) 0070 { 0071 0072 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). 0073 0074 if (noProblem != nullptr) { 0075 return qRound(treatLevel1Int(expr, *noProblem)); 0076 } 0077 0078 return qRound(treatLevel1Int(expr, ok)); 0079 0080 } 0081 0082 } //namespace KisNumericParser. 0083 0084 0085 //intermediate functions 0086 0087 /*! 0088 * \brief extractSubExprLevel1 extract from an expression the part of an expression that need to be treated recursively before computing level 1 operations (+, -). 0089 * \param expr The expression to treat, the part returned will be removed. 0090 * \param nextOp This reference, in case of success, will hold the first level operation identified as separator ('+' or '-') 0091 * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. 0092 * \return The first part of the expression that doesn't contain first level operations not nested within parenthesis. 0093 */ 0094 inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){ 0095 0096 QString ret; 0097 0098 int subCount = 0; 0099 0100 bool lastMetIsNumber = false; 0101 0102 for(int i = 0; i < expr.size(); i++){ 0103 0104 if (expr.at(i) == '(') { 0105 subCount++; 0106 } 0107 0108 if (expr.at(i) == ')') { 0109 subCount--; 0110 } 0111 0112 if (subCount < 0) { 0113 noProblem = false; 0114 return ret; 0115 } 0116 0117 if(i == expr.size()-1 && subCount == 0){ 0118 ret = expr; 0119 expr.clear(); 0120 break; 0121 } 0122 0123 if( (expr.at(i) == '+' || expr.at(i) == '-') && 0124 subCount == 0) { 0125 0126 if (expr.at(i) == '-' && 0127 i < expr.size()-1) { 0128 0129 bool cond = !expr.at(i+1).isSpace(); 0130 0131 if (cond && !lastMetIsNumber) { 0132 continue; 0133 } 0134 0135 } 0136 0137 ret = expr.mid(0, i).trimmed(); 0138 nextOp = expr.at(i).toLatin1(); 0139 expr = expr.mid(i+1); 0140 break; 0141 0142 } 0143 0144 if (expr.at(i).isDigit()) { 0145 lastMetIsNumber = true; 0146 } else if (expr.at(i) != '.' && 0147 !expr.at(i).isSpace()) { 0148 lastMetIsNumber = false; 0149 } 0150 0151 } 0152 0153 noProblem = true; 0154 return ret; 0155 } 0156 0157 0158 /*! 0159 * \brief extractSubExprLevel2 extract from an expression the part of an expression that need to be treated recursively before computing level 2 operations (*, /). 0160 * \param expr The expression to treat, the part returned will be removed. 0161 * \param nextOp This reference, in case of success, will hold the first level operation identified as separator ('*' or '/') 0162 * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. 0163 * \return The first part of the expression that doesn't contain second level operations not nested within parenthesis. 0164 */ 0165 inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){ 0166 0167 QString ret; 0168 0169 int subCount = 0; 0170 0171 for(int i = 0; i < expr.size(); i++){ 0172 0173 if (expr.at(i) == '(') { 0174 subCount++; 0175 } 0176 0177 if (expr.at(i) == ')') { 0178 subCount--; 0179 } 0180 0181 if (subCount < 0) { 0182 noProblem = false; 0183 return ret; 0184 } 0185 0186 if(i == expr.size()-1 && subCount == 0){ 0187 ret = expr; 0188 expr.clear(); 0189 break; 0190 } 0191 0192 if( (expr.at(i) == '*' || expr.at(i) == '/') && 0193 subCount == 0) { 0194 0195 ret = expr.mid(0, i).trimmed(); 0196 nextOp = expr.at(i).toLatin1(); 0197 expr = expr.mid(i+1); 0198 break; 0199 0200 } 0201 0202 } 0203 0204 noProblem = true; 0205 return ret; 0206 } 0207 0208 /*! 0209 * \brief treatLevel1 treat an expression at the first level of recursion. 0210 * \param expr The expression to treat. 0211 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0212 * \return The value of the parsed expression or subexpression or 0 in case of error. 0213 */ 0214 double treatLevel1(const QString &expr, bool & noProblem) 0215 { 0216 0217 noProblem = true; 0218 0219 QString exprDestructable = expr; 0220 0221 char nextOp = '+'; 0222 double result = 0.0; 0223 0224 while (!exprDestructable.isEmpty()) { 0225 0226 double sign = (nextOp == '-') ? -1 : 1; 0227 QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); 0228 0229 if (!noProblem) { 0230 return 0.0; 0231 } 0232 0233 if (sign > 0) { 0234 result += treatLevel2(part, noProblem); 0235 } else { 0236 result -= treatLevel2(part, noProblem); 0237 } 0238 0239 if(!noProblem){ 0240 return 0.0; 0241 } 0242 } 0243 0244 return result; 0245 0246 } 0247 0248 /*! 0249 * \brief treatLevel2 treat a subexpression at the second level of recursion. 0250 * \param expr The subexpression to treat. 0251 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0252 * \return The value of the parsed subexpression or 0 in case of error. 0253 * 0254 * The expression should not contain first level operations not nested in parenthesis. 0255 */ 0256 double treatLevel2(QString const& expr, bool & noProblem) 0257 { 0258 0259 noProblem = true; 0260 0261 QString exprDestructable = expr; 0262 0263 char nextOp = '*'; 0264 0265 QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); 0266 0267 double result = treatLevel3(part, noProblem); 0268 0269 while (!exprDestructable.isEmpty()) { 0270 0271 if (!noProblem) { 0272 return 0.0; 0273 } 0274 0275 bool needToMultiply = (nextOp == '*'); 0276 part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); 0277 0278 if (!noProblem) { 0279 return 0.0; 0280 } 0281 0282 if (needToMultiply) { 0283 result *= treatLevel3(part, noProblem); 0284 } else { 0285 result /= treatLevel3(part, noProblem); 0286 } 0287 } 0288 0289 return result; 0290 } 0291 0292 /*! 0293 * \brief treatLevel3 treat a subexpression at the third level of recursion. 0294 * \param expr The subexpression to treat. 0295 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0296 * \return The value of the parsed subexpression or 0 in case of error. 0297 * 0298 * The expression should not contain first or second level operations not nested in parenthesis. 0299 */ 0300 double treatLevel3(const QString &expr, bool & noProblem) 0301 { 0302 0303 noProblem = true; 0304 0305 int indexPower = -1; 0306 int indexCount = 0; 0307 int subLevels = 0; 0308 0309 for (int i = 0; i < expr.size(); i++) { 0310 if (expr.at(i) == '(') { 0311 subLevels++; 0312 } else if(expr.at(i) == ')') { 0313 subLevels--; 0314 if (subLevels < 0) { 0315 noProblem = false; 0316 return 0.0; 0317 } 0318 } else if (expr.at(i) == '^') { 0319 if (subLevels == 0) { 0320 indexPower = i; 0321 indexCount++; 0322 } 0323 } 0324 } 0325 0326 if (indexCount > 1 || indexPower + 1 >= expr.size()) { 0327 noProblem = false; 0328 return 0.0; 0329 } 0330 0331 if (indexPower > -1) { 0332 0333 QStringList subExprs; 0334 subExprs << expr.mid(0,indexPower); 0335 subExprs << expr.mid(indexPower+1); 0336 0337 bool noProb1 = true; 0338 bool noProb2 = true; 0339 0340 double base = treatFuncs(subExprs[0], noProb1); 0341 double power = treatFuncs(subExprs[1], noProb2); 0342 0343 return qPow(base, power); 0344 0345 } else { 0346 return treatFuncs(expr, noProblem); 0347 } 0348 0349 noProblem = false; 0350 return 0.0; 0351 0352 } 0353 0354 /*! 0355 * \brief treatFuncs treat the last level of recursion: parenthesis and functions. 0356 * \param expr The expression to parse. 0357 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0358 * \return The value of the parsed subexpression or 0 in case of error. 0359 * 0360 * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. 0361 */ 0362 double treatFuncs(QString const& expr, bool & noProblem) 0363 { 0364 0365 noProblem = true; 0366 0367 QRegExp funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function. 0368 QRegExp numExp = numberExpr; 0369 0370 if (funcExp.exactMatch(expr.trimmed())) { 0371 0372 int sign = funcExp.capturedTexts()[1].isEmpty() ? 1 : -1; 0373 QString func = funcExp.capturedTexts()[2].toLower(); 0374 QString subExpr = funcExp.capturedTexts()[3]; 0375 0376 double val = treatLevel1(subExpr, noProblem); 0377 0378 if (!noProblem) { 0379 return 0.0; 0380 } 0381 0382 if (func.isEmpty()) { 0383 return sign*val; 0384 } 0385 0386 if (!supportedFuncs.contains(func)) { 0387 noProblem = false; 0388 return 0.0; 0389 } 0390 0391 //trigonometry is done in degree 0392 if (func == "cos") { 0393 val = qCos(val/180*qAcos(-1)); 0394 } else if (func == "sin") { 0395 val = qSin(val/180*qAcos(-1)); 0396 } else if (func == "tan") { 0397 val = qTan(val/180*qAcos(-1)); 0398 } else if(func == "acos") { 0399 val = qAcos(val)*180/qAcos(-1); 0400 } else if (func == "asin") { 0401 val = qAsin(val)*180/qAcos(-1); 0402 } else if (func == "atan") { 0403 val = qAtan(val)*180/qAcos(-1); 0404 } else if (func == "exp") { 0405 val = qExp(val); 0406 } else if (func == "ln") { 0407 val = qLn(val); 0408 } else if (func == "log10") { 0409 val = qLn(val)/qLn(10.0); 0410 } else if (func == "abs") { 0411 val = qAbs(val); 0412 } 0413 0414 return sign*val; 0415 } else if(numExp.exactMatch(expr.trimmed())) { 0416 return expr.toDouble(&noProblem); 0417 } 0418 0419 double val = QLocale().toDouble(expr, &noProblem); 0420 0421 if(noProblem) { 0422 return val; 0423 } 0424 0425 noProblem = false; 0426 return 0.0; 0427 0428 } 0429 0430 //int functions 0431 /*! 0432 * \brief treatLevel1 treat an expression at the first level of recursion. 0433 * \param expr The expression to treat. 0434 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0435 * \return The value of the parsed expression or subexpression or 0 in case of error. 0436 */ 0437 double treatLevel1Int(QString const& expr, bool & noProblem) 0438 { 0439 0440 noProblem = true; 0441 0442 QString exprDestructable = expr; 0443 0444 char nextOp = '+'; 0445 double result = 0.0; 0446 0447 while (!exprDestructable.isEmpty()) { 0448 0449 double sign = (nextOp == '-') ? -1 : 1; 0450 QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); 0451 0452 if( !noProblem) { 0453 return 0.0; 0454 } 0455 0456 if (sign > 0) { 0457 result += treatLevel2Int(part, noProblem); 0458 } else { 0459 result -= treatLevel2Int(part, noProblem); 0460 } 0461 0462 if(!noProblem){ 0463 return 0.0; 0464 } 0465 } 0466 0467 return result; 0468 0469 } 0470 0471 /*! 0472 * \brief treatLevel2 treat a subexpression at the second level of recursion. 0473 * \param expr The subexpression to treat. 0474 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0475 * \return The value of the parsed subexpression or 0 in case of error. 0476 * 0477 * The expression should not contain first level operations not nested in parenthesis. 0478 */ 0479 double treatLevel2Int(const QString &expr, bool &noProblem) 0480 { 0481 0482 noProblem = true; 0483 0484 QString exprDestructable = expr; 0485 0486 char nextOp = '*'; 0487 0488 QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); 0489 0490 double result = treatFuncsInt(part, noProblem); 0491 0492 while (!exprDestructable.isEmpty()) { 0493 0494 if (!noProblem) { 0495 return 0.0; 0496 } 0497 0498 bool needToMultiply = (nextOp == '*'); 0499 part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); 0500 0501 if (!noProblem) { 0502 return 0.0; 0503 } 0504 0505 if (needToMultiply) { 0506 result *= treatFuncsInt(part, noProblem); 0507 } else { 0508 0509 double val = treatFuncsInt(part, noProblem); 0510 0511 if(std::isinf(result/val) || qIsNaN(result/val)){ 0512 noProblem = false; 0513 return 0.0; 0514 } 0515 0516 result /= val; 0517 } 0518 } 0519 0520 return result; 0521 0522 } 0523 0524 /*! 0525 * \brief treatFuncs treat the last level of recursion: parenthesis 0526 * \param expr The expression to parse. 0527 * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. 0528 * \return The value of the parsed subexpression or 0 in case of error. 0529 * 0530 * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. 0531 */ 0532 double treatFuncsInt(QString const& expr, bool & noProblem) 0533 { 0534 0535 noProblem = true; 0536 0537 QRegExp funcExpInteger = funcExprInteger; 0538 QRegExp integerExp = integerExpr; 0539 QRegExp numberExp = numberExpr; 0540 0541 if (funcExpInteger.exactMatch(expr.trimmed())) { 0542 0543 int sign = funcExpInteger.capturedTexts()[1].isEmpty() ? 1 : -1; 0544 QString subExpr = funcExpInteger.capturedTexts()[2]; 0545 0546 double val = treatLevel1Int(subExpr, noProblem); 0547 0548 if (!noProblem) { 0549 return 0; 0550 } 0551 0552 return sign*val; 0553 0554 } else if(numberExp.exactMatch(expr.trimmed())) { 0555 double value = QVariant(expr).toDouble(&noProblem); 0556 return value; 0557 } 0558 0559 noProblem = false; 0560 return 0; 0561 0562 }