File indexing completed on 2024-05-12 17:22:31

0001 // This file is part of the SpeedCrunch project
0002 // Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
0003 // Copyright (C) 2005, 2006 Johan Thelin <e8johan@gmail.com>
0004 // Copyright (C) 2007-2016 @heldercorreia
0005 // Copyright (C) 2009 Wolf Lammen <ookami1@gmx.de>
0006 // Copyright (C) 2014 Tey <teyut@free.fr>
0007 // Copyright (C) 2015 Pol Welter <polwelter@gmail.com>
0008 // Copyright (C) 2015 Hadrien Theveneau <theveneau@gmail.com>
0009 //
0010 // This program is free software; you can redistribute it and/or
0011 // modify it under the terms of the GNU General Public License
0012 // as published by the Free Software Foundation; either version 2
0013 // of the License, or (at your option) any later version.
0014 //
0015 // This program is distributed in the hope that it will be useful,
0016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018 // GNU General Public License for more details.
0019 //
0020 // You should have received a copy of the GNU General Public License
0021 // along with this program; see the file COPYING.  If not, write to
0022 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0023 // Boston, MA 02110-1301, USA.
0024 
0025 #include "evaluator.h"
0026 #include "settings.h"
0027 #include "rational.h"
0028 #include "units.h"
0029 
0030 #include <KLocalizedString>
0031 
0032 #include <QCoreApplication>
0033 #include <QRegularExpression>
0034 #include <QStack>
0035 
0036 #define ALLOW_IMPLICIT_MULT
0037 
0038 static constexpr int MAX_PRECEDENCE = INT_MAX;
0039 static constexpr int INVALID_PRECEDENCE = INT_MIN;
0040 
0041 #ifdef EVALUATOR_DEBUG
0042 #include <QDebug>
0043 #include <QFile>
0044 #include <QTextStream>
0045 
0046 QTextStream& operator<<(QTextStream& s, Quantity num)
0047 {
0048     s << DMath::format(num, Quantity::Format::Fixed());
0049     return s;
0050 }
0051 #endif // EVALUATOR_DEBUG
0052 
0053 bool isMinus(const QChar& ch)
0054 {
0055     return ch == QLatin1Char('-') || ch == QChar(0x2212);
0056 }
0057 
0058 bool isExponent(const QChar& ch, int base)
0059 {
0060     switch (base) {
0061         case 2:
0062             return ch == QLatin1Char('b') || ch == QLatin1Char('B');
0063         case 8:
0064             return ch == QLatin1Char('o') || ch == QLatin1Char('O') || ch == QLatin1Char('C');
0065         case 10:
0066             return ch == QLatin1Char('e') || ch == QLatin1Char('E');
0067         case 16:
0068             return ch == QLatin1Char('h') || ch == QLatin1Char('H');
0069         default:
0070             return false;
0071     }
0072 }
0073 
0074 const Quantity& Evaluator::checkOperatorResult(const Quantity& n)
0075 {
0076     switch (n.error()) {
0077     case Success: break;
0078     case NoOperand:
0079         if (!m_assignFunc)
0080             // The arguments are still NaN, so ignore this error.
0081             m_error = i18n("cannot operate on a NaN");
0082         break;
0083     case Underflow:
0084         m_error = i18n("underflow - tiny result is out number range");
0085         break;
0086     case Overflow:
0087         m_error = i18n("overflow - huge result is out of number range");
0088         break;
0089     case ZeroDivide:
0090         m_error = i18n("division by zero");
0091         break;
0092     case OutOfLogicRange:
0093         m_error = i18n("overflow - logic result exceeds "
0094                                 "maximum of 256 bits");
0095         break;
0096     case OutOfIntegerRange:
0097         m_error = i18n("overflow - integer result exceeds "
0098                                 "maximum limit for integers");
0099         break;
0100     case TooExpensive:
0101         m_error = i18n("too time consuming - "
0102                                 "computation was rejected");
0103         break;
0104     case DimensionMismatch:
0105         // We cannot make any assumptions about the dimension of the arguments,
0106         // so ignore this error when assigning a function.
0107         if (!m_assignFunc)
0108             m_error = i18n("dimension mismatch - quantities with "
0109                                     "different dimensions cannot be "
0110                                     "compared, added, etc.");
0111         break;
0112     case InvalidDimension:
0113         m_error = i18n("invalid dimension - operation might "
0114                                 "require dimensionless arguments");
0115         break;
0116     case EvalUnstable:
0117         m_error = i18n("Computation aborted - encountered "
0118                                 "numerical instability");
0119         break;
0120     default:;
0121     }
0122 
0123     return n;
0124 }
0125 
0126 QString Evaluator::stringFromFunctionError(Function* function)
0127 {
0128     if (!function->error())
0129         return QString();
0130 
0131     QString result = QString::fromLatin1("%1: ");
0132 
0133     switch (function->error()) {
0134     case Success: break;
0135     case InvalidParamCount:
0136         result += i18n("wrong number of arguments");
0137         break;
0138     case NoOperand:
0139         result += i18n("does not take NaN as an argument");
0140         break;
0141     case Overflow:
0142         result += i18n("overflow - huge result is out of number range");
0143         break;
0144     case Underflow:
0145         result += i18n("underflow - tiny result is out of number range");
0146         break;
0147     case OutOfLogicRange:
0148         result += i18n("overflow - logic result exceeds "
0149                                 "maximum of 256 bits");
0150         break;
0151     case OutOfIntegerRange:
0152         result += i18n("result out of range");
0153         break;
0154     case ZeroDivide:
0155         result += i18n("division by zero");
0156         break;
0157     case EvalUnstable:
0158         result += i18n("Computation aborted - "
0159                                 "encountered numerical instability");
0160         break;
0161     case OutOfDomain:
0162         result += i18n("undefined for argument domain");
0163         break;
0164     case TooExpensive:
0165         result += i18n("computation too expensive");
0166         break;
0167     case InvalidDimension:
0168         result += i18n("invalid dimension - function "
0169                                 "might require dimensionless arguments");
0170         break;
0171     case DimensionMismatch:
0172         result += i18n("dimension mismatch - quantities with "
0173                                 "different dimensions cannot be compared, "
0174                                 "added, etc.");
0175         break;
0176     case IONoBase:
0177     case BadLiteral:
0178     case InvalidPrecision:
0179     case InvalidParam:
0180         result += i18nc("Internal error in the calculator", "internal error, please report a bug");
0181         break;
0182     default:
0183         result += i18nc("Unknown calculation function error", "error");
0184         break;
0185     };
0186 
0187     return result.arg(function->identifier());
0188 }
0189 
0190 class TokenStack : public QVector<Token> {
0191 public:
0192     TokenStack();
0193     bool isEmpty() const;
0194     unsigned itemCount() const;
0195     Token pop();
0196     void push(const Token& token);
0197     const Token& top();
0198     const Token& top(unsigned index);
0199     bool hasError() const { return !m_error.isEmpty(); }
0200     QString error() const { return m_error; }
0201     void reduce(int count, int minPrecedence = INVALID_PRECEDENCE);
0202     void reduce(int count, Token&& top, int minPrecedence = INVALID_PRECEDENCE);
0203     void reduce(QList<Token> tokens, Token&& top,
0204                 int minPrecedence = INVALID_PRECEDENCE);
0205 private:
0206     void ensureSpace();
0207     int topIndex;
0208     QString m_error;
0209 };
0210 
0211 const Token Token::null;
0212 
0213 static Token::Operator matchOperator(const QString& text)
0214 {
0215     Token::Operator result = Token::Invalid;
0216 
0217     if (text.length() == 1) {
0218         QChar p = text.at(0);
0219         switch(p.unicode()) {
0220         case '+':
0221             result = Token::Addition;
0222             break;
0223         case 0x2212: // − MINUS SIGN
0224         case '-':
0225             result = Token::Subtraction;
0226             break;
0227         case 0x00D7: // × MULTIPLICATION SIGN
0228         case 0x22C5: // ⋅ DOT OPERATOR
0229         case '*':
0230             result = Token::Multiplication;
0231             break;
0232         case 0x00F7: // ÷ DIVISION SIGN
0233         case '/':
0234             result = Token::Division;
0235             break;
0236         case '^':
0237             result = Token::Exponentiation;
0238             break;
0239         case ';':
0240             result = Token::ListSeparator;
0241             break;
0242         case '(':
0243             result = Token::AssociationStart;
0244             break;
0245         case ')':
0246             result = Token::AssociationEnd;
0247             break;
0248         case '!':
0249             result = Token::Factorial;
0250             break;
0251         case '=':
0252             result = Token::Assignment;
0253             break;
0254         case '\\':
0255             result = Token::IntegerDivision;
0256             break;
0257         case '&':
0258             result = Token::BitwiseLogicalAND;
0259             break;
0260         case '|':
0261             result = Token::BitwiseLogicalOR;
0262             break;
0263         default:
0264             result = Token::Invalid;
0265         }
0266 
0267     } else if (text.length() == 2) {
0268         if (text == "**")
0269             result = Token::Exponentiation;
0270         else if (text == "<<")
0271           result = Token::ArithmeticLeftShift;
0272         else if (text == ">>")
0273           result = Token::ArithmeticRightShift;
0274         else if (text == "->" || text == "in")
0275             result = Token::UnitConversion;
0276     }
0277 
0278    return result;
0279 }
0280 
0281 // Helper function: give operator precedence e.g. "+" is 300 while "*" is 500.
0282 static int opPrecedence(Token::Operator op)
0283 {
0284     int prec;
0285     switch (op) {
0286     case Token::Factorial:
0287         prec = 800;
0288         break;
0289     case Token::Exponentiation:
0290         prec = 700;
0291         break;
0292     case Token::Function:
0293         // Not really operator but needed for managing shift/reduce conflicts.
0294         prec = 600;
0295         break;
0296     case Token::Multiplication:
0297     case Token::Division:
0298         prec = 500;
0299         break;
0300     case Token::Modulo:
0301     case Token::IntegerDivision:
0302         prec = 600;
0303         break;
0304     case Token::Addition:
0305     case Token::Subtraction:
0306         prec = 300;
0307         break;
0308     case Token::ArithmeticLeftShift:
0309     case Token::ArithmeticRightShift:
0310         prec = 200;
0311         break;
0312     case Token::BitwiseLogicalAND:
0313         prec = 100;
0314         break;
0315     case Token::BitwiseLogicalOR:
0316         prec = 50;
0317         break;
0318     case Token::UnitConversion:
0319         prec = 0;
0320         break;
0321     case Token::AssociationEnd:
0322     case Token::ListSeparator:
0323         prec = -100;
0324         break;
0325     case Token::AssociationStart:
0326         prec = -200;
0327         break;
0328     default:
0329         prec = -200;
0330         break;
0331     }
0332     return prec;
0333 }
0334 
0335 Token::Token(Type type, const QString& text, int pos, int size)
0336 {
0337     m_type = type;
0338     m_text = text;
0339     m_pos = pos;
0340     m_size = size;
0341     m_minPrecedence = MAX_PRECEDENCE;
0342 }
0343 
0344 Token::Token(const Token& token)
0345 {
0346     m_type = token.m_type;
0347     m_text = token.m_text;
0348     m_pos = token.m_pos;
0349     m_size = token.m_size;
0350     m_minPrecedence = token.m_minPrecedence;
0351 }
0352 
0353 Token& Token::operator=(const Token& token)
0354 {
0355     m_type = token.m_type;
0356     m_text = token.m_text;
0357     m_pos = token.m_pos;
0358     m_size = token.m_size;
0359     m_minPrecedence = token.m_minPrecedence;
0360     return*this;
0361 }
0362 
0363 Quantity Token::asNumber() const
0364 {
0365     QString text = m_text;
0366     return isNumber() ? Quantity(CNumber((const char*)text.toLatin1()))
0367                       : Quantity(0);
0368 }
0369 
0370 Token::Operator Token::asOperator() const
0371 {
0372     return isOperator() ? matchOperator(m_text) : Invalid;
0373 }
0374 
0375 QString Token::description() const
0376 {
0377     QString desc;
0378 
0379     switch (m_type) {
0380     case stxNumber:
0381         desc = "Number";
0382         break;
0383     case stxIdentifier:
0384         desc = "Identifier";
0385         break;
0386     case stxOpenPar:
0387     case stxClosePar:
0388     case stxSep:
0389     case stxOperator:
0390         desc = "Operator";
0391         break;
0392     default:
0393         desc = "Unknown";
0394         break;
0395     }
0396 
0397     while (desc.length() < 10)
0398         desc.prepend(' ');
0399 
0400     QString header;
0401     header.append(QString::number(m_pos) + ","
0402                   + QString::number(m_pos + m_size - 1));
0403     header.append("," + (m_minPrecedence == MAX_PRECEDENCE ?
0404                              "MAX" : QString::number(m_minPrecedence)));
0405     header.append("  ");
0406 
0407     while (header.length() < 10)
0408         header.append(' ');
0409 
0410     desc.prepend(header);
0411     desc.append(" : ").append(m_text);
0412 
0413     return desc;
0414 }
0415 
0416 static bool tokenPositionCompare(const Token& a, const Token& b)
0417 {
0418     return (a.pos() < b.pos());
0419 }
0420 
0421 TokenStack::TokenStack() : QVector<Token>()
0422 {
0423     topIndex = 0;
0424     m_error = "";
0425     ensureSpace();
0426 }
0427 
0428 bool TokenStack::isEmpty() const
0429 {
0430     return topIndex == 0;
0431 }
0432 
0433 unsigned TokenStack::itemCount() const
0434 {
0435     return topIndex;
0436 }
0437 
0438 void TokenStack::push(const Token& token)
0439 {
0440     ensureSpace();
0441     (*this)[topIndex++] = token;
0442 }
0443 
0444 Token TokenStack::pop()
0445 {
0446     if (topIndex > 0)
0447         return Token(at(--topIndex));
0448 
0449     m_error = "token stack is empty (BUG)";
0450     return Token();
0451 }
0452 
0453 const Token& TokenStack::top()
0454 {
0455     return top(0);
0456 }
0457 
0458 const Token& TokenStack::top(unsigned index)
0459 {
0460     return (topIndex > (int)index) ? at(topIndex - index - 1) : Token::null;
0461 }
0462 
0463 void TokenStack::ensureSpace()
0464 {
0465     int length = size();
0466     while (topIndex >= length) {
0467         length += 10;
0468         resize(length);
0469     }
0470 }
0471 
0472 /** Remove \a count tokens from the top of the stack, add a stxAbstract token
0473  * to the top and adjust its text position and minimum precedence.
0474  *
0475  * \param minPrecedence minimum precedence to set the top token, or
0476  * \c INVALID_PRECEDENCE if this method should use the minimum value from
0477  * the removed tokens.
0478  */
0479 void TokenStack::reduce(int count, int minPrecedence)
0480 {
0481     // assert(itemCount() > count);
0482 
0483     QList<Token> tokens;
0484     for (int i = 0 ; i < count ; ++i)
0485         tokens.append(pop());
0486 
0487     reduce(tokens, Token(Token::stxAbstract), minPrecedence);
0488 }
0489 
0490 /** Remove \a count tokens from the top of the stack, push \a top to the top
0491  * and adjust its text position and minimum precedence.
0492  *
0493  * \param minPrecedence minimum precedence to set the top token, or
0494  * \c INVALID_PRECEDENCE if this method should use the minimum value
0495  * from the removed tokens.
0496  */
0497 void TokenStack::reduce(int count, Token&& top, int minPrecedence)
0498 {
0499     // assert(itemCount() >= count);
0500 
0501     QList<Token> tokens;
0502     for (int i = 0 ; i < count ; ++i)
0503         tokens.append(pop());
0504 
0505     reduce(tokens, std::forward<Token>(top), minPrecedence);
0506 }
0507 
0508 /** Push \a top to the top and adjust its text position and minimum precedence
0509  * using \a tokens.
0510  *
0511  * \param minPrecedence minimum precedence to set the top token, or
0512  * \c INVALID_PRECEDENCE if this method should use the minimum value from the
0513  * removed tokens.
0514  */
0515 void TokenStack::reduce(QList<Token> tokens, Token&& top, int minPrecedence)
0516 {
0517 #ifdef EVALUATOR_DEBUG
0518     {
0519         const auto& _tokens = tokens;
0520         qDebug() << "reduce(" << _tokens.size() << ", " << top.description()
0521                  << ", " << minPrecedence << ")";
0522         for (const auto& t : _tokens)
0523             qDebug() << t.description();
0524     }
0525 #endif // EVALUATOR_DEBUG
0526 
0527     std::sort(tokens.begin(), tokens.end(), tokenPositionCompare);
0528 
0529     bool computeMinPrec = (minPrecedence == INVALID_PRECEDENCE);
0530     int min_prec = computeMinPrec ? MAX_PRECEDENCE : minPrecedence;
0531     int start = -1, end = -1;
0532     const auto& _tokens = tokens;
0533     for (auto& token : _tokens) {
0534         if (computeMinPrec) {
0535             Token::Operator op = token.asOperator();
0536             if (op != Token::Invalid) {
0537                 int prec = opPrecedence(op);
0538                 if (prec < min_prec)
0539                     min_prec = prec;
0540             }
0541         }
0542 
0543         if (token.pos() == -1 && token.size() == -1)
0544             continue;
0545 
0546         if (token.pos() == -1 || token.size() == -1) {
0547 
0548 #ifdef EVALUATOR_DEBUG
0549             qDebug() << "BUG: found token with either pos or size not set, "
0550                         "but not both.";
0551 #endif  // EVALUATOR_DEBUG
0552             continue;
0553         }
0554 
0555         if (start == -1) {
0556             start = token.pos();
0557         } else {
0558 
0559 #ifdef EVALUATOR_DEBUG
0560             if (token.pos() != end)
0561                 qDebug() << "BUG: tokens expressions are not successive.";
0562 #endif  // EVALUATOR_DEBUG
0563 
0564         }
0565 
0566         end = token.pos() + token.size();
0567     }
0568 
0569     if (start != -1) {
0570         top.setPos(start);
0571         top.setSize(end - start);
0572     }
0573 
0574     top.setMinPrecedence(min_prec);
0575 
0576 #ifdef EVALUATOR_DEBUG
0577     qDebug() << "=> " << top.description();
0578 #endif  // EVALUATOR_DEBUG
0579 
0580     push(top);
0581 }
0582 
0583 #ifdef EVALUATOR_DEBUG
0584 void Tokens::append(const Token& token)
0585 {
0586     qDebug() << QString("tokens.append: type=%1 text=%2")
0587                     .arg(token.type())
0588                     .arg(token.text());
0589     QVector<Token>::append(token);
0590 }
0591 #endif // EVALUATOR_DEBUG
0592 
0593 // Helper function: return true for valid identifier character.
0594 static bool isIdentifier(QChar ch)
0595 {
0596     return ch.unicode() == '_' || ch.unicode() == '$' || ch.isLetter();
0597 }
0598 
0599 // Helper function: return true for valid radix characters.
0600 bool Evaluator::isRadixChar(const QChar& ch)
0601 {
0602     if (Settings::instance()->isRadixCharacterBoth())
0603         return ch.unicode() == '.' || ch.unicode() == ',';
0604 
0605     // There are more than 2 radix characters, actually:
0606     //     U+0027 ' apostrophe
0607     //     U+002C , comma
0608     //     U+002E . full stop
0609     //     U+00B7 · middle dot
0610     //     U+066B ٫ arabic decimal separator
0611     //     U+2396 ⎖ decimal separator key symbol
0612 
0613     return ch.unicode() == Settings::instance()->radixCharacter();
0614 }
0615 
0616 // Helper function: return true for valid thousand separator characters.
0617 bool Evaluator::isSeparatorChar(const QChar& ch)
0618 {
0619     // Match everything that is not alphanumeric or an operator or NUL.
0620     static const QRegExp s_separatorRE(
0621         "[^a-zA-Z0-9\\+\\-−\\*×⋅÷/\\^;\\(\\)%!=\\\\&\\|<>\\?#\\x0000]"
0622     );
0623 
0624     if (isRadixChar(ch))
0625         return false;
0626 
0627     return s_separatorRE.exactMatch(ch);
0628 }
0629 
0630 QString Evaluator::fixNumberRadix(const QString& number)
0631 {
0632     int dotCount = 0;
0633     int commaCount = 0;
0634     QChar lastRadixChar;
0635 
0636     // First pass: count the number of dot and comma characters.
0637     for (int i = 0 ; i < number.size() ; ++i) {
0638         QChar c = number[i];
0639         if (isRadixChar(c)) {
0640             lastRadixChar = c;
0641 
0642             if (c == '.')
0643                 ++dotCount;
0644             else if (c == ',')
0645                 ++commaCount;
0646             else
0647                 return QString(); // Should not happen.
0648         }
0649     }
0650 
0651     // Decide which radix characters to ignore based on their occurence count.
0652     bool ignoreDot = dotCount != 1;
0653     bool ignoreComma = commaCount != 1;
0654     if (!ignoreDot && !ignoreComma) {
0655         // If both radix characters are present once,
0656         // consider the last one as the radix point.
0657         ignoreDot = lastRadixChar != '.';
0658         ignoreComma = lastRadixChar != ',';
0659     }
0660 
0661     QChar radixChar; // Null character by default.
0662     if (!ignoreDot)
0663         radixChar = '.';
0664     else if (!ignoreComma)
0665         radixChar = ',';
0666 
0667     // Second pass: write the result.
0668     QString result = "";
0669     for (int i = 0 ; i < number.size() ; ++i) {
0670         QChar c = number[i];
0671         if (isRadixChar(c)) {
0672             if (c == radixChar)
0673                 result.append('.');
0674         } else
0675           result.append(c);
0676     }
0677 
0678     return result;
0679 }
0680 
0681 Evaluator* Evaluator::instance()
0682 {
0683     static Evaluator inst;
0684     return &inst;
0685 //    if (!s_evaluatorInstance) {
0686 //        s_evaluatorInstance = new Evaluator;
0687 //        qAddPostRoutine(s_deleteEvaluator);
0688 //    }
0689 //    return s_evaluatorInstance;
0690 }
0691 
0692 Evaluator::Evaluator()
0693 {
0694     reset();
0695 }
0696 
0697 void Evaluator::initializeBuiltInVariables()
0698 {
0699     m_variables[QLatin1String("e")] = DMath::e();
0700     m_variables[QString::fromUtf8("ℯ")] = DMath::e();
0701 
0702     m_variables[QLatin1String("pi")] = DMath::pi();
0703     m_variables[QString::fromUtf8("π")] = DMath::pi();
0704 
0705     m_variables[QLatin1String("j")] = DMath::i();
0706 
0707     QList<Unit> unitList(Units::getList());
0708     for (Unit& u : unitList) {
0709         m_variables[u.name] = u.value;
0710         m_allUnits.insert(u.name);
0711         const QString fixed = u.name.toLower().replace('_', ' ');
0712         if (fixed != u.name) {
0713             m_unitFixups[fixed] = u.name;
0714         }
0715     }
0716 
0717     initializeAngleUnits();
0718 }
0719 
0720 void Evaluator::initializeAngleUnits()
0721 {
0722     if (Settings::instance()->angleUnit == 'r') {
0723         m_variables["radian"] = 1;
0724         m_variables["degree"] = HMath::pi() / HNumber(180);
0725         m_variables["gradian"] = HMath::pi() / HNumber(200);
0726         m_variables["gon"] = HMath::pi() / HNumber(200);
0727     } else if (Settings::instance()->angleUnit == 'g') {
0728         m_variables["radian"] = HNumber(200) / HMath::pi();
0729         m_variables["degree"] = HNumber(200) / HNumber(180);
0730         m_variables["gradian"] = 1;
0731         m_variables["gon"] = 1;
0732     } else {    // d
0733         m_variables["radian"] = HNumber(180) / HMath::pi();
0734         m_variables["degree"] = 1;
0735         m_variables["gradian"] = HNumber(180) / HNumber(200);
0736         m_variables["gon"] = HNumber(180) / HNumber(200);
0737     }
0738 }
0739 
0740 void Evaluator::setExpression(const QString& expr)
0741 {
0742     m_expression = expr;
0743     m_dirty = true;
0744     m_valid = false;
0745     m_error = QString();
0746 }
0747 
0748 QString Evaluator::expression() const
0749 {
0750     return m_expression;
0751 }
0752 
0753 // Returns the validity. Note: empty expression is always invalid.
0754 bool Evaluator::isValid()
0755 {
0756     if (m_dirty) {
0757         Tokens tokens = scan(m_expression);
0758         if (!tokens.valid())
0759             compile(tokens);
0760         else
0761             m_valid = false;
0762     }
0763     return m_valid;
0764 }
0765 
0766 void Evaluator::reset()
0767 {
0768     m_expression = QString();
0769     m_dirty = true;
0770     m_valid = false;
0771     m_error = QString();
0772     m_constants.clear();
0773     m_codes.clear();
0774     m_assignId = QString();
0775     m_assignFunc = false;
0776     m_assignArg.clear();
0777     m_functionsInUse.clear();
0778 
0779     initializeBuiltInVariables();
0780 }
0781 
0782 
0783 QString Evaluator::error() const
0784 {
0785     return m_error;
0786 }
0787 
0788 // Returns list of token for the expression.
0789 // This triggers again the lexical analysis step. It is however preferable
0790 // (even when there's small performance penalty) because otherwise we need to
0791 // store parsed tokens all the time which serves no good purpose.
0792 Tokens Evaluator::tokens() const
0793 {
0794     return scan(m_expression);
0795 }
0796 
0797 Tokens Evaluator::scan(const QString& expr) const
0798 {
0799     // Associate character codes with the highest number base
0800     // they might belong to.
0801     constexpr unsigned DIGIT_MAP_COUNT = 128;
0802     static unsigned char s_digitMap[DIGIT_MAP_COUNT] = { 0 };
0803 
0804     if (s_digitMap[0] == 0) {
0805         // Initialize the digits map.
0806         std::fill_n(s_digitMap, DIGIT_MAP_COUNT, 255);
0807         for (int i = '0' ; i <= '9' ; ++i)
0808             s_digitMap[i] = i - '0' + 1;
0809         for (int i = 'a' ; i <= 'z' ; ++i)
0810             s_digitMap[i] = i - 'a' + 11;
0811         for (int i = 'A' ; i <= 'Z' ; ++i)
0812             s_digitMap[i] = i - 'A' + 11;
0813     }
0814 
0815     // Result.
0816     Tokens tokens;
0817 
0818     // Parsing state.
0819     enum {
0820         Init, Start, Finish, Bad, InNumberPrefix, InNumber, InExpIndicator,
0821         InExponentBase, InExponent, InIdentifier, InNumberEnd
0822     } state;
0823 
0824     // Initialize variables.
0825     state = Init;
0826     int i = 0;
0827     QString ex = expr;
0828     QString tokenText;
0829     int tokenStart = 0; // Includes leading spaces.
0830     Token::Type type;
0831     int numberBase = 10;
0832     int expBase = 0;
0833     int expStart = -1;  // Index of the exponent part in the expression.
0834     QString expText;    // Start of the exponent text matching /E[\+\-]*/
0835 
0836     // Force a terminator.
0837     ex.append(QChar());
0838 
0839 #ifdef EVALUATOR_DEBUG
0840     qDebug() << "Scanning" << ex;
0841 #endif // EVALUATOR_DEBUG
0842 
0843     // Main loop.
0844     while (state != Bad && state != Finish && i < ex.length()) {
0845         QChar ch = ex.at(i);
0846 
0847 #ifdef EVALUATOR_DEBUG
0848         qDebug() << QString("state=%1 ch=%2 i=%3 tokenText=%4")
0849                             .arg(state).arg(ch).arg(i).arg(tokenText);
0850 #endif // EVALUATOR_DEBUG
0851 
0852         switch (state) {
0853         case Init:
0854             tokenStart = i;
0855             tokenText = "";
0856             state = Start;
0857 
0858             // State variables reset
0859             expStart = -1;
0860             expText = "";
0861             numberBase = 10;
0862             expBase = 0;
0863 
0864 
0865             // Make sure Start is the next case,
0866             // fallthrough
0867         case Start:
0868             // Skip any whitespaces.
0869             if (ch.isSpace())
0870                 ++i;
0871             else if (ch == '?') // Comment.
0872                 state = Finish;
0873             else if (ch.isDigit()) {
0874                 // Check for number
0875                 state = InNumberPrefix;
0876             } else if (ch == '#') {
0877                 // Simple hexadecimal notation
0878                 tokenText.append("0x");
0879                 numberBase = 16;
0880                 state = InNumber;
0881                 ++i;
0882             } else if (isRadixChar(ch)) {
0883                 // Radix character?
0884                 tokenText.append(ch);
0885                 numberBase = 10;
0886                 state = InNumber;
0887                 ++i;
0888             } else if (isSeparatorChar(ch)) {
0889                 // Leading separator, probably a number
0890                 state = InNumberPrefix;
0891             } else if (ch.isNull()) // Terminator character.
0892                 state = Finish;
0893             else if (isIdentifier(ch)) // Identifier or alphanumeric operator
0894                 state = InIdentifier;
0895             else { // Look for operator match.
0896                 int op;
0897                 QString s;
0898                 s = QString(ch).append(ex.at(i+1));
0899                 op = matchOperator(s);
0900                 // Check for one-char operator.
0901                 if (op == Token::Invalid) {
0902                     s = QString(ch);
0903                     op = matchOperator(s);
0904                 }
0905                 // Any matched operator?
0906                 if (op != Token::Invalid) {
0907                     switch(op) {
0908                         case Token::AssociationStart:
0909                             type = Token::stxOpenPar;
0910                             break;
0911                         case Token::AssociationEnd:
0912                             type = Token::stxClosePar;
0913                             break;
0914                         case Token::ListSeparator:
0915                             type = Token::stxSep;
0916                             break;
0917                         default: type = Token::stxOperator;
0918                     }
0919                     int len = s.length();
0920                     i += len;
0921                     int tokenSize = i - tokenStart;
0922                     tokens.append(Token(type, s.left(len),
0923                                         tokenStart, tokenSize));
0924                     state = Init;
0925                 }
0926                 else
0927                     state = Bad;
0928             }
0929             break;
0930 
0931         // Manage both identifier and alphanumeric operators.
0932         case InIdentifier:
0933             // Consume as long as alpha, dollar sign, underscore, or digit.
0934             if (isIdentifier(ch) || ch.isDigit())
0935                 tokenText.append(ex.at(i++));
0936             else { // We're done with identifier.
0937                 int tokenSize = i - tokenStart;
0938                 if (matchOperator(tokenText)) {
0939                     tokens.append(Token(Token::stxOperator, tokenText,
0940                                         tokenStart, tokenSize));
0941                 } else {
0942                     // Normal identifier.
0943                     tokens.append(Token(Token::stxIdentifier, tokenText,
0944                                         tokenStart, tokenSize));
0945                 }
0946                 state = Init;
0947             }
0948             break;
0949 
0950         // Find out the number base.
0951         case InNumberPrefix:
0952             if (ch.isDigit()) {
0953                 // Only consume the first digit and the second digit
0954                 // if the first was 0.
0955                 tokenText.append(ex.at(i++));
0956                 if (tokenText != "0") {
0957                     numberBase = 10;
0958                     state = InNumber;
0959                 }
0960             } else if (isExponent(ch, numberBase)) {
0961                 if (tokenText.endsWith("0")) {
0962                     // Maybe exponent (tokenText is "0" or "-0").
0963                     numberBase = 10;
0964                     expText = ch.toUpper();
0965                     expStart = i;
0966                     ++i;
0967                     state = InExpIndicator;
0968                 } else {
0969                     // Only leading separators.
0970                     state = Bad;
0971                 }
0972             } else if (isRadixChar(ch)) {
0973                 // Might be a radix point or a separator.
0974                 // Collect it and decide later.
0975                 tokenText.append(ch);
0976                 numberBase = 10;
0977                 state = InNumber;
0978                 ++i;
0979             } else if (ch.toUpper() == 'X' && tokenText == "0") {
0980                 // Hexadecimal number.
0981                 numberBase = 16;
0982                 tokenText.append('x');
0983                 ++i;
0984                 state = InNumber;
0985             } else if (ch.toUpper() == 'B' && tokenText == "0") {
0986                 // Binary number.
0987                 numberBase = 2;
0988                 tokenText.append('b');
0989                 ++i;
0990                 state = InNumber;
0991             } else if (ch.toUpper() == 'O' && tokenText == "0") {
0992                 // Octal number.
0993                 numberBase = 8;
0994                 tokenText.append('o');
0995                 ++i;
0996                 state = InNumber;
0997             } else if (ch.toUpper() == 'D' && tokenText == "0") {
0998                 // Decimal number (with prefix).
0999                 numberBase = 10;
1000                 tokenText.append('d');
1001                 ++i;
1002                 state = InNumber;
1003             } else if (isSeparatorChar(ch)) {
1004                 // Ignore thousand separators.
1005                 ++i;
1006             } else if (tokenText.isEmpty() && (ch == '+' || isMinus(ch))) {
1007                 // Allow expressions like "$-10" or "$+10".
1008                 if (isMinus(ch))
1009                     tokenText.append('-');
1010                 ++i;
1011             } else {
1012                 if (tokenText.endsWith("0")) {
1013                     // Done with integer number (tokenText is "0" or "-0").
1014                     numberBase = 10;
1015                     state = InNumberEnd;
1016                 } else {
1017                     // Only leading separators.
1018                     state = Bad;
1019                 }
1020             }
1021             break;
1022 
1023         // Parse the number digits.
1024         case InNumber: {
1025             ushort c = ch.unicode();
1026             bool isDigit = c < DIGIT_MAP_COUNT && (s_digitMap[c] <= numberBase);
1027 
1028             if (isDigit) {
1029                 // Consume as long as it's a digit
1030                 tokenText.append(ex.at(i++).toUpper());
1031             } else if (isExponent(ch, numberBase)) {
1032                 // Maybe exponent
1033                 expText = ch.toUpper();
1034                 expStart = i;
1035                 ++i;
1036                 tokenText = fixNumberRadix(tokenText);
1037                 if (!tokenText.isNull()) {
1038                     state = InExpIndicator;
1039                 } else
1040                     state = Bad;
1041             } else if (isRadixChar(ch)) {
1042                 // Might be a radix point or a separator.
1043                 // Collect it and decide later.
1044                 tokenText.append(ch);
1045                 ++i;
1046             } else if (isSeparatorChar(ch)) {
1047                 // Ignore thousand separators.
1048                 ++i;
1049             } else {
1050                 // We're done with number.
1051                 tokenText = fixNumberRadix(tokenText);
1052                 if (!tokenText.isNull())
1053                     state = InNumberEnd;
1054                 else
1055                     state = Bad;
1056             }
1057 
1058             break;
1059         }
1060 
1061         // Validate exponent start.
1062         case InExpIndicator: {
1063             ushort c = ch.unicode();
1064             bool isDigit = c < DIGIT_MAP_COUNT && (s_digitMap[c] <= numberBase);
1065 
1066             if (expBase == 0) {
1067                 // Set the default exponent base (same as number)
1068                 expBase = numberBase;
1069             }
1070 
1071             if (expText.length() == 1 && (ch == '+' || isMinus(ch))) {
1072                 // Possible + or - right after E.
1073                 expText.append(ch == QChar(0x2212) ? '-' : ch);
1074                 ++i;
1075             } else if (isDigit) {
1076                 if (ch == '0') {
1077                     // Might be a base prefix
1078                     expText.append(ch);
1079                     ++i;
1080                     state = InExponentBase;
1081                 } else {
1082                     // Parse the exponent absolute value.
1083                     tokenText.append(expText);
1084                     state = InExponent;
1085                 }
1086             } else if (isSeparatorChar(ch)) {
1087                 // Ignore thousand separators.
1088                 ++i;
1089             } else {
1090                 // Invalid thing here. Rollback: might be an identifier
1091                 // used in implicit multiplication.
1092                 i = expStart;
1093                 state = InNumberEnd;
1094             }
1095 
1096             break;
1097         }
1098 
1099         // Detect exponent base.
1100         case InExponentBase: {
1101             int base = -1;
1102             switch (ch.toUpper().unicode()) {
1103                 case 'B':
1104                     base = 2;
1105                     break;
1106                 case 'O':
1107                     base = 8;
1108                     break;
1109                 case 'D':
1110                     base = 10;
1111                     break;
1112                 case 'X':
1113                     base = 16;
1114                     break;
1115             }
1116 
1117             if (base != -1) {
1118                 // Specific exponent base found
1119                 expBase = base;
1120                 tokenText.append(expText);
1121                 tokenText.append(ch.toLower());
1122                 ++i;
1123             } else {
1124                 // No exponent base specified, use the default one
1125                 tokenText.append(expText);
1126             }
1127 
1128             state = InExponent;
1129 
1130             break;
1131         }
1132 
1133         // Parse exponent.
1134         case InExponent: {
1135             ushort c = ch.unicode();
1136             bool isDigit = c < DIGIT_MAP_COUNT && (s_digitMap[c] <= expBase);
1137 
1138             if (isDigit) {
1139                 // Consume as long as it's a digit.
1140                 tokenText.append(ex.at(i++));
1141             } else if (isSeparatorChar(ch)) {
1142                 // Ignore thousand separators.
1143                 ++i;
1144             } else {
1145                 // We're done with floating-point number.
1146                 state = InNumberEnd;
1147             };
1148 
1149             break;
1150         }
1151 
1152         case InNumberEnd: {
1153             int tokenSize = i - tokenStart;
1154             tokens.append(Token(Token::stxNumber, tokenText,
1155                                 tokenStart, tokenSize));
1156 
1157             // Make sure a number cannot be followed by another number.
1158             if (ch.isDigit() || isRadixChar(ch) || ch == '#')
1159                 state = Bad;
1160             else
1161                 state = Init;
1162             break;
1163         }
1164 
1165         case Bad:
1166             tokens.setValid(false);
1167             break;
1168 
1169         default:
1170             break;
1171         };
1172     }
1173 
1174     if (state == Bad)
1175         // Invalidating here too, because usually when we set state to Bad,
1176         // the case Bad won't be run.
1177         tokens.setValid(false);
1178 
1179     return tokens;
1180 }
1181 
1182 void Evaluator::compile(const Tokens& tokens)
1183 {
1184 #ifdef EVALUATOR_DEBUG
1185     QFile debugFile("eval.log");
1186     debugFile.open(QIODevice::WriteOnly);
1187     QTextStream dbg(&debugFile);
1188 #endif
1189 
1190     // Initialize variables.
1191     m_dirty = false;
1192     m_valid = false;
1193     m_codes.clear();
1194     m_constants.clear();
1195     m_identifiers.clear();
1196     m_error = QString();
1197 
1198     // Sanity check.
1199     if (tokens.count() == 0)
1200         return;
1201 
1202     TokenStack syntaxStack;
1203     QStack<int> argStack;
1204     unsigned argCount = 1;
1205 
1206     for (int i = 0; i <= tokens.count() && !syntaxStack.hasError(); ++i) {
1207         // Helper token: Invalid is end-of-expression.
1208         Token token = (i < tokens.count()) ? tokens.at(i)
1209                                           : Token(Token::stxOperator);
1210         Token::Type tokenType = token.type();
1211         if (tokenType >= Token::stxOperator) {
1212             tokenType = Token::stxOperator;
1213         }
1214 
1215 #ifdef EVALUATOR_DEBUG
1216         dbg << "\nToken: " << token.description() << "\n";
1217 #endif
1218 
1219         // Unknown token is invalid.
1220         if (tokenType == Token::stxUnknown)
1221             break;
1222 
1223         // Try to apply all parsing rules.
1224 #ifdef EVALUATOR_DEBUG
1225         dbg << "\tChecking rules..." << "\n";
1226 #endif
1227         // Repeat until no more rule applies.
1228         bool argHandled = false;
1229         while (!syntaxStack.hasError()) {
1230             bool ruleFound = false;
1231 
1232             // Rule for function last argument: id (arg) -> arg.
1233             if (!ruleFound && syntaxStack.itemCount() >= 4) {
1234                 Token par2 = syntaxStack.top();
1235                 Token arg = syntaxStack.top(1);
1236                 Token par1 = syntaxStack.top(2);
1237                 Token id = syntaxStack.top(3);
1238                 if (par2.asOperator() == Token::AssociationEnd
1239                     && arg.isOperand()
1240                     && par1.asOperator() == Token::AssociationStart
1241                     && id.isIdentifier())
1242                 {
1243                     ruleFound = true;
1244                     syntaxStack.reduce(4, MAX_PRECEDENCE);
1245                     m_codes.append(Opcode(Opcode::Function, argCount));
1246 #ifdef EVALUATOR_DEBUG
1247                         dbg << "\tRule for function last argument "
1248                             << argCount << " \n";
1249 #endif
1250                     argCount = argStack.empty() ? 0 : argStack.pop();
1251                 }
1252             }
1253 
1254             // Are we entering a function? If token is operator,
1255             // and stack already has: id (arg.
1256             if (!ruleFound && !argHandled && tokenType == Token::stxOperator
1257                  && syntaxStack.itemCount() >= 3)
1258             {
1259                 Token arg = syntaxStack.top();
1260                 Token par = syntaxStack.top(1);
1261                 Token id = syntaxStack.top(2);
1262                 if (arg.isOperand()
1263                     && par.asOperator() == Token::AssociationStart
1264                     && id.isIdentifier())
1265                 {
1266                     ruleFound = true;
1267                     argStack.push(argCount);
1268 #ifdef EVALUATOR_DEBUG
1269                     dbg << "\tEntering new function, pushing argcount="
1270                         << argCount << " of parent function\n";
1271 #endif
1272                     argCount = 1;
1273                     break;
1274                 }
1275            }
1276 
1277            // Rule for postfix operators: Y POSTFIX -> Y.
1278            // Condition: Y is not an operator, POSTFIX is a postfix op.
1279            // Since we evaluate from left to right,
1280            // we need not check precedence at this point.
1281            if (!ruleFound && syntaxStack.itemCount() >= 2) {
1282                Token postfix = syntaxStack.top();
1283                Token y = syntaxStack.top(1);
1284                if (postfix.isOperator() && y.isOperand()) {
1285                    switch (postfix.asOperator()) {
1286                    case Token::Factorial:
1287                        ruleFound = true;
1288                        syntaxStack.reduce(2);
1289                        m_codes.append(Opcode(Opcode::Fact));
1290                        break;
1291                    default:;
1292                    }
1293                }
1294 #ifdef EVALUATOR_DEBUG
1295                if (ruleFound) {
1296                    dbg << "\tRule for postfix operator "
1297                        << postfix.text() << "\n";
1298                }
1299 #endif
1300            }
1301 
1302            // Rule for parenthesis: (Y) -> Y.
1303            if (!ruleFound && syntaxStack.itemCount() >= 3) {
1304                Token right = syntaxStack.top();
1305                Token y = syntaxStack.top(1);
1306                Token left = syntaxStack.top(2);
1307                if (y.isOperand()
1308                    && right.asOperator() == Token::AssociationEnd
1309                    && left.asOperator() == Token::AssociationStart)
1310                {
1311                    ruleFound = true;
1312                    syntaxStack.reduce(3, MAX_PRECEDENCE);
1313 #ifdef EVALUATOR_DEBUG
1314                    dbg << "\tRule for (Y) -> Y" << "\n";
1315 #endif
1316                }
1317            }
1318 
1319            // Rule for simplified syntax for function,
1320            // e.g. "sin pi" or "cos 1.2". Conditions:
1321            // *precedence of function reduction >= precedence of next token.
1322            // *or next token is not an operator.
1323            if (!ruleFound && syntaxStack.itemCount() >= 2) {
1324                Token arg = syntaxStack.top();
1325                Token id = syntaxStack.top(1);
1326                if (arg.isOperand() && isFunction(id)
1327                    && (!token.isOperator()
1328                        || opPrecedence(Token::Function) >=
1329                               opPrecedence(token.asOperator())))
1330                {
1331                    ruleFound = true;
1332                    m_codes.append(Opcode(Opcode::Function, 1));
1333                    syntaxStack.reduce(2);
1334 #ifdef EVALUATOR_DEBUG
1335                    dbg << "\tRule for simplified function syntax; function "
1336                        << id.text() << "\n";
1337 #endif
1338                }
1339            }
1340 
1341            // Rule for unary operator in simplified function syntax.
1342            // This handles cases like "sin -90".
1343            if (!ruleFound && syntaxStack.itemCount() >= 3) {
1344                Token x = syntaxStack.top();
1345                Token op = syntaxStack.top(1);
1346                Token id = syntaxStack.top(2);
1347                if (x.isOperand() && isFunction(id)
1348                    && (op.asOperator() == Token::Addition
1349                    || op.asOperator() == Token::Subtraction))
1350                {
1351                    ruleFound = true;
1352                    syntaxStack.reduce(2);
1353                    if (op.asOperator() == Token::Subtraction)
1354                      m_codes.append(Opcode(Opcode::Neg));
1355 #ifdef EVALUATOR_DEBUG
1356                      dbg << "\tRule for unary operator in simplified "
1357                             "function syntax; function " << id.text() << "\n";
1358 #endif
1359                }
1360            }
1361 
1362            // Rule for function arguments. If token is ; or ):
1363            // id (arg1 ; arg2 -> id (arg.
1364            // Must come before binary op rule, special case of the latter.
1365            if (!ruleFound && syntaxStack.itemCount() >= 5
1366                && token.isOperator()
1367                && (token.asOperator() == Token::AssociationEnd
1368                    || token.asOperator() == Token::ListSeparator))
1369            {
1370                Token arg2 = syntaxStack.top();
1371                Token sep = syntaxStack.top(1);
1372                Token arg1 = syntaxStack.top(2);
1373                Token par = syntaxStack.top(3);
1374                Token id = syntaxStack.top(4);
1375                if (arg2.isOperand()
1376                    && sep.asOperator() == Token::ListSeparator
1377                    && arg1.isOperand()
1378                    && par.asOperator() == Token::AssociationStart
1379                    && id.isIdentifier())
1380                {
1381                    ruleFound = true;
1382                    argHandled = true;
1383                    syntaxStack.reduce(3, MAX_PRECEDENCE);
1384                    ++argCount;
1385 #ifdef EVALUATOR_DEBUG
1386                    dbg << "\tRule for function argument "
1387                        << argCount << " \n";
1388 #endif
1389                }
1390            }
1391 
1392            // Rule for function call with parentheses,
1393            // but without argument, e.g. "2*PI()".
1394            if (!ruleFound && syntaxStack.itemCount() >= 3) {
1395                Token par2 = syntaxStack.top();
1396                Token par1 = syntaxStack.top(1);
1397                Token id = syntaxStack.top(2);
1398                if (par2.asOperator() == Token::AssociationEnd
1399                    && par1.asOperator() == Token::AssociationStart
1400                    && id.isIdentifier())
1401                {
1402                    ruleFound = true;
1403                    syntaxStack.reduce(3, MAX_PRECEDENCE);
1404                    m_codes.append(Opcode(Opcode::Function, 0));
1405 #ifdef EVALUATOR_DEBUG
1406                    dbg << "\tRule for function call with parentheses, "
1407                           "but without argument\n";
1408 #endif
1409                }
1410            }
1411 
1412            // Rule for binary operator:  A (op) B -> A.
1413            // Conditions: precedence of op >= precedence of token.
1414            // Action: push (op) to result e.g.
1415            // "A * B" becomes "A" if token is operator "+".
1416            // Exception: for caret (power operator), if op is another caret
1417            // then it doesn't apply, e.g. "2^3^2" is evaluated as "2^(3^2)".
1418            // Exception: doesn't apply if B is a function name (to manage
1419            // shift/reduce conflict with simplified function syntax
1420            // (issue #600).
1421            if (!ruleFound && syntaxStack.itemCount() >= 3) {
1422                Token b = syntaxStack.top();
1423                Token op = syntaxStack.top(1);
1424                Token a = syntaxStack.top(2);
1425                if (a.isOperand() && b.isOperand() && op.isOperator()
1426                    && ( // Normal operator.
1427                        (token.isOperator()
1428                            && opPrecedence(op.asOperator()) >=
1429                                opPrecedence(token.asOperator())
1430                            && !(b.isIdentifier() && token.asOperator() == Token::AssociationStart)
1431                            && token.asOperator() != Token::Exponentiation)
1432 
1433                        || ( // May represent implicit multiplication.
1434                            token.isOperand()
1435                            && opPrecedence(op.asOperator()) >=
1436                                opPrecedence(Token::Multiplication)))
1437                    && !(isFunction(b)))
1438                {
1439                    ruleFound = true;
1440                    switch (op.asOperator()) {
1441                    // Simple binary operations.
1442                    case Token::Addition:
1443                        m_codes.append(Opcode::Add);
1444                        break;
1445                    case Token::Subtraction:
1446                        m_codes.append(Opcode::Sub);
1447                        break;
1448                    case Token::Multiplication:
1449                        m_codes.append(Opcode::Mul);
1450                        break;
1451                    case Token::Division:
1452                        m_codes.append(Opcode::Div);
1453                        break;
1454                    case Token::Exponentiation:
1455                        m_codes.append(Opcode::Pow);
1456                        break;
1457                    case Token::Modulo:
1458                        m_codes.append(Opcode::Modulo);
1459                        break;
1460                    case Token::IntegerDivision:
1461                        m_codes.append(Opcode::IntDiv);
1462                        break;
1463                    case Token::ArithmeticLeftShift:
1464                        m_codes.append(Opcode::LSh);
1465                        break;
1466                    case Token::ArithmeticRightShift:
1467                        m_codes.append(Opcode::RSh);
1468                        break;
1469                    case Token::BitwiseLogicalAND:
1470                        m_codes.append(Opcode::BAnd);
1471                        break;
1472                    case Token::BitwiseLogicalOR:
1473                        m_codes.append(Opcode::BOr);
1474                        break;
1475                    case Token::UnitConversion: {
1476                        static const QRegExp unitNameNumberRE(
1477                            "(^[0-9e\\+\\-\\.,]|[0-9e\\.,]$)",
1478                            Qt::CaseInsensitive);
1479                        QString unitName =
1480                            m_expression.mid(b.pos(), b.size()).simplified();
1481                        // Make sure the whole unit name can be used
1482                        // as a single operand in multiplications.
1483                        if (b.minPrecedence() <
1484                                opPrecedence(Token::Multiplication))
1485                        {
1486                            unitName = "(" + unitName + ")";
1487                        }
1488                        // Protect the unit name
1489                        // if it starts or ends with a number.
1490                        else if (unitNameNumberRE.indexIn(unitName) != -1)
1491                            unitName = "(" + unitName + ")";
1492                        m_codes.append(Opcode(Opcode::Conv, unitName));
1493                        break; }
1494                    default: break;
1495                    };
1496                    syntaxStack.reduce(3);
1497 #ifdef EVALUATOR_DEBUG
1498                    dbg << "\tRule for binary operator" << "\n";
1499 #endif
1500                }
1501            }
1502 
1503 #ifdef ALLOW_IMPLICIT_MULT
1504            // Rule for implicit multiplication.
1505            // Action: Treat as A * B.
1506            // Exception: doesn't apply if B is a function name
1507            // (to manage shift/reduce conflict with simplified
1508            // function syntax (fixes issue #600).
1509            if (!ruleFound && syntaxStack.itemCount() >= 2) {
1510                Token b = syntaxStack.top();
1511                Token a = syntaxStack.top(1);
1512 
1513                if (a.isOperand() && b.isOperand()
1514                    && token.asOperator() != Token::AssociationStart
1515                    && ( // Token is normal operator.
1516                         (token.isOperator()
1517                             && opPrecedence(Token::Multiplication) >=
1518                                    opPrecedence(token.asOperator()))
1519                         || token.isOperand()) // Implicit multiplication.
1520                    && !isFunction(b))
1521                {
1522                    ruleFound = true;
1523                    syntaxStack.reduce(2, opPrecedence(Token::Multiplication));
1524                    m_codes.append(Opcode::Mul);
1525 #ifdef EVALUATOR_DEBUG
1526                    dbg << "\tRule for implicit multiplication" << "\n";
1527 #endif
1528                }
1529 
1530            }
1531 #endif
1532 
1533            // Rule for unary operator:  (op1) (op2) X -> (op1) X.
1534            // Conditions: op2 is unary.
1535            // Current token has lower precedence than multiplication.
1536            if (!ruleFound
1537                && token.asOperator() != Token::AssociationStart
1538                && syntaxStack.itemCount() >= 3)
1539            {
1540                Token x = syntaxStack.top();
1541                Token op2 = syntaxStack.top(1);
1542                Token op1 = syntaxStack.top(2);
1543                if (x.isOperand() && op1.isOperator()
1544                    && (op2.asOperator() == Token::Addition
1545                        || op2.asOperator() == Token::Subtraction)
1546                    && (token.isOperand()
1547                        || opPrecedence(token.asOperator()) <=
1548                               opPrecedence(Token::Multiplication)))
1549                {
1550                    ruleFound = true;
1551                    if (op2.asOperator() == Token::Subtraction)
1552                        m_codes.append(Opcode(Opcode::Neg));
1553 
1554                    syntaxStack.reduce(2);
1555 #ifdef EVALUATOR_DEBUG
1556                    dbg << "\tRule for unary operator" << op2.text() << "\n";
1557 #endif
1558                }
1559            }
1560 
1561            // Auxiliary rule for unary prefix operator:  (op) X -> X.
1562            // Conditions: op is unary, op is first in syntax stack.
1563            // Action: create code for (op). Unary MINUS or PLUS are
1564            // treated with the precedence of multiplication.
1565            if (!ruleFound
1566                && token.asOperator() != Token::AssociationStart
1567                && syntaxStack.itemCount() == 2)
1568            {
1569                Token x = syntaxStack.top();
1570                Token op = syntaxStack.top(1);
1571                if (x.isOperand()
1572                    && (op.asOperator() == Token::Addition
1573                        || op.asOperator() == Token::Subtraction)
1574                    && ((token.isOperator()
1575                            && opPrecedence(token.asOperator()) <=
1576                                   opPrecedence(Token::Multiplication))
1577                        || token.isOperand()))
1578                {
1579                    ruleFound = true;
1580                    if (op.asOperator() == Token::Subtraction)
1581                        m_codes.append(Opcode(Opcode::Neg));
1582 #ifdef EVALUATOR_DEBUG
1583                    dbg << "\tRule for unary operator (auxiliary)" << "\n";
1584 #endif
1585                    syntaxStack.reduce(2);
1586                }
1587            }
1588 
1589            if (!ruleFound)
1590                break;
1591         }
1592 
1593         // Can't apply rules anymore, push the token.
1594         syntaxStack.push(token);
1595 
1596         // For identifier, generate code to load from reference.
1597         if (tokenType == Token::stxIdentifier) {
1598             m_identifiers.append(token.text());
1599             m_codes.append(Opcode(Opcode::Ref, m_identifiers.count() - 1));
1600 #ifdef EVALUATOR_DEBUG
1601             dbg << "\tPush " << token.text() << " to identifier pools" << "\n";
1602 #endif
1603         }
1604 
1605         // For constants, generate code to load from a constant.
1606         if (tokenType == Token::stxNumber) {
1607             m_constants.append(token.asNumber());
1608             m_codes.append(Opcode(Opcode::Load, m_constants.count() - 1));
1609 #ifdef EVALUATOR_DEBUG
1610             dbg << "\tPush " << token.asNumber()
1611                 << " to constant pools" << "\n";
1612 #endif
1613         }
1614     }
1615 
1616     m_valid = false;
1617     if (syntaxStack.hasError())
1618         m_error = syntaxStack.error();
1619     // syntaxStack must left only one operand
1620     // and end-of-expression (i.e. Invalid).
1621     else if (syntaxStack.itemCount() == 2
1622              && syntaxStack.top().isOperator()
1623              && syntaxStack.top().asOperator() == Token::Invalid
1624              && !syntaxStack.top(1).isOperator())
1625     {
1626         m_valid = true;
1627     }
1628 
1629 #ifdef EVALUATOR_DEBUG
1630     dbg << "Dump: " << dump() << "\n";
1631     debugFile.close();
1632 #endif
1633 
1634     // Bad parsing? Clean-up everything.
1635     if (!m_valid) {
1636         m_constants.clear();
1637         m_codes.clear();
1638         m_identifiers.clear();
1639     }
1640 }
1641 
1642 Quantity Evaluator::evalNoAssign()
1643 {
1644     Quantity result;
1645 
1646     if (m_dirty) {
1647         // Reset.
1648         m_assignId = QString();
1649         m_assignFunc = false;
1650         m_assignArg.clear();
1651 
1652         Tokens tokens = scan(m_expression);
1653 
1654         // Invalid expression?
1655         if (!tokens.valid()) {
1656             m_error = i18nc("Invalid input from user to calculator", "invalid expression");
1657             return Quantity(0);
1658         }
1659 
1660         // Variable assignment?
1661         if (tokens.count() > 2
1662             && tokens.at(0).isIdentifier()
1663             && tokens.at(1).asOperator() == Token::Assignment)
1664         {
1665             m_assignId = tokens.at(0).text();
1666             tokens.erase(tokens.begin());
1667             tokens.erase(tokens.begin());
1668         } else if (tokens.count() > 2
1669                    && tokens.at(0).isIdentifier()
1670                    && tokens.at(1).asOperator() == Token::AssociationStart)
1671         {
1672             // Check for function assignment.
1673             // Syntax:
1674             // ident opLeftPar (ident (opSemiColon ident)*)? opRightPar opEqual
1675             int t;
1676             if (tokens.count() > 4
1677                 && tokens.at(2).asOperator() == Token::AssociationEnd)
1678             {
1679                 // Functions with no argument.
1680                 t = 3;
1681                 if (tokens.at(3).asOperator() == Token::Assignment)
1682                     m_assignFunc = true;
1683             } else {
1684                 for (t = 2; t + 1 < tokens.count(); t += 2)  {
1685                     if (!tokens.at(t).isIdentifier())
1686                         break;
1687 
1688                     m_assignArg.append(tokens.at(t).text());
1689 
1690                     if (tokens.at(t+1).asOperator() == Token::AssociationEnd) {
1691                         t += 2;
1692                         if (t < tokens.count()
1693                             && tokens.at(t).asOperator() == Token::Assignment)
1694                         {
1695                             m_assignFunc = true;
1696                         }
1697 
1698                         break;
1699                     } else if (tokens.at(t + 1)
1700                                .asOperator() != Token::ListSeparator)
1701                         break;
1702                 }
1703             }
1704 
1705             if (m_assignFunc) {
1706                 m_assignId = tokens.at(0).text();
1707                 for (; t >= 0; --t)
1708                     tokens.erase(tokens.begin());
1709             } else
1710                 m_assignArg.clear();
1711         }
1712 
1713         compile(tokens);
1714         if (!m_valid) {
1715             if (m_error.isEmpty())
1716                 m_error = i18nc("Unknown calculator error during evaluation of user input", "compile error");
1717             return CNumber(0);
1718         }
1719     }
1720 
1721     result = exec(m_codes, m_constants, m_identifiers);
1722     return result;
1723 }
1724 
1725 Quantity Evaluator::exec(const QVector<Opcode>& opcodes,
1726                          const QVector<Quantity>& constants,
1727                          const QStringList& identifiers)
1728 {
1729     QStack<Quantity> stack;
1730     QHash<int, QString> refs;
1731     int index;
1732     Quantity val1, val2;
1733     QVector<Quantity> args;
1734     QString fname;
1735     Function* function;
1736 
1737     for (int pc = 0; pc < opcodes.count(); ++pc) {
1738         const Opcode& opcode = opcodes.at(pc);
1739         index = opcode.index;
1740         switch (opcode.type) {
1741             // No operation.
1742             case Opcode::Nop:
1743                 break;
1744 
1745             // Load a constant, push to stack.
1746             case Opcode::Load:
1747                 val1 = constants.at(index);
1748                 stack.push(val1);
1749                 break;
1750 
1751             // Unary operation.
1752             case Opcode::Neg:
1753                 if (stack.count() < 1) {
1754                     m_error = i18n("invalid expression");
1755                     return CMath::nan();
1756                 }
1757                 val1 = stack.pop();
1758                 val1 = checkOperatorResult(-val1);
1759                 stack.push(val1);
1760                 break;
1761 
1762             // Binary operation: take two values from stack,
1763             // do the operation, push the result to stack.
1764             case Opcode::Add:
1765                 if (stack.count() < 2) {
1766                     m_error = i18n("invalid expression");
1767                     return CMath::nan();
1768                 }
1769                 val1 = stack.pop();
1770                 val2 = stack.pop();
1771                 val2 = checkOperatorResult(val2 + val1);
1772                 stack.push(val2);
1773                 break;
1774 
1775             case Opcode::Sub:
1776                 if (stack.count() < 2) {
1777                     m_error = i18n("invalid expression");
1778                     return CMath::nan();
1779                 }
1780                 val1 = stack.pop();
1781                 val2 = stack.pop();
1782                 val2 = checkOperatorResult(val2 - val1);
1783                 stack.push(val2);
1784                 break;
1785 
1786             case Opcode::Mul:
1787                 if (stack.count() < 2) {
1788                     m_error = i18n("invalid expression");
1789                     return CMath::nan();
1790                 }
1791                 val1 = stack.pop();
1792                 val2 = stack.pop();
1793                 val2 = checkOperatorResult(val2 * val1);
1794                 stack.push(val2);
1795                 break;
1796 
1797             case Opcode::Div:
1798                 if (stack.count() < 2) {
1799                     m_error = i18n("invalid expression");
1800                     return CMath::nan();
1801                 }
1802                 val1 = stack.pop();
1803                 val2 = stack.pop();
1804                 val2 = checkOperatorResult(val2 / val1);
1805                 stack.push(val2);
1806                 break;
1807 
1808             case Opcode::Pow:
1809                 if (stack.count() < 2) {
1810                     m_error = i18n("invalid expression");
1811                     return CMath::nan();
1812                 }
1813                 val1 = stack.pop();
1814                 val2 = stack.pop();
1815                 val2 = checkOperatorResult(DMath::raise(val2, val1));
1816                 stack.push(val2);
1817                 break;
1818 
1819             case Opcode::Fact:
1820                 if (stack.count() < 1) {
1821                     m_error = i18n("invalid expression");
1822                     return CMath::nan();
1823                 }
1824                 val1 = stack.pop();
1825                 val1 = checkOperatorResult(DMath::factorial(val1));
1826                 stack.push(val1);
1827                 break;
1828 
1829             case Opcode::Modulo:
1830                 if (stack.count() < 2) {
1831                     m_error = i18n("invalid expression");
1832                     return CMath::nan();
1833                 }
1834                 val1 = stack.pop();
1835                 val2 = stack.pop();
1836                 val2 = checkOperatorResult(val2 % val1);
1837                 stack.push(val2);
1838                 break;
1839 
1840             case Opcode::IntDiv:
1841                 if (stack.count() < 2) {
1842                     m_error = i18n("invalid expression");
1843                     return CMath::nan();
1844                 }
1845                 val1 = stack.pop();
1846                 val2 = stack.pop();
1847                 val2 = checkOperatorResult(val2 / val1);
1848                 stack.push(DMath::integer(val2));
1849                 break;
1850 
1851             case Opcode::LSh:
1852                 if (stack.count() < 2) {
1853                     m_error = i18n("invalid expression");
1854                     return DMath::nan();
1855                 }
1856                 val1 = stack.pop();
1857                 val2 = stack.pop();
1858                 val2 = val2 << val1;
1859                 stack.push(val2);
1860                 break;
1861 
1862             case Opcode::RSh:
1863                 if (stack.count() < 2) {
1864                     m_error = i18n("invalid expression");
1865                     return DMath::nan();
1866                 }
1867                 val1 = stack.pop();
1868                 val2 = stack.pop();
1869                 val2 = val2 >> val1;
1870                 stack.push(val2);
1871                 break;
1872 
1873             case Opcode::BAnd:
1874                 if (stack.count() < 2) {
1875                     m_error = i18n("invalid expression");
1876                     return DMath::nan();
1877                 }
1878                 val1 = stack.pop();
1879                 val2 = stack.pop();
1880                 val2 &= val1;
1881                 stack.push(val2);
1882                 break;
1883 
1884             case Opcode::BOr:
1885                 if (stack.count() < 2) {
1886                     m_error = i18n("invalid expression");
1887                     return DMath::nan();
1888                 }
1889                 val1 = stack.pop();
1890                 val2 = stack.pop();
1891                 val2 |= val1;
1892                 stack.push(val2);
1893                 break;
1894 
1895             case Opcode::Conv:
1896                 if (stack.count() < 2) {
1897                     m_error = i18n("invalid expression");
1898                     return HMath::nan();
1899                 }
1900                 val1 = stack.pop();
1901                 val2 = stack.pop();
1902                 if (val1.isZero()) {
1903                     m_error = i18n("unit must not be zero");
1904                     return HMath::nan();
1905                 }
1906                 if (!val1.sameDimension(val2)) {
1907                     m_error = i18n("Conversion failed - dimension mismatch");
1908                     return HMath::nan();
1909                 }
1910                 val2.setDisplayUnit(val1.numericValue(), opcode.text);
1911                 stack.push(val2);
1912                 break;
1913 
1914             // Reference.
1915             case Opcode::Ref:
1916                 fname = identifiers.at(index);
1917                 if (m_assignArg.contains(fname)) {
1918                     // Argument.
1919                     stack.push(CMath::nan());
1920                 } else if (m_variables.contains(fname)) {
1921                     // Variable.
1922                     stack.push(m_variables.value(fname));
1923                 } else {
1924                     // Function.
1925                     function = FunctionRepo::instance()->find(fname);
1926                     if (function) {
1927                         stack.push(CMath::nan());
1928                         refs.insert(stack.count(), fname);
1929                     } else if (m_assignFunc) {
1930                         // Allow arbitrary identifiers
1931                         // when declaring user functions.
1932                         stack.push(CMath::nan());
1933                         refs.insert(stack.count(), fname);
1934                     } else {
1935                         m_error = fname + ": "
1936                                   + i18n("unknown function or variable");
1937                         return CMath::nan();
1938                     }
1939                 }
1940                 break;
1941 
1942             // Calling function.
1943             case Opcode::Function:
1944                 // Must do this first to avoid crash
1945                 // when using vars like functions.
1946                 if (refs.isEmpty())
1947                     break;
1948 
1949                 fname = refs.take(stack.count() - index);
1950                 function = FunctionRepo::instance()->find(fname);
1951 
1952                 if (!function && !m_assignFunc) {
1953                     m_error = fname + ": "
1954                               + i18n("unknown function or variable");
1955                     return CMath::nan();
1956                 }
1957 
1958                 if (stack.count() < index) {
1959                     m_error = i18n("invalid expression");
1960                     return CMath::nan();
1961                 }
1962 
1963                 args.clear();
1964                 for(; index; --index)
1965                     args.insert(args.begin(), stack.pop());
1966 
1967                 // Remove the NaN we put on the stack (needed to make the user
1968                 // functions declaration work with arbitrary identifiers).
1969                 stack.pop();
1970 
1971                 // Show function signature if user has given no argument (yet).
1972                 if (function) {
1973                     if (!args.count()) {
1974                         m_error = QString::fromLatin1("%1 (%2)").arg(
1975                             fname,
1976                             function->usage()
1977                         );
1978                         return CMath::nan();
1979                     }
1980                 }
1981 
1982                 if (m_assignFunc) {
1983                     // Allow arbitrary identifiers for declaring user functions.
1984                     stack.push(CMath::nan());
1985                 } else {
1986                     stack.push(function->exec(args));
1987                     if (function->error()) {
1988                         m_error = stringFromFunctionError(function);
1989                         return CMath::nan();
1990                     }
1991                 }
1992                 break;
1993 
1994             default:
1995                 break;
1996         }
1997     }
1998 
1999     // More than one value in stack? Unsuccessful execution.
2000     if (stack.count() != 1) {
2001         m_error = i18n("invalid expression");
2002         return CMath::nan();
2003     }
2004     return stack.pop();
2005 }
2006 
2007 
2008 static void replaceSuperscriptPowersWithCaretEquivalent(QString& expr)
2009 {
2010     static const QRegularExpression s_superscriptPowersRE(
2011         "(\\x{207B})?[\\x{2070}¹²³\\x{2074}-\\x{2079}]+"
2012     );
2013     static const QHash<QChar, QChar> s_superscriptPowersHash {
2014         {L'\u207B', '-'},
2015         {L'\u2070', '0'},
2016         {L'\u00B9', '1'},
2017         {L'\u00B2', '2'},
2018         {L'\u00B3', '3'},
2019         {L'\u2074', '4'},
2020         {L'\u2075', '5'},
2021         {L'\u2076', '6'},
2022         {L'\u2077', '7'},
2023         {L'\u2078', '8'},
2024         {L'\u2079', '9'},
2025     };
2026 
2027     int offset = 0;
2028     while (true) {
2029       QRegularExpressionMatch match = s_superscriptPowersRE.match(expr, offset);
2030       if (!match.hasMatch())
2031           break;
2032 
2033       QString power = match.captured();
2034       for (int pos = power.size() - 1; pos >= 0; --pos) {
2035         QChar c = power.at(pos);
2036         power.replace(pos, 1, s_superscriptPowersHash.value(c, c));
2037       }
2038 
2039       bool isNegative = match.capturedStart(1) != -1;
2040       if (isNegative)
2041           power = "^(" + power + ")";
2042       else
2043           power = "^" + power;
2044 
2045       expr.replace(match.capturedStart(), match.capturedLength(), power);
2046       offset = match.capturedStart() + power.size();
2047     }
2048 }
2049 
2050 QString Evaluator::autoFix(const QString& expr)
2051 {
2052     int par = 0;
2053     QString result;
2054 
2055     // Strip off all funny characters.
2056     for (int c = 0; c < expr.length(); ++c)
2057         if (expr.at(c) >= QChar(32))
2058             result.append(expr.at(c));
2059 
2060     // No extra whitespaces at the beginning and at the end.
2061     result = result.trimmed();
2062     if (result.isEmpty()) {
2063         return result;
2064     }
2065 
2066     // Special handling of unit conversion, because a lot of units have _'s in them and different casing
2067     QRegularExpression unitConversion(R"raw(([0-9\.,0-9]*?)\s*([a-z ]+?)\s+(?:\=|to|is|in)\s+([a-z ]+)$)raw", QRegularExpression::CaseInsensitiveOption);
2068     QRegularExpressionMatch unitConversionMatch = unitConversion.match(expr);
2069     if (unitConversionMatch.hasMatch()) {
2070         const QString amount = unitConversionMatch.captured(1);
2071         bool isNumber = amount.toFloat(&isNumber);
2072         if (isNumber) {
2073             QString unit1 = unitConversionMatch.captured(2).simplified().toLower();
2074             QString unit2 = unitConversionMatch.captured(3).simplified().toLower();
2075             if (m_unitFixups.contains(unit1)) {
2076                 unit1 = m_unitFixups[unit1];
2077             }
2078             if (m_unitFixups.contains(unit2)) {
2079                 unit2 = m_unitFixups[unit2];
2080             }
2081             if (m_allUnits.contains(unit1) && m_allUnits.contains(unit2)) {
2082                 result = amount + " " + unit1 + " in " + unit2;
2083             }
2084         }
2085     }
2086 
2087     // Strip trailing equal sign (=).
2088     while (result.endsWith("="))
2089         result = result.left(result.length() - 1);
2090 
2091     replaceSuperscriptPowersWithCaretEquivalent(result);
2092 
2093     // Automagically close all parenthesis.
2094     Tokens tokens = Evaluator::scan(result);
2095     if (tokens.count()) {
2096         for (int i = 0; i < tokens.count(); ++i)
2097             if (tokens.at(i).asOperator() == Token::AssociationStart)
2098                 ++par;
2099             else if (tokens.at(i).asOperator() == Token::AssociationEnd)
2100                 --par;
2101 
2102         if (par < 0)
2103             par = 0;
2104 
2105         // If the scanner stops in the middle, do not bother to apply fix.
2106         const Token& lastToken = tokens.at(tokens.count() - 1);
2107         if (lastToken.pos() + lastToken.size() >= result.length())
2108             while (par--)
2109                 result.append(')');
2110     }
2111 
2112     // Special treatment for simple function
2113     // e.g. "cos" is regarded as "cos(ans)".
2114     tokens = Evaluator::scan(result);
2115 
2116     if (tokens.count() == 1
2117             && tokens.at(0).isIdentifier()
2118             && FunctionRepo::instance()->find(tokens.at(0).text()))
2119     {
2120         result.append("(0)");
2121     }
2122 
2123     tokens = Evaluator::scan(result);
2124     result.clear();
2125     for (int i = 0; i < tokens.count(); ++i) {
2126         if (i >= tokens.count() - 1) {
2127             result += tokens[i].text() + " ";
2128             continue;
2129         }
2130 
2131         const Token &token = tokens[i];
2132         const Token &next = tokens[i+1];
2133         if (token.type() != Token::stxNumber ||
2134                 next.type() != Token::stxIdentifier ||
2135                 !m_variables.contains(next.text())) {
2136             result += tokens[i].text() + " ";
2137             continue;
2138         }
2139 
2140         result += "(" + token.text() + next.text() + ") ";
2141         i += 1;
2142     }
2143 
2144     return result;
2145 }
2146 
2147 QString Evaluator::dump()
2148 {
2149     QString result;
2150     int c;
2151 
2152     if (m_dirty) {
2153         Tokens tokens = scan(m_expression);
2154         compile(tokens);
2155     }
2156 
2157     result = QString("Expression: [%1]\n").arg(m_expression);
2158 
2159     result.append("  Constants:\n");
2160     for (c = 0; c < m_constants.count(); ++c) {
2161         auto val = m_constants.at(c);
2162         result += QString("    #%1 = %2\n").arg(c).arg(
2163             DMath::format(val, Quantity::Format::Fixed())
2164         );
2165     }
2166 
2167     result.append("\n");
2168     result.append("  Identifiers:\n");
2169     for (c = 0; c < m_identifiers.count(); ++c) {
2170         result += QString("    #%1 = %2\n").arg(c).arg(m_identifiers.at(c));
2171     }
2172 
2173     result.append("\n");
2174     result.append("  Code:\n");
2175     for (int i = 0; i < m_codes.count(); ++i) {
2176         QString code;
2177         switch (m_codes.at(i).type) {
2178             case Opcode::Load:
2179                 code = QString("Load #%1").arg(m_codes.at(i).index);
2180                 break;
2181             case Opcode::Ref:
2182                 code = QString("Ref #%1").arg(m_codes.at(i).index);
2183                 break;
2184             case Opcode::Function:
2185                 code = QString("Function (%1)").arg(m_codes.at(i).index);
2186                 break;
2187             case Opcode::Add:
2188                 code = "Add";
2189                 break;
2190             case Opcode::Sub:
2191                 code = "Sub";
2192                 break;
2193             case Opcode::Mul:
2194                 code = "Mul";
2195                 break;
2196             case Opcode::Div:
2197                 code = "Div";
2198                 break;
2199             case Opcode::Neg:
2200                 code = "Neg";
2201                 break;
2202             case Opcode::Pow:
2203                 code = "Pow";
2204                 break;
2205             case Opcode::Fact:
2206                 code = "Fact";
2207                 break;
2208             case Opcode::LSh:
2209                 code = "LSh";
2210                 break;
2211             case Opcode::RSh:
2212                 code = "RSh";
2213                 break;
2214             case Opcode::BAnd:
2215                 code = "BAnd";
2216                 break;
2217             case Opcode::BOr:
2218                 code = "BOr";
2219                 break;
2220             default:
2221                 code = "Unknown";
2222                 break;
2223         }
2224         result.append("   ").append(code).append("\n");
2225     }
2226 
2227     return result;
2228 }