Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_num_parser.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_num_parser.h"
8
9#include <qnumeric.h> // for qIsNaN
10#include <qmath.h>
11#include <QVector>
12#include <QRegExp>
13#include <QStringList>
14#include <QVariant>
15#include <QLocale>
16
17#include <iostream>
18
19using namespace std;
20
21const QVector<char> opLevel1 = {'+', '-'};
22const QVector<char> opLevel2 = {'*', '/'};
23
24const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"};
25
26const QRegExp funcExpr("(-)?([a-zA-Z]*[0-9]*)?\\((.+)\\)");
27const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)");
28
29const QRegExp funcExprInteger("(-)?\\((.+)\\)");
30const QRegExp integerExpr("(-)?([0-9]+)");
31
32//double functions
33double treatFuncs(QString const& expr, bool & noProblem);
34double treatLevel1(QString const& expr, bool & noProblem);
35double treatLevel2(QString const& expr, bool & noProblem);
36double treatLevel3(QString const& expr, bool & noProblem);
37
38//int functions
39double treatLevel1Int(QString const& expr, bool & noProblem);
40double treatLevel2Int(QString const& expr, bool & noProblem);
41double treatFuncsInt(QString const& expr, bool & noProblem);
42
44
50double parseSimpleMathExpr(const QString &expr, bool *noProblem)
51{
52
53 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
54
55 //then go down each 3 levels of operation priority.
56 if (noProblem != nullptr) {
57 return treatLevel1(expr, *noProblem);
58 }
59
60 return treatLevel1(expr, ok);
61
62}
63
69int parseIntegerMathExpr(QString const& expr, bool* noProblem)
70{
71
72 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
73
74 if (noProblem != nullptr) {
75 return qRound(treatLevel1Int(expr, *noProblem));
76 }
77
78 return qRound(treatLevel1Int(expr, ok));
79
80}
81
82} //namespace KisNumericParser.
83
84
85//intermediate functions
86
94inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){
95
96 QString ret;
97
98 int subCount = 0;
99
100 bool lastMetIsNumber = false;
101
102 for(int i = 0; i < expr.size(); i++){
103
104 if (expr.at(i) == '(') {
105 subCount++;
106 }
107
108 if (expr.at(i) == ')') {
109 subCount--;
110 }
111
112 if (subCount < 0) {
113 noProblem = false;
114 return ret;
115 }
116
117 if(i == expr.size()-1 && subCount == 0){
118 ret = expr;
119 expr.clear();
120 break;
121 }
122
123 if( (expr.at(i) == '+' || expr.at(i) == '-') &&
124 subCount == 0) {
125
126 if (expr.at(i) == '-' &&
127 i < expr.size()-1) {
128
129 bool cond = !expr.at(i+1).isSpace();
130
131 if (cond && !lastMetIsNumber) {
132 continue;
133 }
134
135 }
136
137 ret = expr.mid(0, i).trimmed();
138 nextOp = expr.at(i).toLatin1();
139 expr = expr.mid(i+1);
140 break;
141
142 }
143
144 if (expr.at(i).isDigit()) {
145 lastMetIsNumber = true;
146 } else if (expr.at(i) != '.' &&
147 !expr.at(i).isSpace()) {
148 lastMetIsNumber = false;
149 }
150
151 }
152
153 noProblem = true;
154 return ret;
155}
156
157
165inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){
166
167 QString ret;
168
169 int subCount = 0;
170
171 for(int i = 0; i < expr.size(); i++){
172
173 if (expr.at(i) == '(') {
174 subCount++;
175 }
176
177 if (expr.at(i) == ')') {
178 subCount--;
179 }
180
181 if (subCount < 0) {
182 noProblem = false;
183 return ret;
184 }
185
186 if(i == expr.size()-1 && subCount == 0){
187 ret = expr;
188 expr.clear();
189 break;
190 }
191
192 if( (expr.at(i) == '*' || expr.at(i) == '/') &&
193 subCount == 0) {
194
195 ret = expr.mid(0, i).trimmed();
196 nextOp = expr.at(i).toLatin1();
197 expr = expr.mid(i+1);
198 break;
199
200 }
201
202 }
203
204 noProblem = true;
205 return ret;
206}
207
214double treatLevel1(const QString &expr, bool & noProblem)
215{
216
217 noProblem = true;
218
219 QString exprDestructible = expr;
220
221 char nextOp = '+';
222 double result = 0.0;
223
224 while (!exprDestructible.isEmpty()) {
225
226 double sign = (nextOp == '-') ? -1 : 1;
227 QString part = extractSubExprLevel1(exprDestructible, nextOp, noProblem);
228
229 if (!noProblem) {
230 return 0.0;
231 }
232
233 if (sign > 0) {
234 result += treatLevel2(part, noProblem);
235 } else {
236 result -= treatLevel2(part, noProblem);
237 }
238
239 if(!noProblem){
240 return 0.0;
241 }
242 }
243
244 return result;
245
246}
247
256double treatLevel2(QString const& expr, bool & noProblem)
257{
258
259 noProblem = true;
260
261 QString exprDestructible = expr;
262
263 char nextOp = '*';
264
265 QString part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
266
267 double result = treatLevel3(part, noProblem);
268
269 while (!exprDestructible.isEmpty()) {
270
271 if (!noProblem) {
272 return 0.0;
273 }
274
275 bool needToMultiply = (nextOp == '*');
276 part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
277
278 if (!noProblem) {
279 return 0.0;
280 }
281
282 if (needToMultiply) {
283 result *= treatLevel3(part, noProblem);
284 } else {
285 result /= treatLevel3(part, noProblem);
286 }
287 }
288
289 return result;
290}
291
300double treatLevel3(const QString &expr, bool & noProblem)
301{
302
303 noProblem = true;
304
305 int indexPower = -1;
306 int indexCount = 0;
307 int subLevels = 0;
308
309 for (int i = 0; i < expr.size(); i++) {
310 if (expr.at(i) == '(') {
311 subLevels++;
312 } else if(expr.at(i) == ')') {
313 subLevels--;
314 if (subLevels < 0) {
315 noProblem = false;
316 return 0.0;
317 }
318 } else if (expr.at(i) == '^') {
319 if (subLevels == 0) {
320 indexPower = i;
321 indexCount++;
322 }
323 }
324 }
325
326 if (indexCount > 1 || indexPower + 1 >= expr.size()) {
327 noProblem = false;
328 return 0.0;
329 }
330
331 if (indexPower > -1) {
332
333 QStringList subExprs;
334 subExprs << expr.mid(0,indexPower);
335 subExprs << expr.mid(indexPower+1);
336
337 bool noProb1 = true;
338 bool noProb2 = true;
339
340 double base = treatFuncs(subExprs[0], noProb1);
341 double power = treatFuncs(subExprs[1], noProb2);
342
343 return qPow(base, power);
344
345 } else {
346 return treatFuncs(expr, noProblem);
347 }
348
349 noProblem = false;
350 return 0.0;
351
352}
353
362double treatFuncs(QString const& expr, bool & noProblem)
363{
364
365 noProblem = true;
366
367 QRegExp funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function.
368 QRegExp numExp = numberExpr;
369
370 if (funcExp.exactMatch(expr.trimmed())) {
371
372 int sign = funcExp.capturedTexts()[1].isEmpty() ? 1 : -1;
373 QString func = funcExp.capturedTexts()[2].toLower();
374 QString subExpr = funcExp.capturedTexts()[3];
375
376 double val = treatLevel1(subExpr, noProblem);
377
378 if (!noProblem) {
379 return 0.0;
380 }
381
382 if (func.isEmpty()) {
383 return sign*val;
384 }
385
386 if (!supportedFuncs.contains(func)) {
387 noProblem = false;
388 return 0.0;
389 }
390
391 //trigonometry is done in degree
392 if (func == "cos") {
393 val = qCos(val/180*qAcos(-1));
394 } else if (func == "sin") {
395 val = qSin(val/180*qAcos(-1));
396 } else if (func == "tan") {
397 val = qTan(val/180*qAcos(-1));
398 } else if(func == "acos") {
399 val = qAcos(val)*180/qAcos(-1);
400 } else if (func == "asin") {
401 val = qAsin(val)*180/qAcos(-1);
402 } else if (func == "atan") {
403 val = qAtan(val)*180/qAcos(-1);
404 } else if (func == "exp") {
405 val = qExp(val);
406 } else if (func == "ln") {
407 val = qLn(val);
408 } else if (func == "log10") {
409 val = qLn(val)/qLn(10.0);
410 } else if (func == "abs") {
411 val = qAbs(val);
412 }
413
414 return sign*val;
415 } else if(numExp.exactMatch(expr.trimmed())) {
416 return expr.toDouble(&noProblem);
417 }
418
419 double val = QLocale().toDouble(expr, &noProblem);
420
421 if(noProblem) {
422 return val;
423 }
424
425 noProblem = false;
426 return 0.0;
427
428}
429
430//int functions
437double treatLevel1Int(QString const& expr, bool & noProblem)
438{
439
440 noProblem = true;
441
442 QString exprDestructible = expr;
443
444 char nextOp = '+';
445 double result = 0.0;
446
447 while (!exprDestructible.isEmpty()) {
448
449 double sign = (nextOp == '-') ? -1 : 1;
450 QString part = extractSubExprLevel1(exprDestructible, nextOp, noProblem);
451
452 if( !noProblem) {
453 return 0.0;
454 }
455
456 if (sign > 0) {
457 result += treatLevel2Int(part, noProblem);
458 } else {
459 result -= treatLevel2Int(part, noProblem);
460 }
461
462 if(!noProblem){
463 return 0.0;
464 }
465 }
466
467 return result;
468
469}
470
479double treatLevel2Int(const QString &expr, bool &noProblem)
480{
481
482 noProblem = true;
483
484 QString exprDestructible = expr;
485
486 char nextOp = '*';
487
488 QString part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
489
490 double result = treatFuncsInt(part, noProblem);
491
492 while (!exprDestructible.isEmpty()) {
493
494 if (!noProblem) {
495 return 0.0;
496 }
497
498 bool needToMultiply = (nextOp == '*');
499 part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
500
501 if (!noProblem) {
502 return 0.0;
503 }
504
505 if (needToMultiply) {
506 result *= treatFuncsInt(part, noProblem);
507 } else {
508
509 double val = treatFuncsInt(part, noProblem);
510
511 if(std::isinf(result/val) || qIsNaN(result/val)){
512 noProblem = false;
513 return 0.0;
514 }
515
516 result /= val;
517 }
518 }
519
520 return result;
521
522}
523
532double treatFuncsInt(QString const& expr, bool & noProblem)
533{
534
535 noProblem = true;
536
537 QRegExp funcExpInteger = funcExprInteger;
538 QRegExp integerExp = integerExpr;
539 QRegExp numberExp = numberExpr;
540
541 if (funcExpInteger.exactMatch(expr.trimmed())) {
542
543 int sign = funcExpInteger.capturedTexts()[1].isEmpty() ? 1 : -1;
544 QString subExpr = funcExpInteger.capturedTexts()[2];
545
546 double val = treatLevel1Int(subExpr, noProblem);
547
548 if (!noProblem) {
549 return 0;
550 }
551
552 return sign*val;
553
554 } else if(numberExp.exactMatch(expr.trimmed())) {
555 double value = QVariant(expr).toDouble(&noProblem);
556 return value;
557 }
558
559 noProblem = false;
560 return 0;
561
562}
float value(const T *src, size_t ch)
quint64 part(quint64 n1, quint64 n2, int p)
const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)")
const QRegExp funcExpr("(-)?([a-zA-Z]*[0-9]*)?\\‍((.+)\\‍)")
const QVector< char > opLevel2
const QVector< char > opLevel1
QString extractSubExprLevel2(QString &expr, char &nextOp, bool &noProblem)
extractSubExprLevel2 extract from an expression the part of an expression that need to be treated rec...
const QRegExp integerExpr("(-)?([0-9]+)")
double treatFuncs(QString const &expr, bool &noProblem)
treatFuncs treat the last level of recursion: parenthesis and functions.
const QStringList supportedFuncs
QString extractSubExprLevel1(QString &expr, char &nextOp, bool &noProblem)
extractSubExprLevel1 extract from an expression the part of an expression that need to be treated rec...
double treatFuncsInt(QString const &expr, bool &noProblem)
treatFuncs treat the last level of recursion: parenthesis
double treatLevel1(QString const &expr, bool &noProblem)
treatLevel1 treat an expression at the first level of recursion.
double treatLevel3(QString const &expr, bool &noProblem)
treatLevel3 treat a subexpression at the third level of recursion.
double treatLevel1Int(QString const &expr, bool &noProblem)
treatLevel1 treat an expression at the first level of recursion.
double treatLevel2Int(QString const &expr, bool &noProblem)
treatLevel2 treat a subexpression at the second level of recursion.
const QRegExp funcExprInteger("(-)?\\‍((.+)\\‍)")
double treatLevel2(QString const &expr, bool &noProblem)
treatLevel2 treat a subexpression at the second level of recursion.
the namespace contains functions to transform math expression written as QString in numbers.
int parseIntegerMathExpr(QString const &expr, bool *noProblem)
parse an expression to an int.
double parseSimpleMathExpr(const QString &expr, bool *noProblem)
parse an expression to a double.