File indexing completed on 2024-04-28 16:21:23
0001 /* This file is part of the KDE project 0002 Copyright (C) 2008-2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> 0003 Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org> 0004 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; only 0009 version 2 of the License. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "Formula.h" 0023 0024 #include "CalculationSettings.h" 0025 #include "Cell.h" 0026 #include "CellStorage.h" 0027 #include "Function.h" 0028 #include "FunctionRepository.h" 0029 #include "Sheet.h" 0030 #include "Map.h" 0031 #include "NamedAreaManager.h" 0032 #include "Region.h" 0033 #include "Value.h" 0034 #include "Util.h" 0035 0036 #include "ValueCalc.h" 0037 #include "ValueConverter.h" 0038 #include "ValueParser.h" 0039 0040 #include <limits.h> 0041 0042 #include <QStack> 0043 #include <QString> 0044 #include <QTextStream> 0045 0046 #include <klocale.h> 0047 0048 #define CALLIGRA_SHEETS_UNICODE_OPERATORS 0049 0050 /* 0051 To understand how this formula engine works, please refer to the documentation 0052 in file DESIGN.html. 0053 0054 Useful references: 0055 - "Principles of Compiler Design", A.V.Aho, J.D.Ullman, Addison Wesley, 1978 0056 - "Writing Interactive Compilers and Interpreters", P.J. Brown, 0057 John Wiley and Sons, 1979. 0058 - "The Theory and Practice of Compiler Writing", J.Tremblay, P.G.Sorenson, 0059 McGraw-Hill, 1985. 0060 - "The Java(TM) Virtual Machine Specification", T.Lindholm, F.Yellin, 0061 Addison-Wesley, 1997. 0062 - "Java Virtual Machine", J.Meyer, T.Downing, O'Reilly, 1997. 0063 0064 */ 0065 0066 0067 /* 0068 TODO - features: 0069 - handle Intersection 0070 - cell reference is made relative (absolute now) 0071 - shared formula (different owner, same data) 0072 - relative internal representation (independent of owner) 0073 - OASIS support 0074 TODO - optimizations: 0075 - handle initial formula marker = (and +) 0076 - reuse constant already in the pool 0077 - reuse references already in the pool 0078 - expression optimization (e.g. 1+2+A1 becomes 3+A1) 0079 */ 0080 0081 namespace Calligra 0082 { 0083 namespace Sheets 0084 { 0085 0086 class Opcode 0087 { 0088 public: 0089 0090 enum { Nop = 0, Load, Ref, Cell, Range, Function, Add, Sub, Neg, Mul, Div, 0091 Pow, Concat, Intersect, Not, Equal, Less, Greater, Array, Union 0092 }; 0093 0094 unsigned type; 0095 unsigned index; 0096 0097 Opcode(): type(Nop), index(0) {} 0098 Opcode(unsigned t): type(t), index(0) {} 0099 Opcode(unsigned t, unsigned i): type(t), index(i) {} 0100 }; 0101 0102 // used when evaluation formulas 0103 struct stackEntry { 0104 void reset() { 0105 row1 = col1 = row2 = col2 = -1; 0106 reg = Calligra::Sheets::Region(); 0107 regIsNamedOrLabeled = false; 0108 } 0109 Value val; 0110 Calligra::Sheets::Region reg; 0111 bool regIsNamedOrLabeled; 0112 int row1, col1, row2, col2; 0113 }; 0114 0115 class Q_DECL_HIDDEN Formula::Private : public QSharedData 0116 { 0117 public: 0118 Cell cell; 0119 Sheet *sheet; 0120 mutable bool dirty; 0121 mutable bool valid; 0122 QString expression; 0123 mutable QVector<Opcode> codes; 0124 mutable QVector<Value> constants; 0125 0126 Value valueOrElement(FuncExtra &fe, const stackEntry& entry) const; 0127 }; 0128 0129 class TokenStack : public QVector<Token> 0130 { 0131 public: 0132 TokenStack(); 0133 unsigned itemCount() const; 0134 void push(const Token& token); 0135 Token pop(); 0136 const Token& top(); 0137 const Token& top(unsigned index); 0138 }; 0139 0140 } // namespace Sheets 0141 } // namespace Calligra 0142 0143 using namespace Calligra::Sheets; 0144 0145 // for null token 0146 const Token Token::null; 0147 0148 // helper function: return operator of given token text 0149 // e.g. '*' yields Operator::Asterisk, and so on 0150 Token::Op Calligra::Sheets::matchOperator(const QString& text) 0151 { 0152 Token::Op result = Token::InvalidOp; 0153 0154 if (text.length() == 1) { 0155 QChar p = text[0]; 0156 switch (p.unicode()) { 0157 case '+': result = Token::Plus; break; 0158 case '-': result = Token::Minus; break; 0159 case '*': result = Token::Asterisk; break; 0160 case '/': result = Token::Slash; break; 0161 case '^': result = Token::Caret; break; 0162 case ',': result = Token::Comma; break; 0163 case ';': result = Token::Semicolon; break; 0164 case ' ': result = Token::Intersect; break; 0165 case '(': result = Token::LeftPar; break; 0166 case ')': result = Token::RightPar; break; 0167 case '&': result = Token::Ampersand; break; 0168 case '=': result = Token::Equal; break; 0169 case '<': result = Token::Less; break; 0170 case '>': result = Token::Greater; break; 0171 case '%': result = Token::Percent; break; 0172 case '~': result = Token::Union; break; 0173 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 0174 case '{': result = Token::CurlyBra; break; 0175 case '}': result = Token::CurlyKet; break; 0176 case '|': result = Token::Pipe; break; 0177 #endif 0178 #ifdef CALLIGRA_SHEETS_UNICODE_OPERATORS 0179 case 0x2212: result = Token::Minus; break; 0180 case 0x00D7: result = Token::Asterisk; break; 0181 case 0x00F7: result = Token::Slash; break; 0182 case 0x2215: result = Token::Slash; break; 0183 #endif 0184 default : result = Token::InvalidOp; break; 0185 } 0186 } 0187 0188 if (text.length() == 2) { 0189 if (text == "<>") result = Token::NotEqual; 0190 if (text == "!=") result = Token::NotEqual; 0191 if (text == "<=") result = Token::LessEqual; 0192 if (text == ">=") result = Token::GreaterEqual; 0193 if (text == "==") result = Token::Equal; 0194 } 0195 0196 return result; 0197 } 0198 0199 bool Calligra::Sheets::parseOperator(const QChar *&data, QChar *&out) 0200 { 0201 bool retval = true; 0202 switch(data->unicode()) { 0203 case '+': 0204 case '-': 0205 case '*': 0206 case '/': 0207 case '^': 0208 case ',': 0209 case ';': 0210 case ' ': 0211 case '(': 0212 case ')': 0213 case '&': 0214 case '%': 0215 case '~': 0216 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 0217 case '{': 0218 case '}': 0219 case '|': 0220 #endif 0221 #ifdef CALLIGRA_SHEETS_UNICODE_OPERATORS 0222 case 0x2212: 0223 case 0x00D7: 0224 case 0x00F7: 0225 case 0x2215: 0226 #endif 0227 *out = *data; 0228 ++out; 0229 ++data; 0230 break; 0231 case '<': 0232 *out = *data; 0233 ++out; 0234 ++data; 0235 if (!data->isNull()) { 0236 if (*data == QChar('>', 0) || *data == QChar('=', 0)) { 0237 *out = *data; 0238 ++out; 0239 ++data; 0240 } 0241 } 0242 break; 0243 case '>': 0244 *out = *data; 0245 ++out; 0246 ++data; 0247 if (!data->isNull() && *data == QChar('=', 0)) { 0248 *out = *data; 0249 ++out; 0250 ++data; 0251 } 0252 break; 0253 case '=': 0254 *out++ = *data++; 0255 if (!data->isNull() && *data == QChar('=', 0)) { 0256 *out++ = *data++; 0257 } 0258 break; 0259 case '!': { 0260 const QChar * next = data + 1; 0261 if (!next->isNull() && *next == QChar('=', 0)) { 0262 *out = *data; 0263 ++out; 0264 ++data; 0265 *out = *data; 0266 ++out; 0267 ++data; 0268 } 0269 else { 0270 retval = false; 0271 } 0272 } break; 0273 default: 0274 retval = false; 0275 break; 0276 } 0277 return retval; 0278 } 0279 0280 // helper function: give operator precedence 0281 // e.g. '+' is 1 while '*' is 3 0282 static int opPrecedence(Token::Op op) 0283 { 0284 int prec = -1; 0285 switch (op) { 0286 case Token::Percent : prec = 8; break; 0287 case Token::Caret : prec = 7; break; 0288 case Token::Asterisk : prec = 5; break; 0289 case Token::Slash : prec = 6; break; 0290 case Token::Plus : prec = 3; break; 0291 case Token::Minus : prec = 3; break; 0292 case Token::Union : prec = 2; break; 0293 case Token::Ampersand : prec = 2; break; 0294 case Token::Intersect : prec = 2; break; 0295 case Token::Equal : prec = 1; break; 0296 case Token::NotEqual : prec = 1; break; 0297 case Token::Less : prec = 1; break; 0298 case Token::Greater : prec = 1; break; 0299 case Token::LessEqual : prec = 1; break; 0300 case Token::GreaterEqual : prec = 1; break; 0301 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 0302 // FIXME Stefan: I don't know whether zero is right for this case. :-( 0303 case Token::CurlyBra : prec = 0; break; 0304 case Token::CurlyKet : prec = 0; break; 0305 case Token::Pipe : prec = 0; break; 0306 #endif 0307 case Token::Semicolon : prec = 0; break; 0308 case Token::RightPar : prec = 0; break; 0309 case Token::LeftPar : prec = -1; break; 0310 default: prec = -1; break; 0311 } 0312 return prec; 0313 } 0314 0315 // helper function 0316 static Value tokenAsValue(const Token& token) 0317 { 0318 Value value; 0319 if (token.isBoolean()) value = Value(token.asBoolean()); 0320 else if (token.isInteger()) value = Value(token.asInteger()); 0321 else if (token.isFloat()) value = Value(token.asFloat()); 0322 else if (token.isString()) value = Value(token.asString()); 0323 else if (token.isError()) { 0324 const QString error = token.asError(); 0325 if (error == Value::errorCIRCLE().errorMessage()) 0326 value = Value::errorCIRCLE(); 0327 else if (error == Value::errorDEPEND().errorMessage()) 0328 value = Value::errorDEPEND(); 0329 else if (error == Value::errorDIV0().errorMessage()) 0330 value = Value::errorDIV0(); 0331 else if (error == Value::errorNA().errorMessage()) 0332 value = Value::errorNA(); 0333 else if (error == Value::errorNAME().errorMessage()) 0334 value = Value::errorNAME(); 0335 else if (error == Value::errorNUM().errorMessage()) 0336 value = Value::errorNUM(); 0337 else if (error == Value::errorNULL().errorMessage()) 0338 value = Value::errorNULL(); 0339 else if (error == Value::errorPARSE().errorMessage()) 0340 value = Value::errorPARSE(); 0341 else if (error == Value::errorREF().errorMessage()) 0342 value = Value::errorREF(); 0343 else if (error == Value::errorVALUE().errorMessage()) 0344 value = Value::errorVALUE(); 0345 else { 0346 value = Value(Value::Error); 0347 value.setError(error); 0348 } 0349 } 0350 return value; 0351 } 0352 0353 /********************** 0354 Token 0355 **********************/ 0356 0357 // creates a token 0358 Token::Token(Type type, const QString& text, int pos) 0359 : m_type(type) 0360 , m_text(text) 0361 , m_pos(pos) 0362 { 0363 // the detach is needed as we manipulate the string we use as input afterwards 0364 // by writing to QChar * data point which does nto detach automatically. 0365 m_text.detach(); 0366 } 0367 0368 // copy constructor 0369 Token::Token(const Token& token) 0370 : m_type(token.m_type) 0371 , m_text(token.m_text) 0372 , m_pos(token.m_pos) 0373 { 0374 } 0375 0376 // assignment operator 0377 Token& Token::operator=(const Token & token) 0378 { 0379 m_type = token.m_type; 0380 m_text = token.m_text; 0381 m_pos = token.m_pos; 0382 return *this; 0383 } 0384 0385 bool Token::asBoolean() const 0386 { 0387 if (!isBoolean()) return false; 0388 return m_text.toLower() == "true"; 0389 // FIXME check also for i18n version 0390 } 0391 0392 qint64 Token::asInteger() const 0393 { 0394 if (isInteger()) return m_text.toLongLong(); 0395 else return 0; 0396 } 0397 0398 double Token::asFloat() const 0399 { 0400 if (isFloat()) return m_text.toDouble(); 0401 else return 0.0; 0402 } 0403 0404 QString Token::asString() const 0405 { 0406 if (!isString()) return QString(); 0407 QString res = m_text.mid(1, m_text.length() - 2); 0408 res = res.replace("\"\"", "\""); // double quotes to single quotes 0409 return res; 0410 } 0411 0412 QString Token::asError() const 0413 { 0414 if (isError()) 0415 return m_text; 0416 else 0417 return QString(); 0418 } 0419 0420 Token::Op Token::asOperator() const 0421 { 0422 if (isOperator()) return matchOperator(m_text); 0423 else return InvalidOp; 0424 } 0425 0426 QString Token::sheetName() const 0427 { 0428 if (!isCell() && !isRange()) return QString(); 0429 int i = m_text.indexOf('!'); 0430 if (i < 0) return QString(); 0431 QString sheet = m_text.left(i); 0432 return sheet; 0433 } 0434 0435 QString Token::description() const 0436 { 0437 QString desc; 0438 0439 switch (m_type) { 0440 case Boolean: desc = "Boolean"; break; 0441 case Integer: desc = "Integer"; break; 0442 case Float: desc = "Float"; break; 0443 case String: desc = "String"; break; 0444 case Identifier: desc = "Identifier"; break; 0445 case Cell: desc = "Cell"; break; 0446 case Range: desc = "Range"; break; 0447 case Operator: desc = "Operator"; break; 0448 case Error: desc = "Error"; break; 0449 default: desc = "Unknown"; break; 0450 } 0451 0452 while (desc.length() < 10) desc.prepend(' '); 0453 desc.prepend(" "); 0454 desc.prepend(QString::number(m_pos)); 0455 desc.append(" : ").append(m_text); 0456 0457 return desc; 0458 } 0459 0460 0461 /********************** 0462 TokenStack 0463 **********************/ 0464 0465 TokenStack::TokenStack(): QVector<Token>() 0466 { 0467 } 0468 0469 unsigned TokenStack::itemCount() const 0470 { 0471 return size(); 0472 } 0473 0474 void TokenStack::push(const Token& token) 0475 { 0476 append(token); 0477 } 0478 0479 Token TokenStack::pop() 0480 { 0481 if (!isEmpty()) { 0482 Token token = last(); 0483 pop_back(); 0484 return token; 0485 } 0486 return Token(); 0487 } 0488 0489 const Token& TokenStack::top() 0490 { 0491 return top(0); 0492 } 0493 0494 const Token& TokenStack::top(unsigned index) 0495 { 0496 unsigned top = size(); 0497 if (top > index) 0498 return at(top - index - 1); 0499 return Token::null; 0500 } 0501 0502 0503 /********************** 0504 FormulaPrivate 0505 **********************/ 0506 0507 // helper function: return true for valid identifier character 0508 bool Calligra::Sheets::isIdentifier(QChar ch) 0509 { 0510 switch(ch.unicode()) { 0511 case '_': 0512 case '$': 0513 case '.': 0514 return true; 0515 default: 0516 return ch.isLetter(); 0517 } 0518 } 0519 0520 0521 0522 0523 /********************** 0524 Formula 0525 **********************/ 0526 0527 // Constructor 0528 0529 Formula::Formula(Sheet *sheet, const Cell& cell) 0530 : d(new Private) 0531 { 0532 d->cell = cell; 0533 d->sheet = sheet; 0534 clear(); 0535 } 0536 0537 Formula::Formula(Sheet *sheet) 0538 : d(new Private) 0539 { 0540 d->cell = Cell(); 0541 d->sheet = sheet; 0542 clear(); 0543 } 0544 0545 Formula::Formula() 0546 : d(new Private) 0547 { 0548 d->cell = Cell(); 0549 d->sheet = 0; 0550 clear(); 0551 } 0552 0553 Formula Formula::empty() 0554 { 0555 static Formula f; 0556 return f; 0557 } 0558 0559 Formula::Formula(const Formula& other) 0560 : d(other.d) 0561 { 0562 } 0563 0564 // Destructor 0565 0566 Formula::~Formula() 0567 { 0568 } 0569 0570 const Cell& Formula::cell() const 0571 { 0572 return d->cell; 0573 } 0574 0575 Sheet* Formula::sheet() const 0576 { 0577 return d->sheet; 0578 } 0579 0580 // Sets a new expression for this formula. 0581 // note that both the real lex and parse processes will happen later on 0582 // when needed (i.e. "lazy parse"), for example during formula evaluation. 0583 0584 void Formula::setExpression(const QString& expr) 0585 { 0586 d->expression = expr; 0587 d->dirty = true; 0588 d->valid = false; 0589 } 0590 0591 // Returns the expression associated with this formula. 0592 0593 QString Formula::expression() const 0594 { 0595 return d->expression; 0596 } 0597 0598 // Returns the validity of the formula. 0599 // note: empty formula is always invalid. 0600 0601 bool Formula::isValid() const 0602 { 0603 if (d->dirty) { 0604 KLocale* locale = !d->cell.isNull() ? d->cell.locale() : 0; 0605 if ((!locale) && d->sheet) 0606 locale = d->sheet->map()->calculationSettings()->locale(); 0607 Tokens tokens = scan(d->expression, locale); 0608 0609 if (tokens.valid()) 0610 compile(tokens); 0611 else 0612 d->valid = false; 0613 } 0614 return d->valid; 0615 } 0616 0617 // Clears everything, also mark the formula as invalid. 0618 0619 void Formula::clear() 0620 { 0621 d->expression.clear(); 0622 d->dirty = true; 0623 d->valid = false; 0624 d->constants.clear(); 0625 d->codes.clear(); 0626 } 0627 0628 // Returns list of token for the expression. 0629 // this triggers again the lexical analysis step. it is however preferable 0630 // (even when there's small performance penalty) because otherwise we need to 0631 // store parsed tokens all the time which serves no good purpose. 0632 0633 Tokens Formula::tokens() const 0634 { 0635 KLocale* locale = !d->cell.isNull() ? d->cell.locale() : 0; 0636 if ((!locale) && d->sheet) 0637 locale = d->sheet->map()->calculationSettings()->locale(); 0638 return scan(d->expression, locale); 0639 } 0640 0641 Tokens Formula::scan(const QString &expr, const KLocale* locale) const 0642 { 0643 // parsing state 0644 enum { Start, Finish, InNumber, InDecimal, InExpIndicator, InExponent, 0645 InString, InIdentifier, InCell, InRange, InSheetOrAreaName, InError 0646 } state; 0647 0648 // use locale settings if specified 0649 QString thousand = locale ? locale->thousandsSeparator() : ""; 0650 QString decimal = locale ? locale->decimalSymbol() : "."; 0651 0652 const QChar *data = expr.constData(); 0653 0654 Tokens tokens; 0655 if (data->isNull() || *data != QChar('=', 0)) { 0656 return tokens; 0657 } 0658 tokens.reserve(50); 0659 0660 ++data; 0661 const QChar * const start = data; 0662 const QChar * const end = start + expr.length(); 0663 const QChar *tokenStart = data; 0664 const QChar *cellStart = data; 0665 0666 state = Start; 0667 bool parseError = false; 0668 0669 int length = expr.length() * 1.1; // TODO check if that is needed at all 0670 QString token(length, QChar()); 0671 token.reserve(length); // needed to not realloc at the resize at the end 0672 QChar * out = token.data(); 0673 QChar * const outStart = token.data(); 0674 0675 while (state != Finish && data < end) { 0676 switch (state) { 0677 case Start: 0678 tokenStart = data; 0679 // Whitespaces can be used as intersect-operator for two arrays. 0680 if (data->isSpace()) { 0681 ++data; 0682 } 0683 // check for number 0684 else if (data->isDigit()) { 0685 state = InNumber; 0686 *out++ = *data++; 0687 } 0688 // terminator character 0689 else if (data->isNull()) { 0690 state = Finish; 0691 } 0692 else { 0693 switch (data->unicode()) { 0694 case '"': // a string ? 0695 *out++ = *data++; 0696 state = InString; 0697 break; 0698 case '\'': // aposthrophe (') marks sheet name for 3-d cell, e.g 'Sales Q3'!A4, or a named range 0699 ++data; 0700 state = InSheetOrAreaName; 0701 break; 0702 case '#': // error value? 0703 *out++ = *data++; 0704 state = InError; 0705 break; 0706 default: 0707 // decimal dot ? 0708 if (*data == decimal[0]) { 0709 *out++ = *data++; 0710 state = InDecimal; 0711 } 0712 // beginning with alphanumeric ? 0713 // could be identifier, cell, range, or function... 0714 else if (isIdentifier(*data)) { 0715 *out++ = *data++; 0716 state = InIdentifier; 0717 } 0718 else { 0719 // look for operator match 0720 if (parseOperator(data, out)) { 0721 token.resize(out - outStart); 0722 tokens.append(Token(Token::Operator, token, tokenStart - start)); 0723 token.resize(length); 0724 out = outStart; 0725 } 0726 else { 0727 // not matched an operator, add an Unknown token and remember we had a parse error 0728 parseError = true; 0729 *out++ = *data++; 0730 token.resize(out - outStart); 0731 tokens.append(Token(Token::Unknown, token, tokenStart - start)); 0732 token.resize(length); 0733 out = outStart; 0734 } 0735 } 0736 break; 0737 } 0738 } 0739 break; 0740 case InIdentifier: 0741 // consume as long as alpha, dollar sign, underscore, or digit 0742 if (isIdentifier(*data) || data->isDigit()) { 0743 *out = *data; 0744 ++out; 0745 ++data; 0746 } 0747 // a '!' ? then this must be sheet name, e.g "Sheet4!", unless the next character is '=' 0748 else if (*data == QChar('!', 0) && !(data + 1)->isNull() && *(data + 1) != QChar('=', 0)) { 0749 *out++ = *data++; 0750 cellStart = out; 0751 state = InCell; 0752 } 0753 // a '(' ? then this must be a function identifier 0754 else if (*data == QChar('(', 0)) { 0755 token.resize(out - outStart); 0756 tokens.append(Token(Token::Identifier, token, tokenStart - start)); 0757 token.resize(length); 0758 out = outStart; 0759 state = Start; 0760 } 0761 // we're done with identifier 0762 else { 0763 *out = QChar(); 0764 // check for cell reference, e.g A1, VV123, ... 0765 if (Util::isCellReference(token)) { 0766 // so up to now we've got something like A2 or Sheet2!F4 0767 // check for range reference 0768 if (*data == QChar(':', 0)) { 0769 *out++ = *data++; 0770 state = InRange; 0771 } 0772 // we're done with cell reference 0773 else { 0774 token.resize(out - outStart); 0775 tokens.append(Token(Token::Cell, token, tokenStart - start)); 0776 token.resize(length); 0777 out = outStart; 0778 state = Start; 0779 } 0780 } 0781 else { 0782 token.resize(out - outStart); 0783 if (isNamedArea(token)) { 0784 tokens.append(Token(Token::Range, token, tokenStart - start)); 0785 } 0786 else { 0787 tokens.append(Token(Token::Identifier, token, tokenStart - start)); 0788 } 0789 token.resize(length); 0790 out = outStart; 0791 state = Start; 0792 } 0793 } 0794 break; 0795 case InCell: 0796 // consume as long as alpha, dollar sign, underscore, or digit 0797 if (isIdentifier(*data) || data->isDigit()) { 0798 *out++ = *data++; 0799 } 0800 else { 0801 *out = QChar(); 0802 // check if it's a cell ref like A32, not named area 0803 if (!Util::isCellReference(token, cellStart - outStart)) { 0804 // test failed, means we have something like "Sheet2!TotalSales" 0805 // and not "Sheet2!A2" 0806 // thus, assume so far that it's a named area 0807 token.resize(out - outStart); 0808 tokens.append(Token(Token::Range, token, tokenStart - start)); 0809 token.resize(length); 0810 out = outStart; 0811 state = Start; 0812 } 0813 else { 0814 // so up to now we've got something like A2 or Sheet2!F4 0815 // check for range reference 0816 if (*data == QChar(':', 0)) { 0817 *out++ = *data++; 0818 state = InRange; 0819 } 0820 else { 0821 // we're done with cell reference 0822 token.resize(out - outStart); 0823 tokens.append(Token(Token::Cell, token, tokenStart - start)); 0824 token.resize(length); 0825 out = outStart; 0826 state = Start; 0827 } 0828 } 0829 } 0830 break; 0831 case InRange: 0832 // consume as long as alpha, dollar sign, underscore, or digit or ! 0833 if (isIdentifier(*data) || data->isDigit() || *data == QChar('!', 0)) { 0834 *out++ = *data++; 0835 } 0836 // we're done with range reference 0837 else { 0838 token.resize(out - outStart); 0839 tokens.append(Token(Token::Range, token, tokenStart - start)); 0840 token.resize(length); 0841 out = outStart; 0842 state = Start; 0843 } 0844 break; 0845 case InSheetOrAreaName: 0846 // consume until ' 0847 if (data->isNull()) { 0848 parseError = true; 0849 token.resize(out - outStart); 0850 tokens.append(Token(Token::Unknown, '\'' + token + '\'', tokenStart - start)); 0851 state = Start; 0852 } 0853 else if (*data != QChar('\'', 0)) { 0854 *out++ = *data++; 0855 } 0856 else { 0857 // eat the aposthrophe itself 0858 ++data; 0859 // must be followed by '!' to be sheet name 0860 if (!data->isNull() && *data == QChar('!', 0)) { 0861 *out++ = *data++; 0862 cellStart = out; 0863 state = InCell; 0864 } 0865 else { 0866 token.resize(out - outStart); 0867 if (isNamedArea(token)) { 0868 tokens.append(Token(Token::Range, token, tokenStart - start)); 0869 } 0870 else { 0871 // for compatibility with oocalc (and the openformula spec), don't parse single-quoted 0872 // text as an identifier, instead add an Unknown token and remember we had an error 0873 parseError = true; 0874 tokens.append(Token(Token::Unknown, '\'' + token + '\'', tokenStart - start)); 0875 } 0876 token.resize(length); 0877 out = outStart; 0878 state = Start; 0879 } 0880 } 0881 break; 0882 case InNumber: 0883 // consume as long as it's digit 0884 if (data->isDigit()) { 0885 *out++ = *data++; 0886 } 0887 // skip thousand separator 0888 else if (!thousand.isEmpty() && (*data == thousand[0])) { 0889 ++data; 0890 } 0891 // convert decimal separator to '.', also support '.' directly 0892 // we always support '.' because of bug #98455 0893 else if ((!decimal.isEmpty() && (*data == decimal[0])) || *data == QChar('.', 0)) { 0894 *out++ = QChar('.', 0); 0895 ++data; 0896 state = InDecimal; 0897 } 0898 // exponent ? 0899 else if (*data == QChar('E', 0) || *data == QChar('e', 0)) { 0900 *out++ = QChar('E', 0); 0901 ++data; 0902 state = InExpIndicator; 0903 } 0904 // reference sheet delimiter? 0905 else if (*data == QChar('!', 0)) { 0906 *out++ = *data++; 0907 cellStart = out; 0908 state = InCell; 0909 } 0910 // identifier? 0911 else if (isIdentifier(*data)) { 0912 // has to be a sheet or area name then 0913 *out++ = *data++; 0914 state = InIdentifier; 0915 } 0916 // we're done with integer number 0917 else { 0918 token.resize(out - outStart); 0919 tokens.append(Token(Token::Integer, token, tokenStart - start)); 0920 token.resize(length); 0921 out = outStart; 0922 state = Start; 0923 } 0924 break; 0925 case InDecimal: 0926 // consume as long as it's digit 0927 if (data->isDigit()) { 0928 *out++ = *data++; 0929 } 0930 // exponent ? 0931 else if (*data == QChar('E', 0) || *data == QChar('e', 0)) { 0932 *out++ = QChar('E', 0); 0933 ++data; 0934 state = InExpIndicator; 0935 } 0936 // we're done with floating-point number 0937 else { 0938 token.resize(out - outStart); 0939 tokens.append(Token(Token::Float, token, tokenStart - start)); 0940 token.resize(length); 0941 out = outStart; 0942 state = Start; 0943 } 0944 break; 0945 case InExpIndicator: 0946 // possible + or - right after E, e.g 1.23E+12 or 4.67E-8 0947 if (*data == QChar('+', 0) || *data == QChar('-', 0)) { 0948 *out++ = *data++; 0949 } 0950 // consume as long as it's digit 0951 else if (data->isDigit()) { 0952 *out++ = *data++; 0953 state = InExponent; 0954 } 0955 // invalid thing here 0956 else { 0957 parseError = true; 0958 token.resize(out - outStart); 0959 tokens.append(Token(Token::Unknown, token, tokenStart - start)); 0960 token.resize(length); 0961 out = outStart; 0962 state = Start; 0963 } 0964 break; 0965 case InExponent: 0966 // consume as long as it's digit 0967 if (data->isDigit()) { 0968 *out++ = *data++; 0969 } 0970 // we're done with floating-point number 0971 else { 0972 token.resize(out - outStart); 0973 tokens.append(Token(Token::Float, token, tokenStart - start)); 0974 token.resize(length); 0975 out = outStart; 0976 state = Start; 0977 } 0978 break; 0979 case InString: 0980 // consume until " 0981 if (*data != QChar('"', 0)) { 0982 *out++ = *data++; 0983 } 0984 else { 0985 *out++ = *data++; 0986 // check for escaped "" 0987 if ((!data->isNull()) && (*data == QChar('"', 0))) { 0988 *out++ = *data++; 0989 } else { 0990 token.resize(out - outStart); 0991 tokens.append(Token(Token::String, token, tokenStart - start)); 0992 token.resize(length); 0993 out = outStart; 0994 state = Start; 0995 } 0996 } 0997 break; 0998 case InError: { 0999 ushort c = data->unicode(); 1000 switch (c) { 1001 case '!': 1002 case '?': 1003 // TODO check if there is at least one char that needs to be there 1004 *out++ = *data++; 1005 token.resize(out - outStart); 1006 tokens.append(Token(Token::Error, token, tokenStart - start)); 1007 token.resize(length); 1008 out = outStart; 1009 state = Start; 1010 break; 1011 case '/': 1012 *out++ = *data++; 1013 if (!data->isNull()) { 1014 bool error = false; 1015 if (*data >= 'A' && *data <= 'Z') { 1016 *out++ = *data++; 1017 } 1018 else if (*data >= '0' && *data <= '9'){ 1019 *out++ = *data++; 1020 if (!data->isNull() && (*data == QChar('!', 0) || *data == QChar('?', 0))) { 1021 *out++ = *data++; 1022 } 1023 } 1024 else { 1025 error = true; 1026 } 1027 if (error) { 1028 parseError = true; 1029 token.resize(out - outStart); 1030 tokens.append(Token(Token::Unknown, token, tokenStart - start)); 1031 token.resize(length); 1032 out = outStart; 1033 state = Start; 1034 } 1035 else { 1036 token.resize(out - outStart); 1037 tokens.append(Token(Token::Error, token, tokenStart - start)); 1038 token.resize(length); 1039 out = outStart; 1040 state = Start; 1041 } 1042 } 1043 break; 1044 default: 1045 if ((c >= 'A' && c <= 'Z') || (c >= '0' && c<= '9')) { 1046 *out++ = *data++; 1047 } 1048 else { 1049 parseError = true; 1050 token.resize(out - outStart); 1051 tokens.append(Token(Token::Unknown, token, tokenStart - start)); 1052 token.resize(length); 1053 out = outStart; 1054 state = Start; 1055 } 1056 break; 1057 } 1058 } break; 1059 default: 1060 break; 1061 } 1062 } 1063 1064 // parse error if any text remains 1065 if (data+1 < end) { 1066 tokens.append(Token(Token::Unknown, expr.mid(tokenStart - start), tokenStart - start)); 1067 parseError = true; 1068 } 1069 1070 if (parseError) 1071 tokens.setValid(false); 1072 return tokens; 1073 } 1074 1075 // will affect: dirty, valid, codes, constants 1076 void Formula::compile(const Tokens& tokens) const 1077 { 1078 // initialize variables 1079 d->dirty = false; 1080 d->valid = false; 1081 d->codes.clear(); 1082 d->constants.clear(); 1083 1084 // sanity check 1085 if (tokens.count() == 0) return; 1086 1087 TokenStack syntaxStack; 1088 QStack<int> argStack; 1089 unsigned argCount = 1; 1090 1091 for (int i = 0; i <= tokens.count(); i++) { 1092 // helper token: InvalidOp is end-of-formula 1093 Token token = (i < tokens.count()) ? tokens[i] : Token(Token::Operator); 1094 Token::Type tokenType = token.type(); 1095 1096 // unknown token is invalid 1097 if (tokenType == Token::Unknown) break; 1098 1099 // are we entering a function ? 1100 // if stack already has: id ( 1101 if (syntaxStack.itemCount() >= 2) { 1102 Token par = syntaxStack.top(); 1103 Token id = syntaxStack.top(1); 1104 if (par.asOperator() == Token::LeftPar) 1105 if (id.isIdentifier()) { 1106 argStack.push(argCount); 1107 argCount = 1; 1108 } 1109 } 1110 1111 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 1112 // are we entering an inline array ? 1113 // if stack already has: { 1114 if (syntaxStack.itemCount() >= 1) { 1115 Token bra = syntaxStack.top(); 1116 if (bra.asOperator() == Token::CurlyBra) { 1117 argStack.push(argCount); 1118 argStack.push(1); // row count 1119 argCount = 1; 1120 } 1121 } 1122 #endif 1123 1124 // for constants, push immediately to stack 1125 // generate code to load from a constant 1126 if ((tokenType == Token::Integer) || (tokenType == Token::Float) || 1127 (tokenType == Token::String) || (tokenType == Token::Boolean) || 1128 (tokenType == Token::Error)) { 1129 syntaxStack.push(token); 1130 d->constants.append(tokenAsValue(token)); 1131 d->codes.append(Opcode(Opcode::Load, d->constants.count() - 1)); 1132 } 1133 1134 // for cell, range, or identifier, push immediately to stack 1135 // generate code to load from reference 1136 if ((tokenType == Token::Cell) || (tokenType == Token::Range) || 1137 (tokenType == Token::Identifier)) { 1138 syntaxStack.push(token); 1139 d->constants.append(Value(token.text())); 1140 if (tokenType == Token::Cell) 1141 d->codes.append(Opcode(Opcode::Cell, d->constants.count() - 1)); 1142 else if (tokenType == Token::Range) 1143 d->codes.append(Opcode(Opcode::Range, d->constants.count() - 1)); 1144 else 1145 d->codes.append(Opcode(Opcode::Ref, d->constants.count() - 1)); 1146 } 1147 1148 // special case for percentage 1149 if (tokenType == Token::Operator) 1150 if (token.asOperator() == Token::Percent) 1151 if (syntaxStack.itemCount() >= 1) 1152 if (!syntaxStack.top().isOperator()) { 1153 d->constants.append(Value(0.01)); 1154 d->codes.append(Opcode(Opcode::Load, d->constants.count() - 1)); 1155 d->codes.append(Opcode(Opcode::Mul)); 1156 } 1157 1158 // for any other operator, try to apply all parsing rules 1159 if (tokenType == Token::Operator) 1160 if (token.asOperator() != Token::Percent) { 1161 // repeat until no more rule applies 1162 for (; ;) { 1163 bool ruleFound = false; 1164 1165 // rule for function arguments, if token is ; or ) 1166 // id ( arg1 ; arg2 -> id ( arg 1167 if (!ruleFound) 1168 if (syntaxStack.itemCount() >= 5) 1169 if ((token.asOperator() == Token::RightPar) || 1170 (token.asOperator() == Token::Semicolon)) { 1171 Token arg2 = syntaxStack.top(); 1172 Token sep = syntaxStack.top(1); 1173 Token arg1 = syntaxStack.top(2); 1174 Token par = syntaxStack.top(3); 1175 Token id = syntaxStack.top(4); 1176 if (!arg2.isOperator()) 1177 if (sep.asOperator() == Token::Semicolon) 1178 if (!arg1.isOperator()) 1179 if (par.asOperator() == Token::LeftPar) 1180 if (id.isIdentifier()) { 1181 ruleFound = true; 1182 syntaxStack.pop(); 1183 syntaxStack.pop(); 1184 argCount++; 1185 } 1186 } 1187 1188 // rule for empty function arguments, if token is ; or ) 1189 // id ( arg ; -> id ( arg 1190 if (!ruleFound) 1191 if (syntaxStack.itemCount() >= 3) 1192 if ((token.asOperator() == Token::RightPar) || 1193 (token.asOperator() == Token::Semicolon)) { 1194 Token sep = syntaxStack.top(); 1195 Token arg = syntaxStack.top(1); 1196 Token par = syntaxStack.top(2); 1197 Token id = syntaxStack.top(3); 1198 if (sep.asOperator() == Token::Semicolon) 1199 if (!arg.isOperator()) 1200 if (par.asOperator() == Token::LeftPar) 1201 if (id.isIdentifier()) { 1202 ruleFound = true; 1203 syntaxStack.pop(); 1204 d->constants.append(Value::null()); 1205 d->codes.append(Opcode(Opcode::Load, d->constants.count() - 1)); 1206 argCount++; 1207 } 1208 } 1209 1210 // rule for function last argument: 1211 // id ( arg ) -> arg 1212 if (!ruleFound) 1213 if (syntaxStack.itemCount() >= 4) { 1214 Token par2 = syntaxStack.top(); 1215 Token arg = syntaxStack.top(1); 1216 Token par1 = syntaxStack.top(2); 1217 Token id = syntaxStack.top(3); 1218 if (par2.asOperator() == Token::RightPar) 1219 if (!arg.isOperator()) 1220 if (par1.asOperator() == Token::LeftPar) 1221 if (id.isIdentifier()) { 1222 ruleFound = true; 1223 syntaxStack.pop(); 1224 syntaxStack.pop(); 1225 syntaxStack.pop(); 1226 syntaxStack.pop(); 1227 syntaxStack.push(arg); 1228 d->codes.append(Opcode(Opcode::Function, argCount)); 1229 Q_ASSERT(!argStack.empty()); 1230 argCount = argStack.empty() ? 0 : argStack.pop(); 1231 } 1232 } 1233 1234 // rule for function call with parentheses, but without argument 1235 // e.g. "2*PI()" 1236 if (!ruleFound) 1237 if (syntaxStack.itemCount() >= 3) { 1238 Token par2 = syntaxStack.top(); 1239 Token par1 = syntaxStack.top(1); 1240 Token id = syntaxStack.top(2); 1241 if (par2.asOperator() == Token::RightPar) 1242 if (par1.asOperator() == Token::LeftPar) 1243 if (id.isIdentifier()) { 1244 ruleFound = true; 1245 syntaxStack.pop(); 1246 syntaxStack.pop(); 1247 syntaxStack.pop(); 1248 syntaxStack.push(Token(Token::Integer)); 1249 d->codes.append(Opcode(Opcode::Function, 0)); 1250 Q_ASSERT(!argStack.empty()); 1251 argCount = argStack.empty() ? 0 : argStack.pop(); 1252 } 1253 } 1254 1255 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 1256 // rule for inline array elements, if token is ; or | or } 1257 // { arg1 ; arg2 -> { arg 1258 if (!ruleFound) 1259 if (syntaxStack.itemCount() >= 4) 1260 if ((token.asOperator() == Token::Semicolon) || 1261 (token.asOperator() == Token::CurlyKet) || 1262 (token.asOperator() == Token::Pipe)) { 1263 Token arg2 = syntaxStack.top(); 1264 Token sep = syntaxStack.top(1); 1265 Token arg1 = syntaxStack.top(2); 1266 Token bra = syntaxStack.top(3); 1267 if (!arg2.isOperator()) 1268 if (sep.asOperator() == Token::Semicolon) 1269 if (!arg1.isOperator()) 1270 if (bra.asOperator() == Token::CurlyBra) { 1271 ruleFound = true; 1272 syntaxStack.pop(); 1273 syntaxStack.pop(); 1274 argCount++; 1275 } 1276 } 1277 1278 // rule for last array row element, if token is ; or | or } 1279 // { arg1 | arg2 -> { arg 1280 if (!ruleFound) 1281 if (syntaxStack.itemCount() >= 4) 1282 if ((token.asOperator() == Token::Semicolon) || 1283 (token.asOperator() == Token::CurlyKet) || 1284 (token.asOperator() == Token::Pipe)) { 1285 Token arg2 = syntaxStack.top(); 1286 Token sep = syntaxStack.top(1); 1287 Token arg1 = syntaxStack.top(2); 1288 Token bra = syntaxStack.top(3); 1289 if (!arg2.isOperator()) 1290 if (sep.asOperator() == Token::Pipe) 1291 if (!arg1.isOperator()) 1292 if (bra.asOperator() == Token::CurlyBra) { 1293 ruleFound = true; 1294 syntaxStack.pop(); 1295 syntaxStack.pop(); 1296 int rowCount = argStack.pop(); 1297 argStack.push(++rowCount); 1298 argCount = 1; 1299 } 1300 } 1301 1302 // rule for last array element: 1303 // { arg } -> arg 1304 if (!ruleFound) 1305 if (syntaxStack.itemCount() >= 3) { 1306 Token ket = syntaxStack.top(); 1307 Token arg = syntaxStack.top(1); 1308 Token bra = syntaxStack.top(2); 1309 if (ket.asOperator() == Token::CurlyKet) 1310 if (!arg.isOperator()) 1311 if (bra.asOperator() == Token::CurlyBra) { 1312 ruleFound = true; 1313 syntaxStack.pop(); 1314 syntaxStack.pop(); 1315 syntaxStack.pop(); 1316 syntaxStack.push(arg); 1317 const int rowCount = argStack.pop(); 1318 d->constants.append(Value((int)argCount)); // cols 1319 d->constants.append(Value(rowCount)); 1320 d->codes.append(Opcode(Opcode::Array, d->constants.count() - 2)); 1321 Q_ASSERT(!argStack.empty()); 1322 argCount = argStack.empty() ? 0 : argStack.pop(); 1323 } 1324 } 1325 #endif 1326 // rule for parenthesis: ( Y ) -> Y 1327 if (!ruleFound) 1328 if (syntaxStack.itemCount() >= 3) { 1329 Token right = syntaxStack.top(); 1330 Token y = syntaxStack.top(1); 1331 Token left = syntaxStack.top(2); 1332 if (right.isOperator()) 1333 if (!y.isOperator()) 1334 if (left.isOperator()) 1335 if (right.asOperator() == Token::RightPar) 1336 if (left.asOperator() == Token::LeftPar) { 1337 ruleFound = true; 1338 syntaxStack.pop(); 1339 syntaxStack.pop(); 1340 syntaxStack.pop(); 1341 syntaxStack.push(y); 1342 } 1343 } 1344 1345 // rule for binary operator: A (op) B -> A 1346 // conditions: precedence of op >= precedence of token 1347 // action: push (op) to result 1348 // e.g. "A * B" becomes 'A' if token is operator '+' 1349 if (!ruleFound) 1350 if (syntaxStack.itemCount() >= 3) { 1351 Token b = syntaxStack.top(); 1352 Token op = syntaxStack.top(1); 1353 Token a = syntaxStack.top(2); 1354 if (!a.isOperator()) 1355 if (!b.isOperator()) 1356 if (op.isOperator()) 1357 if (token.asOperator() != Token::LeftPar) 1358 if (opPrecedence(op.asOperator()) >= opPrecedence(token.asOperator())) { 1359 ruleFound = true; 1360 syntaxStack.pop(); 1361 syntaxStack.pop(); 1362 syntaxStack.pop(); 1363 syntaxStack.push(b); 1364 switch (op.asOperator()) { 1365 // simple binary operations 1366 case Token::Plus: d->codes.append(Opcode::Add); break; 1367 case Token::Minus: d->codes.append(Opcode::Sub); break; 1368 case Token::Asterisk: d->codes.append(Opcode::Mul); break; 1369 case Token::Slash: d->codes.append(Opcode::Div); break; 1370 case Token::Caret: d->codes.append(Opcode::Pow); break; 1371 case Token::Ampersand: d->codes.append(Opcode::Concat); break; 1372 case Token::Intersect: d->codes.append(Opcode::Intersect); break; 1373 case Token::Union: d->codes.append(Opcode::Union); break; 1374 1375 // simple value comparisons 1376 case Token::Equal: d->codes.append(Opcode::Equal); break; 1377 case Token::Less: d->codes.append(Opcode::Less); break; 1378 case Token::Greater: d->codes.append(Opcode::Greater); break; 1379 1380 // NotEqual is Equal, followed by Not 1381 case Token::NotEqual: 1382 d->codes.append(Opcode::Equal); 1383 d->codes.append(Opcode::Not); 1384 break; 1385 1386 // LessOrEqual is Greater, followed by Not 1387 case Token::LessEqual: 1388 d->codes.append(Opcode::Greater); 1389 d->codes.append(Opcode::Not); 1390 break; 1391 1392 // GreaterOrEqual is Less, followed by Not 1393 case Token::GreaterEqual: 1394 d->codes.append(Opcode::Less); 1395 d->codes.append(Opcode::Not); 1396 break; 1397 default: break; 1398 }; 1399 } 1400 } 1401 1402 // rule for unary operator: (op1) (op2) X -> (op1) X 1403 // conditions: op2 is unary, token is not '(' 1404 // action: push (op2) to result 1405 // e.g. "* - 2" becomes '*' 1406 if (!ruleFound) 1407 if (token.asOperator() != Token::LeftPar) 1408 if (syntaxStack.itemCount() >= 3) { 1409 Token x = syntaxStack.top(); 1410 Token op2 = syntaxStack.top(1); 1411 Token op1 = syntaxStack.top(2); 1412 if (!x.isOperator()) 1413 if (op1.isOperator()) 1414 if (op2.isOperator()) 1415 if ((op2.asOperator() == Token::Plus) || 1416 (op2.asOperator() == Token::Minus)) { 1417 ruleFound = true; 1418 syntaxStack.pop(); 1419 syntaxStack.pop(); 1420 syntaxStack.push(x); 1421 if (op2.asOperator() == Token::Minus) 1422 d->codes.append(Opcode(Opcode::Neg)); 1423 } 1424 } 1425 1426 // auxiliary rule for unary operator: (op) X -> X 1427 // conditions: op is unary, op is first in syntax stack, token is not '(' 1428 // action: push (op) to result 1429 if (!ruleFound) 1430 if (token.asOperator() != Token::LeftPar) 1431 if (syntaxStack.itemCount() == 2) { 1432 Token x = syntaxStack.top(); 1433 Token op = syntaxStack.top(1); 1434 if (!x.isOperator()) 1435 if (op.isOperator()) 1436 if ((op.asOperator() == Token::Plus) || 1437 (op.asOperator() == Token::Minus)) { 1438 ruleFound = true; 1439 syntaxStack.pop(); 1440 syntaxStack.pop(); 1441 syntaxStack.push(x); 1442 if (op.asOperator() == Token::Minus) 1443 d->codes.append(Opcode(Opcode::Neg)); 1444 } 1445 } 1446 1447 if (!ruleFound) break; 1448 } 1449 1450 // can't apply rules anymore, push the token 1451 if (token.asOperator() != Token::Percent) 1452 syntaxStack.push(token); 1453 } 1454 } 1455 1456 // syntaxStack must left only one operand and end-of-formula (i.e. InvalidOp) 1457 d->valid = false; 1458 if (syntaxStack.itemCount() == 2) 1459 if (syntaxStack.top().isOperator()) 1460 if (syntaxStack.top().asOperator() == Token::InvalidOp) 1461 if (!syntaxStack.top(1).isOperator()) 1462 d->valid = true; 1463 1464 // bad parsing ? clean-up everything 1465 if (!d->valid) { 1466 d->constants.clear(); 1467 d->codes.clear(); 1468 } 1469 } 1470 1471 bool Formula::isNamedArea(const QString& expr) const 1472 { 1473 return d->sheet ? d->sheet->map()->namedAreaManager()->contains(expr) : false; 1474 } 1475 1476 1477 // Evaluates the formula, returns the result. 1478 1479 // evaluate the cellIndirections 1480 Value Formula::eval(CellIndirection cellIndirections) const 1481 { 1482 QHash<Cell, Value> values; 1483 return evalRecursive(cellIndirections, values); 1484 } 1485 1486 // We need to unroll arrays. Do use the same logic to unroll like OpenOffice.org and Excel are using. 1487 Value Formula::Private::valueOrElement(FuncExtra &fe, const stackEntry& entry) const 1488 { 1489 const Value& v = entry.val; 1490 const Region& region = entry.reg; 1491 if(v.isArray()) { 1492 if(v.count() == 1) // if there is only one item, use that one 1493 return v.element(0); 1494 1495 if(region.isValid() && entry.regIsNamedOrLabeled) { 1496 const QPoint position = region.firstRange().topLeft(); 1497 const int idx = fe.myrow - position.y(); // do we need to do the same for columns? 1498 if(idx >= 0 && idx < int(v.count())) 1499 return v.element(idx); // within the range returns the selected element 1500 } 1501 } 1502 return v; 1503 } 1504 1505 // On OO.org Calc and MS Excel operations done with +, -, * and / do fail if one of the values is 1506 // non-numeric. This differs from formulas like SUM which just ignores non numeric values. 1507 Value numericOrError(const ValueConverter* converter, const Value &v) 1508 { 1509 switch (v.type()) { 1510 case Value::Empty: 1511 case Value::Boolean: 1512 case Value::Integer: 1513 case Value::Float: 1514 case Value::Complex: 1515 case Value::Error: 1516 return v; 1517 case Value::String: { 1518 if (v.asString().isEmpty()) 1519 return v; 1520 bool ok; 1521 converter->asNumeric(v, &ok); 1522 if (ok) 1523 return v; 1524 } break; 1525 case Value::Array: 1526 case Value::CellRange: 1527 return v; 1528 } 1529 return Value::errorVALUE(); 1530 } 1531 1532 Value Formula::evalRecursive(CellIndirection cellIndirections, QHash<Cell, Value>& values) const 1533 { 1534 QStack<stackEntry> stack; 1535 stackEntry entry; 1536 int index; 1537 Value val1, val2; 1538 QString c; 1539 QVector<Value> args; 1540 1541 const Map* map = d->sheet ? d->sheet->map() : new Map(0 /*document*/); 1542 const ValueConverter* converter = map->converter(); 1543 ValueCalc* calc = map->calc(); 1544 1545 QSharedPointer<Function> function; 1546 FuncExtra fe; 1547 fe.mycol = fe.myrow = 0; 1548 if (!d->cell.isNull()) { 1549 fe.mycol = d->cell.column(); 1550 fe.myrow = d->cell.row(); 1551 } 1552 1553 if (d->dirty) { 1554 Tokens tokens = scan(d->expression); 1555 d->valid = tokens.valid(); 1556 if (tokens.valid()) 1557 compile(tokens); 1558 } 1559 1560 if (!d->valid) 1561 return Value::errorPARSE(); 1562 1563 for (int pc = 0; pc < d->codes.count(); pc++) { 1564 Value ret; // for the function caller 1565 Opcode& opcode = d->codes[pc]; 1566 index = opcode.index; 1567 switch (opcode.type) { 1568 // no operation 1569 case Opcode::Nop: 1570 break; 1571 1572 // load a constant, push to stack 1573 case Opcode::Load: 1574 entry.reset(); 1575 entry.val = d->constants[index]; 1576 stack.push(entry); 1577 break; 1578 1579 // unary operation 1580 case Opcode::Neg: 1581 entry.reset(); 1582 entry.val = d->valueOrElement(fe, stack.pop()); 1583 if (!entry.val.isError()) // do nothing if we got an error 1584 entry.val = calc->mul(entry.val, -1); 1585 stack.push(entry); 1586 break; 1587 1588 // binary operation: take two values from stack, do the operation, 1589 // push the result to stack 1590 case Opcode::Add: 1591 entry.reset(); 1592 val2 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1593 val1 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1594 val2 = calc->add(val1, val2); 1595 entry.reset(); 1596 entry.val = val2; 1597 stack.push(entry); 1598 break; 1599 1600 case Opcode::Sub: 1601 val2 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1602 val1 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1603 val2 = calc->sub(val1, val2); 1604 entry.reset(); 1605 entry.val = val2; 1606 stack.push(entry); 1607 break; 1608 1609 case Opcode::Mul: 1610 val2 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1611 val1 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1612 val2 = calc->mul(val1, val2); 1613 entry.reset(); 1614 entry.val = val2; 1615 stack.push(entry); 1616 break; 1617 1618 case Opcode::Div: 1619 val2 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1620 val1 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1621 val2 = calc->div(val1, val2); 1622 entry.reset(); 1623 entry.val = val2; 1624 stack.push(entry); 1625 break; 1626 1627 case Opcode::Pow: 1628 val2 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1629 val1 = numericOrError(converter, d->valueOrElement(fe, stack.pop())); 1630 val2 = calc->pow(val1, val2); 1631 entry.reset(); 1632 entry.val = val2; 1633 stack.push(entry); 1634 break; 1635 1636 // string concatenation 1637 case Opcode::Concat: 1638 val1 = converter->asString(stack.pop().val); 1639 val2 = converter->asString(stack.pop().val); 1640 if (val1.isError() || val2.isError()) 1641 val1 = Value::errorVALUE(); 1642 else 1643 val1 = Value(val2.asString().append(val1.asString())); 1644 entry.reset(); 1645 entry.val = val1; 1646 stack.push(entry); 1647 break; 1648 1649 // array intersection 1650 case Opcode::Intersect: { 1651 val1 = stack.pop().val; 1652 val2 = stack.pop().val; 1653 Region r1(d->constants[index].asString(), map, d->sheet); 1654 Region r2(d->constants[index+1].asString(), map, d->sheet); 1655 if(!r1.isValid() || !r2.isValid()) { 1656 val1 = Value::errorNULL(); 1657 } else { 1658 Region r = r1.intersected(r2); 1659 QRect rect = r.boundingRect(); 1660 Cell cell; 1661 if(rect.top() == rect.bottom()) 1662 cell = Cell(r.firstSheet(), fe.mycol, rect.top()); 1663 else if(rect.left() == rect.right()) 1664 cell = Cell(r.firstSheet(), rect.left(), fe.mycol); 1665 if(cell.isNull()) 1666 val1 = Value::errorNULL(); 1667 else if(cell.isEmpty()) 1668 val1 = Value::errorNULL(); 1669 else 1670 val1 = cell.value(); 1671 } 1672 entry.reset(); 1673 entry.val = val1; 1674 stack.push(entry); 1675 } break; 1676 1677 // region union 1678 case Opcode::Union: { 1679 Region r = stack.pop().reg; 1680 Region r2 = stack.pop().reg; 1681 entry.reset(); 1682 if (!r.isValid() || !r2.isValid()) { 1683 val1 = Value::errorVALUE(); 1684 r = Region(); 1685 } else { 1686 r.add(r2); 1687 r.firstSheet()->cellStorage()->valueRegion(r); 1688 // store the reference, so we can use it within functions (not entirely correct) 1689 entry.col1 = r.boundingRect().left(); 1690 entry.row1 = r.boundingRect().top(); 1691 entry.col2 = r.boundingRect().right(); 1692 entry.row2 = r.boundingRect().bottom(); 1693 } 1694 entry.val = val1; 1695 entry.reg = r; 1696 stack.push(entry); 1697 } break; 1698 1699 // logical not 1700 case Opcode::Not: 1701 val1 = converter->asBoolean(d->valueOrElement(fe, stack.pop())); 1702 if (val1.isError()) 1703 val1 = Value::errorVALUE(); 1704 else 1705 val1 = Value(!val1.asBoolean()); 1706 entry.reset(); 1707 entry.val = val1; 1708 stack.push(entry); 1709 break; 1710 1711 // comparison 1712 case Opcode::Equal: 1713 val1 = d->valueOrElement(fe, stack.pop()); 1714 val2 = d->valueOrElement(fe, stack.pop()); 1715 if (val1.isError()) 1716 ; 1717 else if (val2.isError()) 1718 val1 = val2; 1719 else if (val2.compare(val1, calc->settings()->caseSensitiveComparisons()) == 0) 1720 val1 = Value(true); 1721 else 1722 val1 = Value(false); 1723 entry.reset(); 1724 entry.val = val1; 1725 stack.push(entry); 1726 break; 1727 1728 // less than 1729 case Opcode::Less: 1730 val1 = d->valueOrElement(fe, stack.pop()); 1731 val2 = d->valueOrElement(fe, stack.pop()); 1732 if (val1.isError()) 1733 ; 1734 else if (val2.isError()) 1735 val1 = val2; 1736 else if (val2.compare(val1, calc->settings()->caseSensitiveComparisons()) < 0) 1737 val1 = Value(true); 1738 else 1739 val1 = Value(false); 1740 entry.reset(); 1741 entry.val = val1; 1742 stack.push(entry); 1743 break; 1744 1745 // greater than 1746 case Opcode::Greater: { 1747 val1 = d->valueOrElement(fe, stack.pop()); 1748 val2 = d->valueOrElement(fe, stack.pop()); 1749 if (val1.isError()) 1750 ; 1751 else if (val2.isError()) 1752 val1 = val2; 1753 else if (val2.compare(val1, calc->settings()->caseSensitiveComparisons()) > 0) 1754 val1 = Value(true); 1755 else 1756 val1 = Value(false); 1757 entry.reset(); 1758 entry.val = val1; 1759 stack.push(entry); 1760 } 1761 break; 1762 1763 // cell in a sheet 1764 case Opcode::Cell: { 1765 c = d->constants[index].asString(); 1766 val1 = Value::empty(); 1767 entry.reset(); 1768 1769 const Region region(c, map, d->sheet); 1770 if (!region.isValid()) { 1771 val1 = Value::errorREF(); 1772 } else if (region.isSingular()) { 1773 const QPoint position = region.firstRange().topLeft(); 1774 if (cellIndirections.isEmpty()) 1775 val1 = Cell(region.firstSheet(), position).value(); 1776 else { 1777 Cell cell(region.firstSheet(), position); 1778 cell = cellIndirections.value(cell, cell); 1779 if (values.contains(cell)) 1780 val1 = values.value(cell); 1781 else { 1782 values[cell] = Value::errorCIRCLE(); 1783 if (cell.isFormula()) 1784 val1 = cell.formula().evalRecursive(cellIndirections, values); 1785 else 1786 val1 = cell.value(); 1787 values[cell] = val1; 1788 } 1789 } 1790 // store the reference, so we can use it within functions 1791 entry.col1 = entry.col2 = position.x(); 1792 entry.row1 = entry.row2 = position.y(); 1793 entry.reg = region; 1794 entry.regIsNamedOrLabeled = map->namedAreaManager()->contains(c); 1795 } else { 1796 warnSheets << "Unhandled non singular region in Opcode::Cell with rects=" << region.rects(); 1797 } 1798 entry.val = val1; 1799 stack.push(entry); 1800 } 1801 break; 1802 1803 // selected range in a sheet 1804 case Opcode::Range: { 1805 c = d->constants[index].asString(); 1806 val1 = Value::empty(); 1807 entry.reset(); 1808 1809 const Region region(c, map, d->sheet); 1810 if (region.isValid()) { 1811 val1 = region.firstSheet()->cellStorage()->valueRegion(region); 1812 // store the reference, so we can use it within functions 1813 entry.col1 = region.firstRange().left(); 1814 entry.row1 = region.firstRange().top(); 1815 entry.col2 = region.firstRange().right(); 1816 entry.row2 = region.firstRange().bottom(); 1817 entry.reg = region; 1818 entry.regIsNamedOrLabeled = map->namedAreaManager()->contains(c); 1819 } 1820 1821 entry.val = val1; // any array is valid here 1822 stack.push(entry); 1823 } 1824 break; 1825 1826 // reference 1827 case Opcode::Ref: 1828 val1 = d->constants[index]; 1829 entry.reset(); 1830 entry.val = val1; 1831 stack.push(entry); 1832 break; 1833 1834 // calling function 1835 case Opcode::Function: 1836 // sanity check, this should not happen unless opcode is wrong 1837 // (i.e. there's a bug in the compile() function) 1838 if (stack.count() < index) 1839 return Value::errorVALUE(); // not enough arguments 1840 1841 args.clear(); 1842 fe.ranges.clear(); 1843 fe.ranges.resize(index); 1844 fe.regions.clear(); 1845 fe.regions.resize(index); 1846 fe.sheet = d->sheet; 1847 for (; index; index--) { 1848 stackEntry e = stack.pop(); 1849 args.insert(args.begin(), e.val); 1850 // fill the FunctionExtra object 1851 fe.ranges[index - 1].col1 = e.col1; 1852 fe.ranges[index - 1].row1 = e.row1; 1853 fe.ranges[index - 1].col2 = e.col2; 1854 fe.ranges[index - 1].row2 = e.row2; 1855 fe.regions[index - 1] = e.reg; 1856 } 1857 1858 // function name as string value 1859 val1 = converter->asString(stack.pop().val); 1860 if (val1.isError()) 1861 return val1; 1862 function = FunctionRepository::self()->function(val1.asString()); 1863 if (!function) 1864 return Value::errorNAME(); // no such function 1865 1866 ret = function->exec(args, calc, &fe); 1867 entry.reset(); 1868 entry.val = ret; 1869 stack.push(entry); 1870 1871 break; 1872 1873 #ifdef CALLIGRA_SHEETS_INLINE_ARRAYS 1874 // creating an array 1875 case Opcode::Array: { 1876 const int cols = d->constants[index].asInteger(); 1877 const int rows = d->constants[index+1].asInteger(); 1878 // check if enough array elements are available 1879 if (stack.count() < cols * rows) 1880 return Value::errorVALUE(); 1881 Value array(Value::Array); 1882 for (int row = rows - 1; row >= 0; --row) { 1883 for (int col = cols - 1; col >= 0; --col) { 1884 array.setElement(col, row, stack.pop().val); 1885 } 1886 } 1887 entry.reset(); 1888 entry.val = array; 1889 stack.push(entry); 1890 break; 1891 } 1892 #endif 1893 default: 1894 break; 1895 } 1896 } 1897 1898 if (!d->sheet) 1899 delete map; 1900 1901 // more than one value in stack ? unsuccessful execution... 1902 if (stack.count() != 1) 1903 return Value::errorVALUE(); 1904 1905 return stack.pop().val; 1906 } 1907 1908 Formula& Formula::operator=(const Formula & other) 1909 { 1910 d = other.d; 1911 return *this; 1912 } 1913 1914 bool Formula::operator==(const Formula& other) const 1915 { 1916 return (d->expression == other.d->expression); 1917 } 1918 1919 // Debugging aid 1920 1921 QString Formula::dump() const 1922 { 1923 QString result; 1924 1925 if (d->dirty) { 1926 Tokens tokens = scan(d->expression); 1927 compile(tokens); 1928 } 1929 1930 result = QString("Expression: [%1]\n").arg(d->expression); 1931 #if 0 1932 Value value = eval(); 1933 result.append(QString("Result: %1\n").arg( 1934 converter->asString(value).asString())); 1935 #endif 1936 1937 result.append(" Constants:\n"); 1938 for (int c = 0; c < d->constants.count(); c++) { 1939 QString vtext; 1940 Value val = d->constants[c]; 1941 if (val.isString()) vtext = QString("[%1]").arg(val.asString()); 1942 else if (val.isNumber()) vtext = QString("%1").arg((double) numToDouble(val.asFloat())); 1943 else if (val.isBoolean()) vtext = QString("%1").arg(val.asBoolean() ? "True" : "False"); 1944 else if (val.isError()) vtext = "error"; 1945 else vtext = "???"; 1946 result += QString(" #%1 = %2\n").arg(c).arg(vtext); 1947 } 1948 1949 result.append("\n"); 1950 result.append(" Code:\n"); 1951 for (int i = 0; i < d->codes.count(); i++) { 1952 QString ctext; 1953 switch (d->codes[i].type) { 1954 case Opcode::Load: ctext = QString("Load #%1").arg(d->codes[i].index); break; 1955 case Opcode::Ref: ctext = QString("Ref #%1").arg(d->codes[i].index); break; 1956 case Opcode::Function: ctext = QString("Function (%1)").arg(d->codes[i].index); break; 1957 case Opcode::Add: ctext = "Add"; break; 1958 case Opcode::Sub: ctext = "Sub"; break; 1959 case Opcode::Mul: ctext = "Mul"; break; 1960 case Opcode::Div: ctext = "Div"; break; 1961 case Opcode::Neg: ctext = "Neg"; break; 1962 case Opcode::Concat: ctext = "Concat"; break; 1963 case Opcode::Pow: ctext = "Pow"; break; 1964 case Opcode::Intersect: ctext = "Intersect"; break; 1965 case Opcode::Equal: ctext = "Equal"; break; 1966 case Opcode::Not: ctext = "Not"; break; 1967 case Opcode::Less: ctext = "Less"; break; 1968 case Opcode::Greater: ctext = "Greater"; break; 1969 case Opcode::Array: ctext = QString("Array (%1x%2)").arg(d->constants[d->codes[i].index].asInteger()).arg(d->constants[d->codes[i].index+1].asInteger()); break; 1970 case Opcode::Nop: ctext = "Nop"; break; 1971 case Opcode::Cell: ctext = "Cell"; break; 1972 case Opcode::Range: ctext = "Range"; break; 1973 default: ctext = "Unknown"; break; 1974 } 1975 result.append(" ").append(ctext).append("\n"); 1976 } 1977 1978 return result; 1979 } 1980 1981 QTextStream& operator<<(QTextStream& ts, Formula formula) 1982 { 1983 ts << formula.dump(); 1984 return ts; 1985 }