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 }