Warning, file /education/kmplot/kmplot/parser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KmPlot - a math. function plotter for the KDE-Desktop 0003 0004 SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de> 0005 SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org> 0006 0007 This file is part of the KDE Project. 0008 KmPlot is part of the KDE-EDU Project. 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 0012 */ 0013 0014 // local includes 0015 #include "parser.h" 0016 #include "parseradaptor.h" 0017 #include "settings.h" 0018 #include "xparser.h" 0019 0020 // KDE includes 0021 #include <KConfig> 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 0025 #include <QDebug> 0026 #include <QList> 0027 0028 // standard c(++) includes 0029 #include <assert.h> 0030 #include <cmath> 0031 #include <limits> 0032 #include <locale.h> 0033 #include <math.h> 0034 #include <stdio.h> 0035 #include <stdlib.h> 0036 0037 double Parser::m_radiansPerAngleUnit = 0; 0038 0039 /** 0040 * List of predefined functions. 0041 * \note Some function names include other function names (e.g. "sinh" has the 0042 * string "sin" in it). The Parser will stop once it has found a matching 0043 * function name, so such functions must be in order of longest first. 0044 */ 0045 ScalarFunction Parser::scalarFunctions[ScalarCount] = { 0046 // Hyperbolic trig 0047 {"sinh", QString(), sinh}, // Sinus hyperbolicus 0048 {"cosh", QString(), cosh}, // Cosinus hyperbolicus 0049 {"tanh", QString(), tanh}, // Tangens hyperbolicus 0050 {"arcsinh", "arsinh", asinh}, // Area-sinus hyperbolicus = inverse of sinh 0051 {"arccosh", "arcosh", acosh}, // Area-cosinus hyperbolicus = inverse of cosh 0052 {"arctanh", "artanh", atanh}, // Area-tangens hyperbolicus = inverse of tanh 0053 0054 // Reciprocal-hyperbolic 0055 {"cosech", QString(), cosech}, // Co-Secans hyperbolicus 0056 {"sech", QString(), sech}, // Secans hyperbolicus 0057 {"coth", QString(), coth}, // Co-Tangens hyperbolicus 0058 {"arccosech", "arcosech", arcosech}, // Area-co-secans hyperbolicus = inverse of cosech 0059 {"arcsech", "arsech", arsech}, // Area-secans hyperbolicus = inverse of sech 0060 {"arccoth", "arcoth", arcoth}, // Area-co-tangens hyperbolicus = inverse of coth 0061 0062 // Reciprocal-trig 0063 {"cosec", QString(), lcosec}, // Co-Secans = 1/sin 0064 {"sec", QString(), lsec}, // Secans = 1/cos 0065 {"cot", QString(), lcot}, // Co-Tangens = 1/tan 0066 {"arccosec", "arcosech", larccosec}, // Arcus co-secans = inverse of cosec 0067 {"arcsec", "arsec", larcsec}, // Arcus secans = inverse of sec 0068 {"arccot", "arcot", larccot}, // Arcus co-tangens = inverse of cotan 0069 0070 // Trigonometric functions 0071 {"sin", QString(), lsin}, // Sinus 0072 {"cos", QString(), lcos}, // Cosinus 0073 {"tan", QString(), ltan}, // Tangens 0074 {"arcsin", QString(), larcsin}, // Arcus sinus = inverse of sin 0075 {"arccos", QString(), larccos}, // Arcus cosinus = inverse of cos 0076 {"arctan", QString(), larctan}, // Arcus tangens = inverse of tan 0077 0078 // Other 0079 {"sqrt", QString(), sqrt}, // Square root 0080 {"sqr", QString(), sqr}, // Square 0081 {"sign", QString(), sign}, // Signum 0082 {"H", QString(), heaviside}, // Heaviside step function 0083 {"log", QString(), log10}, // Logarithm base 10 0084 {"ln", QString(), log}, // Logarithm base e 0085 {"exp", QString(), exp}, // Exponential function base e 0086 {"abs", QString(), fabs}, // Absolute value 0087 {"floor", QString(), floor}, // round down to nearest integer 0088 {"ceil", QString(), ceil}, // round up to nearest integer 0089 {"round", QString(), round}, // round to nearest integer 0090 {"gamma", QString(), tgamma}, // gamma function 0091 {"lgamma", QString(), lgamma}, // log-gamma function 0092 {"factorial", QString(), factorial}, // factorial 0093 {"erfc", QString(), lerfc}, // error function 0094 {"erf", QString(), lerf}, // complementary error function 0095 0096 // legendre 0097 {"P_0", QString(), legendre0}, // lengedre polynomial (n=0) 0098 {"P_1", QString(), legendre1}, // lengedre polynomial (n=1) 0099 {"P_2", QString(), legendre2}, // lengedre polynomial (n=2) 0100 {"P_3", QString(), legendre3}, // lengedre polynomial (n=3) 0101 {"P_4", QString(), legendre4}, // lengedre polynomial (n=4) 0102 {"P_5", QString(), legendre5}, // lengedre polynomial (n=5) 0103 {"P_6", QString(), legendre6}, // lengedre polynomial (n=6) 0104 }; 0105 0106 VectorFunction Parser::vectorFunctions[VectorCount] = { 0107 {"min", min}, // minimum of a set of reals 0108 {"max", max}, // maximum of a set of reals 0109 {"mod", mod}, // l2 modulus of a set of reals 0110 }; 0111 0112 /** 0113 * Order by longest string first, useful in parsing since we want to at each point 0114 * match the longest string first, so e.g. "sinh(x)" shouldn't be read as "sin(h) * x" 0115 */ 0116 class LengthOrderedString : public QString 0117 { 0118 public: 0119 LengthOrderedString() 0120 { 0121 } 0122 LengthOrderedString(const QString &s) 0123 : QString(s) 0124 { 0125 } 0126 0127 bool operator<(const LengthOrderedString &other) const 0128 { 0129 return (length() > other.length()) || ((length() == other.length()) && (static_cast<const QString &>(*this) < static_cast<const QString &>(other))); 0130 } 0131 }; 0132 0133 // BEGIN class Parser 0134 Parser::Parser() 0135 : m_sanitizer(this) 0136 { 0137 m_evalPos = 0; 0138 m_nextFunctionID = 0; 0139 m_stack = new double[STACKSIZE]; 0140 stkptr = m_stack; 0141 m_constants = new Constants; 0142 0143 m_error = 0; 0144 m_ownEquation = 0; 0145 m_currentEquation = 0; 0146 } 0147 0148 Parser::~Parser() 0149 { 0150 for (Function *function : qAsConst(m_ufkt)) 0151 delete function; 0152 delete m_ownEquation; 0153 delete m_constants; 0154 delete[] m_stack; 0155 } 0156 0157 QStringList Parser::predefinedFunctions(bool includeAliases) const 0158 { 0159 QStringList names; 0160 0161 for (int func = 0; func < ScalarCount; ++func) { 0162 names << scalarFunctions[func].name1; 0163 if (includeAliases && !scalarFunctions[func].name2.isEmpty()) 0164 names << scalarFunctions[func].name2; 0165 } 0166 0167 for (int func = 0; func < VectorCount; ++func) 0168 names << vectorFunctions[func].name; 0169 0170 return names; 0171 } 0172 0173 QStringList Parser::userFunctions() const 0174 { 0175 QStringList names; 0176 0177 for (Function *f : qAsConst(m_ufkt)) { 0178 for (Equation *eq : qAsConst(f->eq)) { 0179 if (!eq->name().isEmpty()) 0180 names << eq->name(); 0181 } 0182 } 0183 0184 names.sort(); 0185 return names; 0186 } 0187 0188 void Parser::reparseAllFunctions() 0189 { 0190 for (Function *f : m_ufkt) { 0191 for (Equation *eq : f->eq) 0192 initEquation(eq); 0193 } 0194 } 0195 0196 void Parser::setAngleMode(AngleMode mode) 0197 { 0198 switch (mode) { 0199 case Radians: 0200 m_radiansPerAngleUnit = 1.0; 0201 break; 0202 0203 case Degrees: 0204 m_radiansPerAngleUnit = M_PI / 180; 0205 break; 0206 } 0207 } 0208 0209 uint Parser::getNewId() 0210 { 0211 uint i = m_nextFunctionID; 0212 while (1) { 0213 if (!m_ufkt.contains(i)) { 0214 m_nextFunctionID = i + 1; 0215 return i; 0216 } 0217 ++i; 0218 } 0219 } 0220 0221 double Parser::eval(const QString &str, Error *error, int *errorPosition) 0222 { 0223 Error t1; 0224 if (!error) 0225 error = &t1; 0226 int t2; 0227 if (!errorPosition) 0228 errorPosition = &t2; 0229 0230 if (!m_ownEquation) 0231 m_ownEquation = new Equation(Equation::Constant, 0); 0232 0233 QString fName = XParser::self()->findFunctionName(QStringLiteral("f"), -1); 0234 0235 QString eq = QStringLiteral("%1=%2").arg(fName).arg(str); 0236 if (!m_ownEquation->setFstr(eq, (int *)error, errorPosition)) { 0237 if (errorPosition) 0238 *errorPosition -= fName.length() + 1; 0239 return 0; 0240 } 0241 0242 return fkt(m_ownEquation, Vector()); 0243 } 0244 0245 double Parser::fkt(uint id, int eq, double x) 0246 { 0247 if (!m_ufkt.contains(id) || m_ufkt[id]->eq.size() <= eq) { 0248 *m_error = NoSuchFunction; 0249 return 0; 0250 } 0251 0252 return fkt(m_ufkt[id]->eq[eq], x); 0253 } 0254 0255 double Parser::fkt(Equation *eq, double x) 0256 { 0257 Function *function = eq->parent(); 0258 Q_ASSERT_X(function->type() != Function::Differential, "Parser::fkt", "Do not use this function directly! Instead, call XParser::differential"); 0259 0260 switch (function->type()) { 0261 case Function::Cartesian: 0262 case Function::Parametric: 0263 case Function::Polar: { 0264 Vector var(2); 0265 var[0] = x; 0266 var[1] = function->k; 0267 0268 return fkt(eq, var); 0269 } 0270 0271 case Function::Implicit: { 0272 Vector var(3); 0273 0274 // Can only calculate when one of x, y is fixed 0275 assert(function->m_implicitMode != Function::UnfixedXY); 0276 0277 if (function->m_implicitMode == Function::FixedX) { 0278 var[0] = function->x; 0279 var[1] = x; 0280 } else { 0281 // fixed y 0282 var[0] = x; 0283 var[1] = function->y; 0284 } 0285 var[2] = function->k; 0286 0287 return fkt(eq, var); 0288 } 0289 0290 case Function::Differential: 0291 return 0; 0292 } 0293 0294 qWarning() << "Unknown function type!\n"; 0295 return 0; 0296 } 0297 0298 double Parser::fkt(Equation *eq, const Vector &x) 0299 { 0300 if (eq->mem.isEmpty()) 0301 return 0; 0302 0303 // Consistency check: Make sure that we leave the stkptr at the same place 0304 // that we started it 0305 double *stkInitial = stkptr; 0306 0307 double *pDouble; 0308 double (**pScalarFunction)(double); 0309 double (**pVectorFunction)(const Vector &); 0310 uint *pUint; 0311 eq->mptr = eq->mem.data(); 0312 0313 // Start with zero in our stackpointer 0314 // 0315 *stkptr = 0; 0316 0317 while (1) { 0318 // qDebug() << "*eq->mptr: "<<int(*eq->mptr); 0319 0320 switch (*eq->mptr++) { 0321 case KONST: { 0322 pDouble = (double *)eq->mptr; 0323 *stkptr = *pDouble++; 0324 eq->mptr = (char *)pDouble; 0325 break; 0326 } 0327 0328 case VAR: { 0329 pUint = (uint *)eq->mptr; 0330 uint var = *pUint++; 0331 if (int(var) >= x.size()) { 0332 // Assume variable has value zero 0333 *stkptr = 0; 0334 } else 0335 *stkptr = x[var]; 0336 eq->mptr = (char *)pUint; 0337 break; 0338 } 0339 0340 case PUSH: { 0341 ++stkptr; 0342 break; 0343 } 0344 0345 case PLUS: { 0346 stkptr[-1] += *stkptr; 0347 --stkptr; 0348 break; 0349 } 0350 0351 case MINUS: { 0352 stkptr[-1] -= *stkptr; 0353 --stkptr; 0354 break; 0355 } 0356 0357 case GT: { 0358 stkptr[-1] = (*(stkptr - 1) > *stkptr) ? 1 : 0; 0359 stkptr--; 0360 break; 0361 } 0362 0363 case GE: { 0364 stkptr[-1] = (*(stkptr - 1) >= *stkptr) ? 1 : 0; 0365 stkptr--; 0366 break; 0367 } 0368 0369 case LT: { 0370 stkptr[-1] = (*(stkptr - 1) < *stkptr) ? 1 : 0; 0371 stkptr--; 0372 break; 0373 } 0374 0375 case LE: { 0376 stkptr[-1] = (*(stkptr - 1) <= *stkptr) ? 1 : 0; 0377 stkptr--; 0378 break; 0379 } 0380 0381 case PM: { 0382 pUint = (uint *)eq->mptr; 0383 uint whichPM = *pUint++; 0384 eq->mptr = (char *)pUint; 0385 0386 assert(int(whichPM) < eq->pmSignature().size()); 0387 bool plus = eq->pmSignature()[whichPM]; 0388 0389 if (plus) 0390 stkptr[-1] += *stkptr; 0391 else 0392 stkptr[-1] -= *stkptr; 0393 0394 --stkptr; 0395 break; 0396 } 0397 0398 case MULT: { 0399 stkptr[-1] *= *stkptr; 0400 --stkptr; 0401 break; 0402 } 0403 0404 case DIV: { 0405 if (*stkptr == 0.) 0406 *(--stkptr) = HUGE_VAL; 0407 else { 0408 stkptr[-1] /= *stkptr; 0409 --stkptr; 0410 } 0411 break; 0412 } 0413 0414 case POW: { 0415 stkptr[-1] = pow(*(stkptr - 1), *stkptr); 0416 --stkptr; 0417 break; 0418 } 0419 0420 case NEG: { 0421 *stkptr = -*stkptr; 0422 break; 0423 } 0424 0425 case SQRT: { 0426 *stkptr = sqrt(*stkptr); 0427 break; 0428 } 0429 0430 case FACT: { 0431 *stkptr = factorial(*stkptr); 0432 break; 0433 } 0434 0435 case FKT_1: { 0436 pScalarFunction = (double (**)(double))eq->mptr; 0437 *stkptr = (*pScalarFunction++)(*stkptr); 0438 eq->mptr = (char *)pScalarFunction; 0439 break; 0440 } 0441 0442 case FKT_N: { 0443 pUint = (uint *)eq->mptr; 0444 int numArgs = *pUint++; 0445 0446 eq->mptr = (char *)pUint; 0447 0448 pVectorFunction = (double (**)(const Vector &))eq->mptr; 0449 0450 Vector args(numArgs); 0451 for (int i = 0; i < int(numArgs); ++i) 0452 args[i] = *(stkptr - numArgs + 1 + i); 0453 0454 if (numArgs > 0) 0455 stkptr += 1 - numArgs; 0456 *stkptr = (*pVectorFunction++)(args); 0457 0458 eq->mptr = (char *)pVectorFunction; 0459 break; 0460 } 0461 0462 case UFKT: { 0463 pUint = (uint *)eq->mptr; 0464 uint id = *pUint++; 0465 uint id_eq = *pUint++; 0466 0467 // The number of arguments being passed to the function 0468 int numArgs = *pUint++; 0469 0470 Vector args(numArgs); 0471 for (int i = 0; i < numArgs; ++i) 0472 args[i] = *(stkptr - numArgs + 1 + i); 0473 0474 if (m_ufkt.contains(id)) { 0475 if (numArgs > 0) 0476 stkptr += 1 - numArgs; 0477 *stkptr = fkt(m_ufkt[id]->eq[id_eq], args); 0478 } 0479 0480 eq->mptr = (char *)pUint; 0481 break; 0482 } 0483 0484 case ENDE: { 0485 // If the stack isn't where we started at, then we've gone 0486 // up / down the wrong number of places - definitely a bug (and 0487 // will lead to crashes over time as memory rapidly runs out). 0488 assert(stkptr == stkInitial); 0489 return *stkptr; 0490 } 0491 case ERROR: { 0492 // something went wrong due to a incorrect formular or 0493 // missing const. 0494 qDebug() << "Error in equation " << eq->fstr(); 0495 // Adjust stack again. Stack is wrong, only if we have a PUSH token 0496 // just before the ERROR token, afaik. 0497 while (stkptr != stkInitial) { 0498 stkptr--; 0499 } 0500 return *stkptr; 0501 } 0502 } 0503 } 0504 } 0505 0506 int Parser::addFunction(const QString &str1, const QString &str2, Function::Type type, bool force) 0507 { 0508 QString str[2] = {str1, str2}; 0509 0510 Function *temp = new Function(type); 0511 temp->setId(getNewId()); 0512 0513 for (int i = 0; i < 2; ++i) { 0514 if (str[i].isEmpty() || temp->eq.size() <= i) 0515 continue; 0516 0517 int error; 0518 if (!temp->eq[i]->setFstr(str[i], &error, 0, force) && !force) { 0519 qDebug() << "could not set fstr to \"" << str[i] << "\"! error:" << errorString(Error(error)) << "\n"; 0520 delete temp; 0521 return -1; 0522 } 0523 0524 bool duplicate = (fnameToID(temp->eq[i]->name()) != -1); 0525 if (temp->eq[i]->looksLikeFunction() && duplicate && !force) { 0526 qDebug() << "function name reused.\n"; 0527 *m_error = FunctionNameReused; 0528 delete temp; 0529 return -1; 0530 } 0531 } 0532 0533 m_ufkt[temp->id()] = temp; 0534 0535 temp->plotAppearance(Function::Derivative0).color = XParser::self()->defaultColor(temp->id()); 0536 temp->plotAppearance(Function::Derivative1).color = QColor::fromRgb(QRandomGenerator::global()->generate()); 0537 temp->plotAppearance(Function::Derivative2).color = QColor::fromRgb(QRandomGenerator::global()->generate()); 0538 temp->plotAppearance(Function::Integral).color = QColor::fromRgb(QRandomGenerator::global()->generate()); 0539 0540 emit functionAdded(temp->id()); 0541 return temp->id(); // return the unique ID-number for the function 0542 } 0543 0544 void Parser::initEquation(Equation *eq, Error *error, int *errorPosition) 0545 { 0546 Error t1; 0547 if (!error) 0548 error = &t1; 0549 int t2; 0550 if (!errorPosition) 0551 errorPosition = &t2; 0552 0553 if (eq->parent()) 0554 eq->parent()->clearFunctionDependencies(); 0555 0556 m_error = error; 0557 0558 *m_error = ParseSuccess; 0559 *errorPosition = -1; 0560 0561 m_currentEquation = eq; 0562 mem = &eq->mem; 0563 mptr = mem->data(); 0564 m_pmAt = 0; 0565 0566 m_eval = eq->fstr(); 0567 m_sanitizer.fixExpression(&m_eval); 0568 m_evalRemaining = m_eval; 0569 m_evalPos = m_eval.indexOf('=') + 1; 0570 heir0(); 0571 0572 if (!evalRemaining().isEmpty() && *m_error == ParseSuccess) 0573 *m_error = SyntaxError; 0574 0575 if (*m_error != ParseSuccess) { 0576 *errorPosition = m_sanitizer.realPos(m_evalPos); 0577 qDebug() << "add an error token for " << eq->fstr(); 0578 // add an error token and let the user decide 0579 addToken(ERROR); 0580 } 0581 addToken(ENDE); 0582 } 0583 0584 bool Parser::removeFunction(Function *item) 0585 { 0586 // Build up a list of functions that need to be removed is this function is removed 0587 QList<Function *> toRemove; 0588 QStringList otherRemoveNames; 0589 QList<Function *> newFunctions; // Added since the last iteration 0590 0591 toRemove << item; 0592 newFunctions << item; 0593 0594 while (!newFunctions.isEmpty()) { 0595 const QList<Function *> currentFunctions = newFunctions; 0596 newFunctions.clear(); 0597 0598 for (Function *f : currentFunctions) { 0599 for (Function *other : qAsConst(m_ufkt)) { 0600 if ((other == f) || toRemove.contains(other)) 0601 continue; 0602 0603 if (other->dependsOn(f)) { 0604 toRemove << other; 0605 otherRemoveNames << other->name(); 0606 newFunctions << other; 0607 } 0608 } 0609 } 0610 } 0611 0612 if (toRemove.size() > 1) { 0613 KGuiItem buttonContinue = KStandardGuiItem::cont(); 0614 buttonContinue.setText(i18n("Remove all")); 0615 0616 int answer = 0617 KMessageBox::warningContinueCancel(0, 0618 i18n("The function %1 is depended upon by the following functions: %2. These must be removed in addition.", 0619 item->name(), 0620 otherRemoveNames.join(", ")), 0621 QString(), 0622 buttonContinue); 0623 0624 if (answer == KMessageBox::Cancel) 0625 return false; 0626 } 0627 0628 for (Function *f : qAsConst(toRemove)) { 0629 uint id = f->id(); 0630 m_ufkt.remove(id); 0631 delete f; 0632 emit functionRemoved(id); 0633 } 0634 0635 return true; 0636 } 0637 0638 bool Parser::removeFunction(uint id) 0639 { 0640 return m_ufkt.contains(id) && removeFunction(m_ufkt[id]); 0641 } 0642 0643 void Parser::removeAllFunctions() 0644 { 0645 while (!m_ufkt.isEmpty()) { 0646 Function *f = *m_ufkt.begin(); 0647 int id = f->id(); 0648 m_ufkt.remove(id); 0649 delete f; 0650 emit functionRemoved(id); 0651 } 0652 } 0653 0654 uint Parser::countFunctions() 0655 { 0656 return m_ufkt.count(); 0657 } 0658 0659 void Parser::heir0() 0660 { 0661 heir1(); 0662 0663 if (*m_error != ParseSuccess) 0664 return; 0665 0666 while (1) { 0667 if (m_eval.length() <= m_evalPos) 0668 return; 0669 0670 QChar c = m_eval[m_evalPos]; 0671 0672 switch (c.unicode()) { 0673 default: 0674 return; 0675 0676 case '<': 0677 case '>': 0678 case 0x2264: // less than or equal 0679 case 0x2265: // greater than or equal 0680 ++m_evalPos; 0681 addToken(PUSH); 0682 heir1(); 0683 if (*m_error != ParseSuccess) 0684 return; 0685 } 0686 switch (c.unicode()) { 0687 case '<': 0688 addToken(LT); 0689 break; 0690 0691 case '>': 0692 addToken(GT); 0693 break; 0694 0695 case 0x2264: // less than or equal 0696 addToken(LE); 0697 break; 0698 0699 case 0x2265: // greater than or equal 0700 addToken(GE); 0701 break; 0702 } 0703 } 0704 } 0705 0706 void Parser::heir1() 0707 { 0708 heir2(); 0709 0710 if (*m_error != ParseSuccess) 0711 return; 0712 0713 while (1) { 0714 if (m_eval.length() <= m_evalPos) 0715 return; 0716 0717 QChar c = m_eval[m_evalPos]; 0718 0719 switch (c.unicode()) { 0720 default: 0721 return; 0722 0723 case 0xb1: 0724 if (m_pmAt >= MAX_PM) { 0725 *m_error = TooManyPM; 0726 return; 0727 } 0728 if (m_currentEquation == m_ownEquation) { 0729 *m_error = InvalidPM; 0730 return; 0731 } 0732 // no break 0733 Q_FALLTHROUGH(); 0734 case '+': 0735 case '-': 0736 ++m_evalPos; 0737 addToken(PUSH); 0738 heir2(); 0739 if (*m_error != ParseSuccess) 0740 return; 0741 } 0742 switch (c.unicode()) { 0743 case '+': 0744 addToken(PLUS); 0745 break; 0746 0747 case '-': 0748 addToken(MINUS); 0749 break; 0750 0751 case 0xb1: 0752 addToken(PM); 0753 adduint(m_pmAt++); 0754 break; 0755 } 0756 } 0757 } 0758 0759 void Parser::heir2() 0760 { 0761 if (match(SqrtSymbol)) // square root symbol 0762 { 0763 heir2(); 0764 if (*m_error != ParseSuccess) 0765 return; 0766 addToken(SQRT); 0767 } else 0768 heir3(); 0769 } 0770 0771 void Parser::heir3() 0772 { 0773 QChar c; 0774 heir4(); 0775 if (*m_error != ParseSuccess) 0776 return; 0777 while (1) { 0778 if (m_eval.length() <= m_evalPos) 0779 return; 0780 0781 c = m_eval[m_evalPos]; 0782 switch (c.unicode()) { 0783 default: 0784 return; 0785 case '*': 0786 case '/': 0787 ++m_evalPos; 0788 addToken(PUSH); 0789 heir4(); 0790 if (*m_error != ParseSuccess) 0791 return; 0792 } 0793 switch (c.unicode()) { 0794 case '*': 0795 addToken(MULT); 0796 break; 0797 case '/': 0798 addToken(DIV); 0799 break; 0800 } 0801 } 0802 } 0803 0804 void Parser::heir4() 0805 { 0806 if (match(QStringLiteral("-"))) { 0807 heir4(); 0808 if (*m_error != ParseSuccess) 0809 return; 0810 addToken(NEG); 0811 } else if (match(QStringLiteral("+"))) { 0812 heir4(); 0813 } else { 0814 heir5(); 0815 } 0816 } 0817 0818 void Parser::heir5() 0819 { 0820 primary(); 0821 if (*m_error != ParseSuccess) 0822 return; 0823 0824 while (true) { 0825 if (match(QStringLiteral("^"))) { 0826 addToken(PUSH); 0827 heir4(); 0828 if (*m_error != ParseSuccess) 0829 return; 0830 addToken(POW); 0831 } else if (match(QStringLiteral("!"))) 0832 addToken(FACT); 0833 else 0834 return; 0835 } 0836 } 0837 0838 void Parser::primary() 0839 { 0840 // Notes: 0841 // - tryUserFunction has to go after tryVariable since differential 0842 // equations treat the function name as a variable 0843 // - tryConstant has to go before tryUserFunction. This solves a problem, 0844 // when a function and a constant share the same name 0845 0846 tryFunction() || tryPredefinedFunction() || tryVariable() || tryConstant() || tryUserFunction() || tryNumber(); 0847 } 0848 0849 bool Parser::tryFunction() 0850 { 0851 if (!match(QStringLiteral("(")) && !match(QStringLiteral(","))) 0852 return false; 0853 0854 heir0(); 0855 if (!match(QStringLiteral(")")) && !match(QStringLiteral(","))) 0856 *m_error = MissingBracket; 0857 return true; 0858 } 0859 0860 bool Parser::tryPredefinedFunction() 0861 { 0862 for (int i = 0; i < ScalarCount; ++i) { 0863 if (match(scalarFunctions[i].name1) || match(scalarFunctions[i].name2)) { 0864 primary(); 0865 addToken(FKT_1); 0866 addfptr(scalarFunctions[i].mfadr); 0867 return true; 0868 } 0869 } 0870 for (int i = 0; i < VectorCount; ++i) { 0871 if (match(vectorFunctions[i].name)) { 0872 int argCount = readFunctionArguments(); 0873 0874 addToken(FKT_N); 0875 addfptr(vectorFunctions[i].mfadr, argCount); 0876 return true; 0877 } 0878 } 0879 0880 return false; 0881 } 0882 0883 bool Parser::tryVariable() 0884 { 0885 const QStringList variables = m_currentEquation->variables(); 0886 0887 // Sort the parameters by size, so that when identifying parameters, want to 0888 // match e.g. "ab" before "a" 0889 typedef QMultiMap<int, QString> ISMap; 0890 ISMap sorted; 0891 for (const QString &var : variables) 0892 sorted.insert(-var.length(), var); 0893 0894 for (const QString &var : qAsConst(sorted)) { 0895 if (match(var)) { 0896 addToken(VAR); 0897 adduint(variables.indexOf(var)); 0898 return true; 0899 } 0900 } 0901 0902 return false; 0903 } 0904 0905 bool Parser::tryUserFunction() 0906 { 0907 for (Function *it : qAsConst(m_ufkt)) { 0908 for (int i = 0; i < it->eq.size(); ++i) { 0909 if (!match(it->eq[i]->name())) 0910 continue; 0911 0912 if (it->eq[i] == m_currentEquation || (m_currentEquation && it->dependsOn(m_currentEquation->parent()))) { 0913 *m_error = RecursiveFunctionCall; 0914 return true; 0915 } 0916 0917 int argCount = readFunctionArguments(); 0918 if (argCount != it->eq[i]->variables().size()) { 0919 *m_error = IncorrectArgumentCount; 0920 return true; 0921 } 0922 0923 addToken(UFKT); 0924 addfptr(it->id(), i, argCount); 0925 if (m_currentEquation->parent()) 0926 m_currentEquation->parent()->addFunctionDependency(it); 0927 0928 return true; 0929 } 0930 } 0931 0932 return false; 0933 } 0934 0935 bool Parser::tryConstant() 0936 { 0937 #define CHECK_CONSTANT(a, b) \ 0938 if (match(a)) { \ 0939 addConstant(b); \ 0940 return true; \ 0941 } 0942 0943 ConstantList constants = m_constants->list(Constant::All); 0944 0945 QMap<LengthOrderedString, Constant> orderedConstants; 0946 for (ConstantList::iterator i = constants.begin(); i != constants.end(); ++i) 0947 orderedConstants[i.key()] = i.value(); 0948 0949 for (QMap<LengthOrderedString, Constant>::iterator i = orderedConstants.begin(); i != orderedConstants.end(); ++i) 0950 CHECK_CONSTANT(i.key(), i.value().value.value()); 0951 0952 // Or a predefined constant? 0953 CHECK_CONSTANT("pi", M_PI); 0954 CHECK_CONSTANT(PiSymbol, M_PI); 0955 CHECK_CONSTANT("e", M_E); 0956 CHECK_CONSTANT(InfinitySymbol, std::numeric_limits<double>::infinity()); 0957 0958 return false; 0959 } 0960 0961 bool Parser::tryNumber() 0962 { 0963 QByteArray remaining = evalRemaining().toLatin1(); 0964 char *lptr = remaining.data(); 0965 char *p = 0; 0966 // we converted all to "C" format in fixExpression 0967 char *oldLocale = setlocale(LC_NUMERIC, "C"); 0968 double const w = strtod(lptr, &p); 0969 setlocale(LC_NUMERIC, oldLocale); 0970 if (lptr != p) { 0971 m_evalPos += p - lptr; 0972 addConstant(w); 0973 return true; 0974 } 0975 0976 return false; 0977 } 0978 0979 int Parser::readFunctionArguments() 0980 { 0981 if (!evalRemaining().startsWith('(')) 0982 return 0; 0983 0984 int argCount = 0; 0985 bool argLeft = true; 0986 do { 0987 argCount++; 0988 primary(); 0989 0990 argLeft = m_eval.at(m_evalPos - 1) == ','; 0991 if (argLeft) { 0992 addToken(PUSH); 0993 m_evalPos--; 0994 } 0995 } while (*m_error == ParseSuccess && argLeft && !evalRemaining().isEmpty()); 0996 0997 return argCount; 0998 } 0999 1000 void Parser::growEqMem(int growth) 1001 { 1002 int pos = mptr - mem->data(); 1003 mem->resize(mem->size() + growth); 1004 mptr = mem->data() + pos; 1005 } 1006 1007 void Parser::addToken(Token token) 1008 { 1009 growEqMem(sizeof(Token)); 1010 *mptr++ = token; 1011 } 1012 1013 void Parser::addConstant(double x) 1014 { 1015 addToken(KONST); 1016 1017 growEqMem(sizeof(double)); 1018 double *pd = (double *)mptr; 1019 1020 *pd++ = x; 1021 mptr = (char *)pd; 1022 } 1023 1024 void Parser::adduint(uint x) 1025 { 1026 growEqMem(sizeof(uint)); 1027 uint *p = (uint *)mptr; 1028 *p++ = x; 1029 mptr = (char *)p; 1030 } 1031 1032 void Parser::addfptr(double (*fadr)(double)) 1033 { 1034 typedef double (**sfPtr)(double); 1035 1036 growEqMem(sizeof(sfPtr)); 1037 // double (**pf)(double)=(double(**)(double))mptr; 1038 1039 sfPtr pf = (sfPtr)mptr; 1040 *pf++ = fadr; 1041 mptr = (char *)pf; 1042 } 1043 1044 void Parser::addfptr(double (*fadr)(const Vector &), int argCount) 1045 { 1046 typedef double (**vfPtr)(const Vector &); 1047 1048 growEqMem(sizeof(uint)); 1049 uint *p = (uint *)mptr; 1050 *p++ = argCount; 1051 mptr = (char *)p; 1052 1053 growEqMem(sizeof(vfPtr)); 1054 vfPtr pf = (vfPtr)mptr; 1055 *pf++ = fadr; 1056 mptr = (char *)pf; 1057 } 1058 1059 void Parser::addfptr(uint id, uint eq_id, uint args) 1060 { 1061 growEqMem(3 * sizeof(uint)); 1062 1063 uint *p = (uint *)mptr; 1064 *p++ = id; 1065 *p++ = eq_id; 1066 *p++ = args; 1067 mptr = (char *)p; 1068 } 1069 1070 int Parser::fnameToID(const QString &name) 1071 { 1072 for (Function *it : qAsConst(m_ufkt)) { 1073 for (Equation *eq : qAsConst(it->eq)) { 1074 if (eq->looksLikeFunction() && (name == eq->name())) 1075 return it->id(); 1076 } 1077 } 1078 return -1; // Name not found 1079 } 1080 1081 // static 1082 QString Parser::errorString(Error error) 1083 { 1084 switch (error) { 1085 case ParseSuccess: 1086 return QString(); 1087 1088 case SyntaxError: 1089 return i18n("Syntax error"); 1090 1091 case MissingBracket: 1092 return i18n("Missing parenthesis"); 1093 1094 case StackOverflow: 1095 return i18n("Stack overflow"); 1096 1097 case FunctionNameReused: 1098 return i18n("Name of function is not free"); 1099 1100 case RecursiveFunctionCall: 1101 return i18n("recursive function not allowed"); 1102 1103 case EmptyFunction: 1104 return i18n("Empty function"); 1105 1106 case NoSuchFunction: 1107 return i18n("Function could not be found"); 1108 1109 case ZeroOrder: 1110 return i18n("The differential equation must be at least first-order"); 1111 1112 case TooManyPM: 1113 return i18n("Too many plus-minus symbols"); 1114 1115 case InvalidPM: 1116 return i18n("Invalid plus-minus symbol (expression must be constant)"); 1117 1118 case TooManyArguments: 1119 return i18n("The function has too many arguments"); 1120 1121 case IncorrectArgumentCount: 1122 return i18n("The function does not have the correct number of arguments"); 1123 } 1124 1125 return QString(); 1126 } 1127 1128 void Parser::displayErrorDialog(Error error) 1129 { 1130 QString message(errorString(error)); 1131 if (!message.isEmpty()) 1132 KMessageBox::error(0, message, QStringLiteral("KmPlot")); 1133 } 1134 1135 QString Parser::evalRemaining() 1136 { 1137 /// note changing this code may need to change code in match() as well; similar 1138 int newLength = qMax(0, m_eval.length() - m_evalPos); 1139 if (newLength != m_evalRemaining.length()) 1140 m_evalRemaining = m_eval.right(newLength); 1141 return m_evalRemaining; 1142 } 1143 1144 bool Parser::match(const QString &lit) 1145 { 1146 if (lit.isEmpty()) 1147 return false; 1148 1149 /// note changing this code may need to change code in evalRemaining() as well; similar 1150 // Do we need to update m_evalRemaining ? 1151 int newLength = qMax(0, m_eval.length() - m_evalPos); 1152 if (newLength != m_evalRemaining.length()) 1153 evalRemaining(); 1154 1155 if (!m_evalRemaining.startsWith(lit)) 1156 return false; 1157 1158 m_evalPos += lit.length(); 1159 return true; 1160 } 1161 1162 Function *Parser::functionWithID(int id) const 1163 { 1164 return m_ufkt.contains(id) ? m_ufkt[id] : 0; 1165 } 1166 1167 // static 1168 QString Parser::number(double value) 1169 { 1170 QString str = QString::number(value, 'g', 16); 1171 str.replace('e', QLatin1String("*10^")); 1172 return str; 1173 } 1174 // END class Parser 1175 1176 // BEGIN predefined mathematical functions 1177 double sqr(double x) 1178 { 1179 return x * x; 1180 } 1181 double lsec(double x) 1182 { 1183 return (1 / cos(x * Parser::radiansPerAngleUnit())); 1184 } 1185 double lcosec(double x) 1186 { 1187 return (1 / sin(x * Parser::radiansPerAngleUnit())); 1188 } 1189 double lcot(double x) 1190 { 1191 return (1 / tan(x * Parser::radiansPerAngleUnit())); 1192 } 1193 double larcsec(double x) 1194 { 1195 return acos(1 / x) / Parser::radiansPerAngleUnit(); 1196 } 1197 double larccosec(double x) 1198 { 1199 return asin(1 / x) / Parser::radiansPerAngleUnit(); 1200 } 1201 double larccot(double x) 1202 { 1203 return (M_PI / 2 - atan(x)) / Parser::radiansPerAngleUnit(); 1204 } 1205 double sech(double x) 1206 { 1207 return (1 / cosh(x)); 1208 } 1209 double cosech(double x) 1210 { 1211 return (1 / sinh(x)); 1212 } 1213 double coth(double x) 1214 { 1215 return (1 / tanh(x)); 1216 } 1217 double arsech(double x) 1218 { 1219 return acosh(1 / x); 1220 } 1221 double arcosech(double x) 1222 { 1223 return asinh(1 / x); 1224 } 1225 double arcoth(double x) 1226 { 1227 return atanh(1 / x); 1228 } 1229 double lcos(double x) 1230 { 1231 return cos(x * Parser::radiansPerAngleUnit()); 1232 } 1233 double lsin(double x) 1234 { 1235 return sin(x * Parser::radiansPerAngleUnit()); 1236 } 1237 double ltan(double x) 1238 { 1239 return tan(x * Parser::radiansPerAngleUnit()); 1240 } 1241 double larccos(double x) 1242 { 1243 return acos(x) / Parser::radiansPerAngleUnit(); 1244 } 1245 double larcsin(double x) 1246 { 1247 return asin(x) / Parser::radiansPerAngleUnit(); 1248 } 1249 double larctan(double x) 1250 { 1251 return atan(x) / Parser::radiansPerAngleUnit(); 1252 } 1253 double factorial(double x) 1254 { 1255 return tgamma(x + 1); 1256 } 1257 double legendre0(double) 1258 { 1259 return 1.0; 1260 } 1261 double legendre1(double x) 1262 { 1263 return x; 1264 } 1265 double legendre2(double x) 1266 { 1267 return (3 * x * x - 1) / 2; 1268 } 1269 double legendre3(double x) 1270 { 1271 return (5 * x * x * x - 3 * x) / 2; 1272 } 1273 double legendre4(double x) 1274 { 1275 return (35 * x * x * x * x - 30 * x * x + 3) / 8; 1276 } 1277 double legendre5(double x) 1278 { 1279 return (63 * x * x * x * x * x - 70 * x * x * x + 15 * x) / 8; 1280 } 1281 double legendre6(double x) 1282 { 1283 return (231 * x * x * x * x * x * x - 315 * x * x * x * x + 105 * x * x - 5) / 16; 1284 } 1285 1286 double sign(double x) 1287 { 1288 if (x < 0.) 1289 return -1.; 1290 else if (x > 0.) 1291 return 1.; 1292 return 0.; 1293 } 1294 1295 double heaviside(double x) 1296 { 1297 if (x < 0.0) 1298 return 0.0; 1299 else if (x > 0.0) 1300 return 1.0; 1301 else 1302 return 0.5; 1303 } 1304 1305 double min(const Vector &args) 1306 { 1307 double best = HUGE_VAL; 1308 for (int i = 0; i < args.size(); ++i) { 1309 if (args[i] < best) 1310 best = args[i]; 1311 } 1312 1313 return best; 1314 } 1315 1316 double max(const Vector &args) 1317 { 1318 double best = -HUGE_VAL; 1319 for (int i = 0; i < args.size(); ++i) { 1320 if (args[i] > best) 1321 best = args[i]; 1322 } 1323 1324 return best; 1325 } 1326 1327 double mod(const Vector &args) 1328 { 1329 double squared = 0; 1330 for (int i = 0; i < args.size(); ++i) 1331 squared += args[i] * args[i]; 1332 1333 return std::sqrt(squared); 1334 } 1335 1336 double lerf(double x) 1337 { 1338 return erf(x); 1339 } 1340 1341 double lerfc(double x) 1342 { 1343 return erfc(x); 1344 } 1345 1346 // END predefined mathematical functions 1347 1348 // BEGIN class ExpressionSanitizer 1349 enum StringType { ConstantString, NumberString, UnknownLetter, FunctionString, Other }; 1350 1351 ExpressionSanitizer::ExpressionSanitizer(Parser *parser) 1352 : m_parser(parser) 1353 { 1354 m_str = 0l; 1355 m_decimalSymbol = QLocale().decimalPoint(); 1356 } 1357 1358 void ExpressionSanitizer::fixExpression(QString *str) 1359 { 1360 m_str = str; 1361 1362 m_map.resize(m_str->length()); 1363 for (int i = 0; i < m_str->length(); ++i) 1364 m_map[i] = i; 1365 1366 // greater-equal, less-equal with proper symbols 1367 // note that this must go before the next code for implicit equations, since 1368 // it removes the spurious equals signs 1369 replace(QStringLiteral(">="), GeSymbol); 1370 replace(QStringLiteral("<="), LeSymbol); 1371 1372 // hack for implicit functions: change e.g. "y = x + 2" to "y - (x+2)" so 1373 // that they can be evaluated via equality with zero. 1374 if (str->count('=') > 1) { 1375 int equalsPos = str->lastIndexOf('='); 1376 replace(equalsPos, 1, QStringLiteral("-(")); 1377 append(')'); 1378 } 1379 1380 stripWhiteSpace(); 1381 1382 // make sure all minus-like signs (including the actual unicode minus sign) 1383 // are represented by a dash (unicode 0x002d) 1384 QChar dashes[6] = {QChar(0x2012), QChar(0x2013), QChar(0x2014), QChar(0x2015), QChar(0x2053), QChar(0x2212)}; 1385 for (unsigned i = 0; i < 6; ++i) 1386 replace(dashes[i], '-'); 1387 1388 // replace the proper unicode divide sign by the forward-slash 1389 replace(QChar(0xf7), '/'); 1390 replace(QChar(0x2215), '/'); 1391 1392 // replace the unicode middle-dot for multiplication by the star symbol 1393 replace(QChar(0xd7), '*'); 1394 replace(QChar(0x2219), '*'); 1395 1396 // minus-plus symbol to plus-minus symbol 1397 replace(QChar(0x2213), PmSymbol); 1398 1399 // various power symbols 1400 replace(QChar(0x00B2), QStringLiteral("^2")); 1401 replace(QChar(0x00B3), QStringLiteral("^3")); 1402 replace(QChar(0x2070), QStringLiteral("^0")); 1403 replace(QChar(0x2074), QStringLiteral("^4")); 1404 replace(QChar(0x2075), QStringLiteral("^5")); 1405 replace(QChar(0x2076), QStringLiteral("^6")); 1406 replace(QChar(0x2077), QStringLiteral("^7")); 1407 replace(QChar(0x2078), QStringLiteral("^8")); 1408 replace(QChar(0x2079), QStringLiteral("^9")); 1409 1410 // fractions 1411 replace(QChar(0x00BC), QStringLiteral("(1/4)")); 1412 replace(QChar(0x00BD), QStringLiteral("(1/2)")); 1413 replace(QChar(0x00BE), QStringLiteral("(3/4)")); 1414 replace(QChar(0x2153), QStringLiteral("(1/3)")); 1415 replace(QChar(0x2154), QStringLiteral("(2/3)")); 1416 replace(QChar(0x2155), QStringLiteral("(1/5)")); 1417 replace(QChar(0x2156), QStringLiteral("(2/5)")); 1418 replace(QChar(0x2157), QStringLiteral("(3/5)")); 1419 replace(QChar(0x2158), QStringLiteral("(4/5)")); 1420 replace(QChar(0x2159), QStringLiteral("(1/6)")); 1421 replace(QChar(0x215a), QStringLiteral("(5/6)")); 1422 replace(QChar(0x215b), QStringLiteral("(1/8)")); 1423 replace(QChar(0x215c), QStringLiteral("(3/8)")); 1424 replace(QChar(0x215d), QStringLiteral("(5/8)")); 1425 replace(QChar(0x215e), QStringLiteral("(7/8)")); 1426 1427 // BEGIN replace e.g. |x+2| with abs(x+2) 1428 str->replace(AbsSymbol, '|'); 1429 1430 int maxDepth = str->count('('); 1431 QVector<bool> absAt(maxDepth + 1); 1432 for (int i = 0; i < maxDepth + 1; ++i) 1433 absAt[i] = false; 1434 1435 int depth = 0; 1436 1437 for (int i = 0; i < str->length(); ++i) { 1438 if (str->at(i) == '|') { 1439 if (absAt[depth]) { 1440 // Closing it 1441 replace(i, 1, QStringLiteral(")")); 1442 absAt[depth] = false; 1443 } else { 1444 // Opening it 1445 replace(i, 1, QStringLiteral("abs(")); 1446 i += 3; 1447 absAt[depth] = true; 1448 } 1449 } else if (str->at(i) == '(') 1450 depth++; 1451 else if (str->at(i) == ')') { 1452 depth--; 1453 if (depth < 0) 1454 depth = 0; 1455 } 1456 } 1457 // END replace e.g. |x+2| with abs(x+2) 1458 1459 if (m_decimalSymbol == QLatin1String(",")) 1460 str->replace(QRegularExpression(QStringLiteral("(\\d),(\\d)")), 1461 "\\1.\\2"); // if we use comma as a decimal separator it is reasonable to separate numbers in function arguments by comma with space 1462 else 1463 str->replace(m_decimalSymbol, QLatin1String(".")); // replace the locale decimal symbol with a '.' otherwise 1464 1465 // BEGIN build up strings 1466 QMap<LengthOrderedString, StringType> strings; 1467 1468 const QStringList predefinedFunctions = XParser::self()->predefinedFunctions(true); 1469 for (const QString &f : predefinedFunctions) 1470 strings[f] = FunctionString; 1471 1472 for (Function *it : qAsConst(m_parser->m_ufkt)) { 1473 for (Equation *eq : qAsConst(it->eq)) 1474 strings[eq->name()] = FunctionString; 1475 } 1476 1477 const QStringList constantNames = m_parser->constants()->names(); 1478 for (const QString &name : constantNames) 1479 strings[name] = ConstantString; 1480 strings[QStringLiteral("pi")] = ConstantString; 1481 // END build up strings 1482 1483 strings.remove(QString()); 1484 1485 StringType prevType = Other; 1486 1487 for (int i = 1; i < str->length();) { 1488 int incLength = 1; 1489 StringType currentType = Other; 1490 QChar ch = str->at(i); 1491 1492 QString remaining = str->right(str->length() - i); 1493 1494 QMap<LengthOrderedString, StringType>::const_iterator end = strings.constEnd(); 1495 for (QMap<LengthOrderedString, StringType>::const_iterator it = strings.constBegin(); it != end; ++it) { 1496 if (!remaining.startsWith(it.key())) 1497 continue; 1498 1499 currentType = it.value(); 1500 incLength = it.key().length(); 1501 break; 1502 } 1503 1504 if (currentType == Other) { 1505 if (ch.isNumber() || (ch == '.')) 1506 currentType = NumberString; 1507 } 1508 1509 if (currentType == Other) { 1510 if (ch.isLetter()) 1511 currentType = UnknownLetter; // probably a variable 1512 } 1513 1514 if (((currentType == FunctionString) || (ch == '(') || (currentType == UnknownLetter) || (currentType == ConstantString)) 1515 && ((prevType == NumberString) || (prevType == ConstantString) || (prevType == UnknownLetter) || (str->at(i - 1) == ')'))) { 1516 insert(i, '*'); 1517 incLength++; 1518 } 1519 1520 prevType = currentType; 1521 i += incLength; 1522 } 1523 } 1524 1525 void ExpressionSanitizer::stripWhiteSpace() 1526 { 1527 int i = 0; 1528 1529 while (i < m_str->length()) { 1530 if (m_str->at(i).isSpace()) { 1531 m_str->remove(i, 1); 1532 m_map.remove(i, 1); 1533 } else 1534 i++; 1535 } 1536 } 1537 1538 void ExpressionSanitizer::remove(const QString &str) 1539 { 1540 int at = 0; 1541 1542 do { 1543 at = m_str->indexOf(str, at); 1544 if (at != -1) { 1545 m_map.remove(at, str.length()); 1546 m_str->remove(at, str.length()); 1547 } 1548 } while (at != -1); 1549 } 1550 1551 void ExpressionSanitizer::remove(const QChar &str) 1552 { 1553 remove(QString(str)); 1554 } 1555 1556 void ExpressionSanitizer::replace(QChar before, QChar after) 1557 { 1558 m_str->replace(before, after); 1559 } 1560 1561 void ExpressionSanitizer::replace(QChar before, const QString &after) 1562 { 1563 if (after.isEmpty()) { 1564 remove(before); 1565 return; 1566 } 1567 1568 int at = 0; 1569 1570 do { 1571 at = m_str->indexOf(before, at); 1572 if (at != -1) { 1573 int to = m_map[at]; 1574 for (int i = at + 1; i < at + after.length(); ++i) 1575 m_map.insert(i, to); 1576 1577 m_str->replace(at, 1, after); 1578 at += after.length() - 1; 1579 } 1580 } while (at != -1); 1581 } 1582 1583 void ExpressionSanitizer::replace(int pos, int len, const QString &after) 1584 { 1585 int before = m_map[pos]; 1586 m_map.remove(pos, len); 1587 m_map.insert(pos, after.length(), before); 1588 m_str->replace(pos, len, after); 1589 } 1590 1591 void ExpressionSanitizer::replace(const QString &before, const QString &after) 1592 { 1593 int index; 1594 while ((index = m_str->indexOf(before)) > -1) 1595 replace(index, before.length(), after); 1596 } 1597 1598 void ExpressionSanitizer::insert(int i, QChar ch) 1599 { 1600 m_map.insert(i, m_map[i]); 1601 m_str->insert(i, ch); 1602 } 1603 1604 void ExpressionSanitizer::append(QChar str) 1605 { 1606 m_map.insert(m_map.size(), m_map[m_map.size() - 1]); 1607 m_str->append(str); 1608 } 1609 1610 int ExpressionSanitizer::realPos(int evalPos) 1611 { 1612 if (m_map.isEmpty() || (evalPos < 0)) 1613 return -1; 1614 1615 if (evalPos >= m_map.size()) { 1616 // qWarning() << "evalPos="<<evalPos<<" is out of range.\n"; 1617 // return m_map[ m_map.size() - 1 ]; 1618 return -1; 1619 } 1620 1621 return m_map[evalPos]; 1622 } 1623 1624 void ExpressionSanitizer::displayMap() 1625 { 1626 QString out('\n'); 1627 1628 for (int i = 0; i < m_map.size(); ++i) 1629 out += QStringLiteral("%1").arg(m_map[i], 3); 1630 out += '\n'; 1631 1632 for (int i = 0; i < m_str->length(); ++i) 1633 out += " " + (*m_str)[i]; 1634 out += '\n'; 1635 1636 qDebug() << out; 1637 } 1638 // END class ExpressionSanitizer