kspread

formula.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
3 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "formula.h"
22
23#include "kspread_cell.h"
24#include "kspread_sheet.h"
25#include "kspread_doc.h"
26#include "kspread_util.h"
27#include "kspread_value.h"
28
29#include "valuecalc.h"
30#include "valueconverter.h"
31#include "valueparser.h"
32
33#include "functions.h"
34
35#include <limits.h>
36
37#include <tqregexp.h>
38#include <tqstring.h>
39#include <tqvaluevector.h>
40#include <tqvaluestack.h>
41
42#include <tdelocale.h>
43
44/*
45 To understand how this formula engine works, please refer to the documentation
46 in file DESIGN.html.
47
48 Useful references:
49 - "Principles of Compiler Design", A.V.Aho, J.D.Ullman, Addison Wesley, 1978
50 - "Writing Interactive Compilers and Interpreters", P.J. Brown,
51 John Wiley and Sons, 1979.
52 - "The Theory and Practice of Compiler Writing", J.Tremblay, P.G.Sorenson,
53 McGraw-Hill, 1985.
54 - "The Java(TM) Virtual Machine Specification", T.Lindholm, F.Yellin,
55 Addison-Wesley, 1997.
56 - "Java Virtual Machine", J.Meyer, T.Downing, O'Reilly, 1997.
57
58 */
59
60
61 /*
62TODO - features:
63 - handle Intersection
64 - cell reference is made relative (absolute now)
65 - shared formula (different owner, same data)
66 - relative internal representation (independent of owner)
67 - OASIS support
68TODO - optimizations:
69 - handle initial formula marker = (and +)
70 - reuse constant already in the pool
71 - reuse references already in the pool
72 - expression optimization (e.g. 1+2+A1 becomes 3+A1)
73 */
74
75namespace KSpread
76{
77
78class Opcode
79{
80public:
81
82 enum { Nop = 0, Load, Ref, Cell, Range, Function, Add, Sub, Neg, Mul, Div,
83 Pow, Concat, Not, Equal, Less, Greater };
84
85 unsigned type;
86 unsigned index;
87
88 Opcode(): type(Nop), index(0) {};
89 Opcode( unsigned t ): type(t), index(0) {};
90 Opcode( unsigned t, unsigned i ): type(t), index(i) {};
91};
92
93class Formula::Private
94{
95public:
96 Formula *formula;
97 Cell *cell;
98 Sheet *sheet;
99 bool dirty;
100 bool valid;
101 TQString expression;
102 TQValueVector<Opcode> codes;
103 TQValueVector<Value> constants;
104};
105
106class TokenStack : public TQValueVector<Token>
107{
108public:
109 TokenStack();
110 bool isEmpty() const;
111 unsigned itemCount() const;
112 void push( const Token& token );
113 Token pop();
114 const Token& top();
115 const Token& top( unsigned index );
116private:
117 void ensureSpace();
118 unsigned topIndex;
119};
120
121}
122
123using namespace KSpread;
124
125// for null token
126const Token Token::null;
127
128// helper function: return operator of given token text
129// e.g. "*" yields Operator::Asterisk, and so on
130Token::Op KSpread::matchOperator( const TQString& text )
131{
133
134 if( text.length() == 1 )
135 {
136 TQChar p = text[0];
137 switch( p.unicode() )
138 {
139 case '+': result = Token::Plus; break;
140 case '-': result = Token::Minus; break;
141 case '*': result = Token::Asterisk; break;
142 case '/': result = Token::Slash; break;
143 case '^': result = Token::Caret; break;
144 case ',': result = Token::Comma; break;
145 case ';': result = Token::Semicolon; break;
146 case '(': result = Token::LeftPar; break;
147 case ')': result = Token::RightPar; break;
148 case '&': result = Token::Ampersand; break;
149 case '=': result = Token::Equal; break;
150 case '<': result = Token::Less; break;
151 case '>': result = Token::Greater; break;
152 case '%': result = Token::Percent; break;
153 default : result = Token::InvalidOp; break;
154 }
155 }
156
157 if( text.length() == 2 )
158 {
159 if( text == "<>" ) result = Token::NotEqual;
160 if( text == "<=" ) result = Token::LessEqual;
161 if( text == ">=" ) result = Token::GreaterEqual;
162 if( text == "==" ) result = Token::Equal;
163 }
164
165 return result;
166}
167
168// helper function: give operator precedence
169// e.g. "+" is 1 while "*" is 3
170static int opPrecedence( Token::Op op )
171{
172 int prec = -1;
173 switch( op )
174 {
175 case Token::Percent : prec = 8; break;
176 case Token::Caret : prec = 7; break;
177 case Token::Asterisk : prec = 5; break;
178 case Token::Slash : prec = 6; break;
179 case Token::Plus : prec = 3; break;
180 case Token::Minus : prec = 3; break;
181 case Token::Ampersand : prec = 2; break;
182 case Token::Equal : prec = 1; break;
183 case Token::NotEqual : prec = 1; break;
184 case Token::Less : prec = 1; break;
185 case Token::Greater : prec = 1; break;
186 case Token::LessEqual : prec = 1; break;
187 case Token::GreaterEqual : prec = 1; break;
188 case Token::Semicolon : prec = 0; break;
189 case Token::RightPar : prec = 0; break;
190 case Token::LeftPar : prec = -1; break;
191 default: prec = -1; break;
192 }
193 return prec;
194}
195
196// helper function
197static Value tokenAsValue( const Token& token )
198{
199 Value value;
200 if( token.isBoolean() ) value = Value( token.asBoolean() );
201 else if( token.isInteger() ) value = Value( token.asInteger() );
202 else if( token.isFloat() ) value = Value( token.asFloat() );
203 else if( token.isString() ) value = Value( token.asString() );
204 return value;
205}
206
207/**********************
208 Token
209 **********************/
210
211// creates a token
212Token::Token( Type type, const TQString& text, int pos )
213{
214 m_type = type;
215 m_text = text;
216 m_pos = pos;
217}
218
219// copy constructor
220Token::Token( const Token& token )
221{
222 m_type = token.m_type;
223 m_text = token.m_text;
224 m_pos = token.m_pos;
225}
226
227// assignment operator
228Token& Token::operator=( const Token& token )
229{
230 m_type = token.m_type;
231 m_text = token.m_text;
232 m_pos = token.m_pos;
233 return *this;
234}
235
237{
238 if( !isBoolean() ) return false;
239 return m_text.lower() == "true";
240 // FIXME check also for i18n version
241}
242
244{
245 if( isInteger() ) return m_text.toLong();
246 else return 0;
247}
248
249double Token::asFloat() const
250{
251 if( isFloat() ) return m_text.toDouble();
252 else return 0.0;
253}
254
255TQString Token::asString() const
256{
257 if( isString() ) return m_text.mid( 1, m_text.length()-2 );
258 else return TQString();
259}
260
262{
263 if( isOperator() ) return matchOperator( m_text );
264 else return InvalidOp;
265}
266
267TQString Token::sheetName() const
268{
269 if( !isCell() && !isRange() ) return TQString();
270 int i = m_text.find( '!' );
271 if( i < 0 ) return TQString();
272 TQString sheet = m_text.left( i );
273 return sheet;
274}
275
276TQString Token::description() const
277{
278 TQString desc;
279
280 switch (m_type )
281 {
282 case Boolean: desc = "Boolean"; break;
283 case Integer: desc = "Integer"; break;
284 case Float: desc = "Float"; break;
285 case String: desc = "String"; break;
286 case Identifier: desc = "Identifier"; break;
287 case Cell: desc = "Cell"; break;
288 case Range: desc = "Range"; break;
289 case Operator: desc = "Operator"; break;
290 default: desc = "Unknown"; break;
291 }
292
293 while( desc.length() < 10 ) desc.prepend( ' ' );
294 desc.prepend( " " );
295 desc.prepend( TQString::number( m_pos ) );
296 desc.append( " : " ).append( m_text );
297
298 return desc;
299}
300
301
302/**********************
303 TokenStack
304 **********************/
305
306TokenStack::TokenStack(): TQValueVector<Token>()
307{
308 topIndex = 0;
309 ensureSpace();
310}
311
312bool TokenStack::isEmpty() const
313{
314 return topIndex == 0;
315}
316
317unsigned TokenStack::itemCount() const
318{
319 return topIndex;
320}
321
322void TokenStack::push( const Token& token )
323{
324 ensureSpace();
325 at( topIndex++ ) = token;
326}
327
328Token TokenStack::pop()
329{
330 return (topIndex > 0 ) ? Token( at( --topIndex ) ) : Token();
331}
332
333const Token& TokenStack::top()
334{
335 return top( 0 );
336}
337
338const Token& TokenStack::top( unsigned index )
339{
340 if( topIndex > index )
341 return at( topIndex-index-1 );
342 return Token::null;
343}
344
345void TokenStack::ensureSpace()
346{
347 while( topIndex >= size() )
348 resize( size() + 10 );
349}
350
351/**********************
352 FormulaPrivate
353 **********************/
354
355// helper function: return true for valid identifier character
356bool KSpread::isIdentifier( TQChar ch )
357{
358 return ( ch.unicode() == '_' ) || (ch.unicode() == '$' ) || ( ch.isLetter() );
359}
360
361
362
363
364/**********************
365 Formula
366 **********************/
367
368// Constructor
369
371{
372 d = new Private;
373 d->cell = cell;
374 d->sheet = sheet;
375 clear();
376}
377
379{
380 d = new Private;
381 d->cell = 0;
382 d->sheet = 0;
383 clear();
384}
385
386// Destructor
387
389{
390 delete d;
391}
392
394{
395 return d->cell;
396}
397
398Sheet* Formula::sheet() const
399{
400 return d->sheet;
401}
402
403// Sets a new expression for this formula.
404// note that both the real lex and parse processes will happen later on
405// when needed (i.e. "lazy parse"), for example during formula evaluation.
406
407void Formula::setExpression( const TQString& expr )
408{
409 d->expression = expr;
410 d->dirty = true;
411 d->valid = false;
412}
413
414// Returns the expression associated with this formula.
415
416TQString Formula::expression() const
417{
418 return d->expression;
419}
420
421// Returns the validity of the formula.
422// note: empty formula is always invalid.
423
425{
426 if( d->dirty )
427 {
428 TDELocale* locale = d->cell ? d->cell->locale() : 0;
429 if ((!locale) && d->sheet)
430 locale = d->sheet->doc()->locale();
431 Tokens tokens = scan( d->expression, locale );
432 if( tokens.valid() )
433 compile( tokens );
434 else
435 d->valid = false;
436 }
437 return d->valid;
438}
439
440// Clears everything, also mark the formula as invalid.
441
443{
444 d->expression = TQString();
445 d->dirty = true;
446 d->valid = false;
447 d->constants.clear();
448 d->codes.clear();
449}
450
451// Returns list of token for the expression.
452// this triggers again the lexical analysis step. it is however preferable
453// (even when there's small performance penalty) because otherwise we need to
454// store parsed tokens all the time which serves no good purpose.
455
457{
458 TDELocale* locale = d->cell ? d->cell->locale() : 0;
459 if ((!locale) && d->sheet)
460 locale = d->sheet->doc()->locale();
461 return scan( d->expression, locale );
462}
463
464Tokens Formula::scan( const TQString& expr, TDELocale* locale ) const
465{
466 // to hold the result
468
469 // parsing state
470 enum { Start, Finish, Bad, InNumber, InDecimal, InExpIndicator, InExponent,
471 InString, InIdentifier, InCell, InRange, InSheetOrAreaName } state;
472
473 // use locale settings if specified
474 TQString thousand = locale ? locale->thousandsSeparator() : "";
475 TQString decimal = locale ? locale->decimalSymbol() : ".";
476
477 // initialize variables
478 state = Start;
479 unsigned int i = 0;
480 TQString ex = expr;
481 TQString tokenText;
482 int tokenStart = 0;
483
484 // first character must be equal sign (=)
485 if( ex[0] != '=' )
486 return tokens;
487
488 // but the scanner should not see this equal sign
489 ex.remove( 0, 1 );
490
491 // force a terminator
492 ex.append( TQChar() );
493
494 // main loop
495 while( (state != Bad) && (state != Finish) && (i < ex.length()) )
496 {
497 TQChar ch = ex[i];
498
499 switch( state )
500 {
501
502 case Start:
503
504 tokenStart = i;
505
506 // skip any whitespaces
507 if( ch.isSpace() ) i++;
508
509 // check for number
510 else if( ch.isDigit() )
511 {
512 state = InNumber;
513 }
514
515 // a string ?
516 else if ( ch == '"' )
517 {
518 tokenText.append( ex[i++] );
519 state = InString;
520 }
521
522 // beginning with alphanumeric ?
523 // could be identifier, cell, range, or function...
524 else if( isIdentifier( ch ) )
525 {
526 state = InIdentifier;
527 }
528
529 // aposthrophe (') marks sheet name for 3-d cell, e.g 'Sales Q3'!A4, or a named range
530 else if ( ch.unicode() == '\'' )
531 {
532 i++;
533 state = InSheetOrAreaName;
534 }
535
536 // decimal dot ?
537 else if ( ch == decimal )
538 {
539 tokenText.append( ex[i++] );
540 state = InDecimal;
541 }
542
543 // terminator character
544 else if ( ch == TQChar::null )
545 state = Finish;
546
547 // look for operator match
548 else
549 {
550 int op;
551 TQString s;
552
553 // check for two-chars operator, such as '<=', '>=', etc
554 s.append( ch ).append( ex[i+1] );
555 op = matchOperator( s );
556
557 // check for one-char operator, such as '+', ';', etc
558 if( op == Token::InvalidOp )
559 {
560 s = TQString( ch );
561 op = matchOperator( s );
562 }
563
564 // any matched operator ?
565 if( op != Token::InvalidOp )
566 {
567 int len = s.length();
568 i += len;
569 tokens.append( Token( Token::Operator, s.left( len ), tokenStart ) );
570 }
571 else state = Bad;
572 }
573 break;
574
575 case InIdentifier:
576
577 // consume as long as alpha, dollar sign, underscore, or digit
578 if( isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
579
580 // a '!' ? then this must be sheet name, e.g "Sheet4!"
581 else if( ch == '!' )
582 {
583 tokenText.append( ex[i++] );
584 state = InCell;
585 }
586
587 // a '(' ? then this must be a function identifier
588 else if( ch == '(' )
589 {
590 tokens.append (Token (Token::Identifier, tokenText, tokenStart));
591 tokenStart = i;
592 tokenText = "";
593 state = Start;
594 }
595
596 // we're done with identifier
597 else
598 {
599 // check for cell reference, e.g A1, VV123, ...
600 TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
601 int n = exp.search( tokenText );
602 if( n >= 0 )
603 state = InCell;
604 else
605 {
606 if ( isNamedArea( tokenText ) )
607 tokens.append (Token (Token::Range, tokenText, tokenStart));
608 else
609 tokens.append (Token (Token::Identifier, tokenText, tokenStart));
610 tokenStart = i;
611 tokenText = "";
612 state = Start;
613 }
614 }
615 break;
616
617 case InCell:
618
619 // consume as long as alpha, dollar sign, underscore, or digit
620 if( isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
621
622 // we're done with cell ref, possibly with sheet name (like "Sheet2!B2")
623 // note that "Sheet2!TotalSales" is also possible, in which "TotalSales" is a named area
624 else
625 {
626
627 // check if it's a cell ref like A32, not named area
628 TQString cell;
629 for( int j = tokenText.length()-1; j>=0; j-- )
630 if( tokenText[j] == '!' )
631 break;
632 else
633 cell.prepend( tokenText[j] );
634 TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
635 if( exp.search( cell ) != 0 )
636 {
637
638 // we're done with named area
639 // (Tomas) huh? this doesn't seem to check for named areas ...
640 tokens.append( Token( Token::Range, tokenText, tokenStart ) );
641 tokenText = "";
642 state = Start;
643 }
644
645 else
646 {
647
648 // so up to now we've got something like A2 or Sheet2!F4
649 // check for range reference
650 if( ch == ':' )
651 {
652 tokenText.append( ex[i++] );
653 state = InRange;
654 }
655 else
656 {
657 // we're done with cell reference
658 tokens.append( Token( Token::Cell, tokenText, tokenStart ) );
659 tokenText = "";
660 state = Start;
661 }
662 }
663 }
664 break;
665
666 case InRange:
667
668 // consume as long as alpha, dollar sign, underscore, or digit
669 if( isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
670
671 // we're done with range reference
672 else
673 {
674 tokens.append( Token( Token::Range, tokenText, tokenStart ) );
675 tokenText = "";
676 state = Start;
677 }
678 break;
679
680 case InSheetOrAreaName:
681
682 // consume until '
683 if ( ch.unicode() != '\'' )
684 tokenText.append( ex[i++] );
685
686 else
687 {
688 // eat the aposthrophe itself
689 ++i;
690 // must be followed by '!' to be sheet name
691 if( ex[i] == '!' )
692 {
693 tokenText.append( ex[i++] );
694 state = InCell;
695 }
696 else
697 {
698 if ( isNamedArea( tokenText ) )
699 tokens.append (Token (Token::Range, tokenText, tokenStart));
700 else
701 tokens.append (Token (Token::Identifier, tokenText, tokenStart));
702 tokenStart = i;
703 tokenText = "";
704 state = Start;
705 }
706 }
707 break;
708
709 case InNumber:
710
711 // consume as long as it's digit
712 if( ch.isDigit() ) tokenText.append( ex[i++] );
713
714 // skip thousand separator
715 else if( !thousand.isEmpty() && ( ch ==thousand[0] ) ) i++;
716
717 // convert decimal separator to '.', also support '.' directly
718 // we always support '.' because of bug #98455
719 else if(( !decimal.isEmpty() && ( ch == decimal[0] ) ) || (ch == '.'))
720 {
721 tokenText.append( '.' );
722 i++;
723 state = InDecimal;
724 }
725
726 // exponent ?
727 else if( ch.upper() == 'E' )
728 {
729 tokenText.append( 'E' );
730 i++;
731 state = InExpIndicator;
732 }
733
734 // reference sheet delimiter?
735 else if( ch == '!' )
736 {
737 tokenText.append( ex[i++] );
738 state = InCell;
739 }
740
741 // identifier?
742 else if( isIdentifier( ch ) )
743 {
744 // has to be a sheet or area name then
745 state = InIdentifier;
746 }
747
748 // we're done with integer number
749 else
750 {
751 tokens.append( Token( Token::Integer, tokenText, tokenStart ) );
752 tokenText = "";
753 state = Start;
754 };
755 break;
756
757 case InDecimal:
758
759 // consume as long as it's digit
760 if( ch.isDigit() ) tokenText.append( ex[i++] );
761
762 // exponent ?
763 else if( ch.upper() == 'E' )
764 {
765 tokenText.append( 'E' );
766 i++;
767 state = InExpIndicator;
768 }
769
770 // we're done with floating-point number
771 else
772 {
773 tokens.append( Token( Token::Float, tokenText, tokenStart ) );
774 tokenText = "";
775 state = Start;
776 };
777 break;
778
779 case InExpIndicator:
780
781 // possible + or - right after E, e.g 1.23E+12 or 4.67E-8
782 if( ( ch == '+' ) || ( ch == '-' ) ) tokenText.append( ex[i++] );
783
784 // consume as long as it's digit
785 else if( ch.isDigit() ) state = InExponent;
786
787 // invalid thing here
788 else state = Bad;
789
790 break;
791
792 case InExponent:
793
794 // consume as long as it's digit
795 if( ch.isDigit() ) tokenText.append( ex[i++] );
796
797 // we're done with floating-point number
798 else
799 {
800 tokens.append( Token( Token::Float, tokenText, tokenStart ) );
801 tokenText = "";
802 state = Start;
803 };
804 break;
805
806 case InString:
807
808 // consume until "
809 if( ch != '"' ) tokenText.append( ex[i++] );
810
811 else
812 {
813 tokenText.append( ch ); i++;
814 tokens.append( Token( Token::String, tokenText, tokenStart ) );
815 tokenText = "";
816 state = Start;
817 }
818 break;
819
820 case Bad:
821 default:
822 break;
823 };
824 };
825
826 if( state == Bad )
827 tokens.setValid( false );
828
829 return tokens;
830}
831
832// will affect: dirty, valid, codes, constants
833void Formula::compile( const Tokens& tokens ) const
834{
835 // initialize variables
836 d->dirty = false;
837 d->valid = false;
838 d->codes.clear();
839 d->constants.clear();
840
841 // sanity check
842 if( tokens.count() == 0 ) return;
843
844 TokenStack syntaxStack;
845 TQValueStack<int> argStack;
846 unsigned argCount = 1;
847
848 for( unsigned i = 0; i <= tokens.count(); i++ )
849 {
850 // helper token: InvalidOp is end-of-formula
851 Token token = ( i < tokens.count() ) ? tokens[i] : Token( Token::Operator );
852 Token::Type tokenType = token.type();
853
854 // unknown token is invalid
855 if( tokenType == Token::Unknown ) break;
856
857 // for constants, push immediately to stack
858 // generate code to load from a constant
859 if ( ( tokenType == Token::Integer ) || ( tokenType == Token::Float ) ||
860 ( tokenType == Token::String ) || ( tokenType == Token::Boolean ) )
861 {
862 syntaxStack.push( token );
863 d->constants.append( tokenAsValue( token ) );
864 d->codes.append( Opcode( Opcode::Load, d->constants.count()-1 ) );
865 }
866
867 // for cell, range, or identifier, push immediately to stack
868 // generate code to load from reference
869 if( ( tokenType == Token::Cell ) || ( tokenType == Token::Range ) ||
870 ( tokenType == Token::Identifier ) )
871 {
872 syntaxStack.push( token );
873 d->constants.append( Value( token.text() ) );
874 if (tokenType == Token::Cell)
875 d->codes.append( Opcode( Opcode::Cell, d->constants.count()-1 ) );
876 else if (tokenType == Token::Range)
877 d->codes.append( Opcode( Opcode::Range, d->constants.count()-1 ) );
878 else
879 d->codes.append( Opcode( Opcode::Ref, d->constants.count()-1 ) );
880 }
881
882 // are we entering a function ?
883 // if token is operator, and stack already has: id ( arg
884 if( tokenType == Token::Operator )
885 if( syntaxStack.itemCount() >= 3 )
886 {
887 Token arg = syntaxStack.top();
888 Token par = syntaxStack.top( 1 );
889 Token id = syntaxStack.top( 2 );
890 if( !arg.isOperator() )
891 if( par.asOperator() == Token::LeftPar )
892 if( id.isIdentifier() )
893 {
894 argStack.push( argCount );
895 argCount = 1;
896 }
897 }
898
899 // special case for percentage
900 if( tokenType == Token::Operator )
901 if( token.asOperator() == Token::Percent )
902 if( syntaxStack.itemCount() >= 1 )
903 if( !syntaxStack.top().isOperator() )
904 {
905 d->constants.append( Value( 0.01 ) );
906 d->codes.append( Opcode( Opcode::Load, d->constants.count()-1 ) );
907 d->codes.append( Opcode( Opcode::Mul ) );
908 }
909
910 // for any other operator, try to apply all parsing rules
911 if( tokenType == Token::Operator )
912 if( token.asOperator() != Token::Percent )
913 {
914 // repeat until no more rule applies
915 for( ; ; )
916 {
917 bool ruleFound = false;
918
919 // rule for function arguments, if token is ; or )
920 // id ( arg1 ; arg2 -> id ( arg
921 if( !ruleFound )
922 if( syntaxStack.itemCount() >= 5 )
923 if( ( token.asOperator() == Token::RightPar ) ||
924 ( token.asOperator() == Token::Semicolon ) )
925 {
926 Token arg2 = syntaxStack.top();
927 Token sep = syntaxStack.top( 1 );
928 Token arg1 = syntaxStack.top( 2 );
929 Token par = syntaxStack.top( 3 );
930 Token id = syntaxStack.top( 4 );
931 if( !arg2.isOperator() )
932 if( sep.asOperator() == Token::Semicolon )
933 if( !arg1.isOperator() )
934 if( par.asOperator() == Token::LeftPar )
935 if( id.isIdentifier() )
936 {
937 ruleFound = true;
938 syntaxStack.pop();
939 syntaxStack.pop();
940 argCount++;
941 }
942 }
943
944 // rule for function last argument:
945 // id ( arg ) -> arg
946 if( !ruleFound )
947 if( syntaxStack.itemCount() >= 4 )
948 {
949 Token par2 = syntaxStack.top();
950 Token arg = syntaxStack.top( 1 );
951 Token par1 = syntaxStack.top( 2 );
952 Token id = syntaxStack.top( 3 );
953 if( par2.asOperator() == Token::RightPar )
954 if( !arg.isOperator() )
955 if( par1.asOperator() == Token::LeftPar )
956 if( id.isIdentifier() )
957 {
958 ruleFound = true;
959 syntaxStack.pop();
960 syntaxStack.pop();
961 syntaxStack.pop();
962 syntaxStack.pop();
963 syntaxStack.push( arg );
964 d->codes.append( Opcode( Opcode::Function, argCount ) );
965 argCount = argStack.empty() ? 0 : argStack.pop();
966 }
967 }
968
969 // rule for function call with parentheses, but without argument
970 // e.g. "2*PI()"
971 if( !ruleFound )
972 if( syntaxStack.itemCount() >= 3 )
973 {
974 Token par2 = syntaxStack.top();
975 Token par1 = syntaxStack.top( 1 );
976 Token id = syntaxStack.top( 2 );
977 if( par2.asOperator() == Token::RightPar )
978 if( par1.asOperator() == Token::LeftPar )
979 if( id.isIdentifier() )
980 {
981 ruleFound = true;
982 syntaxStack.pop();
983 syntaxStack.pop();
984 syntaxStack.pop();
985 syntaxStack.push( Token( Token::Integer ) );
986 d->codes.append( Opcode( Opcode::Function, 0 ) );
987 }
988 }
989
990 // rule for parenthesis: ( Y ) -> Y
991 if( !ruleFound )
992 if( syntaxStack.itemCount() >= 3 )
993 {
994 Token right = syntaxStack.top();
995 Token y = syntaxStack.top( 1 );
996 Token left = syntaxStack.top( 2 );
997 if( right.isOperator() )
998 if( !y.isOperator() )
999 if( left.isOperator() )
1000 if( right.asOperator() == Token::RightPar )
1001 if( left.asOperator() == Token::LeftPar )
1002 {
1003 ruleFound = true;
1004 syntaxStack.pop();
1005 syntaxStack.pop();
1006 syntaxStack.pop();
1007 syntaxStack.push( y );
1008 }
1009 }
1010
1011 // rule for binary operator: A (op) B -> A
1012 // conditions: precedence of op >= precedence of token
1013 // action: push (op) to result
1014 // e.g. "A * B" becomes "A" if token is operator "+"
1015 // exception: for caret (power operator), if op is another caret
1016 // then the rule doesn't apply, e.g. "2^3^2" is evaluated as "2^(3^2)"
1017 if( !ruleFound )
1018 if( syntaxStack.itemCount() >= 3 )
1019 {
1020 Token b = syntaxStack.top();
1021 Token op = syntaxStack.top( 1 );
1022 Token a = syntaxStack.top( 2 );
1023 if( !a.isOperator() )
1024 if( !b.isOperator() )
1025 if( op.isOperator() )
1026 if( token.asOperator() != Token::LeftPar )
1027 if( token.asOperator() != Token::Caret )
1028 if( opPrecedence( op.asOperator() ) >= opPrecedence( token.asOperator() ) )
1029 {
1030 ruleFound = true;
1031 syntaxStack.pop();
1032 syntaxStack.pop();
1033 syntaxStack.pop();
1034 syntaxStack.push( b );
1035 switch( op.asOperator() )
1036 {
1037 // simple binary operations
1038 case Token::Plus: d->codes.append( Opcode::Add ); break;
1039 case Token::Minus: d->codes.append( Opcode::Sub ); break;
1040 case Token::Asterisk: d->codes.append( Opcode::Mul ); break;
1041 case Token::Slash: d->codes.append( Opcode::Div ); break;
1042 case Token::Caret: d->codes.append( Opcode::Pow ); break;
1043 case Token::Ampersand: d->codes.append( Opcode::Concat ); break;
1044
1045 // simple value comparisons
1046 case Token::Equal: d->codes.append( Opcode::Equal ); break;
1047 case Token::Less: d->codes.append( Opcode::Less ); break;
1048 case Token::Greater: d->codes.append( Opcode::Greater ); break;
1049
1050 // NotEqual is Equal, followed by Not
1051 case Token::NotEqual:
1052 d->codes.append( Opcode::Equal );
1053 d->codes.append( Opcode::Not );
1054 break;
1055
1056 // LessOrEqual is Greater, followed by Not
1057 case Token::LessEqual:
1058 d->codes.append( Opcode::Greater );
1059 d->codes.append( Opcode::Not );
1060 break;
1061
1062 // GreaterOrEqual is Less, followed by Not
1064 d->codes.append( Opcode::Less );
1065 d->codes.append( Opcode::Not );
1066 break;
1067 default: break;
1068 };
1069 }
1070 }
1071
1072 // rule for unary operator: (op1) (op2) X -> (op1) X
1073 // conditions: op2 is unary, token is not '('
1074 // action: push (op2) to result
1075 // e.g. "* - 2" becomes "*"
1076 if( !ruleFound )
1077 if( token.asOperator() != Token::LeftPar )
1078 if( syntaxStack.itemCount() >= 3 )
1079 {
1080 Token x = syntaxStack.top();
1081 Token op2 = syntaxStack.top( 1 );
1082 Token op1 = syntaxStack.top( 2 );
1083 if( !x.isOperator() )
1084 if( op1.isOperator() )
1085 if( op2.isOperator() )
1086 if( ( op2.asOperator() == Token::Plus ) ||
1087 ( op2.asOperator() == Token::Minus ) )
1088 {
1089 ruleFound = true;
1090 syntaxStack.pop();
1091 syntaxStack.pop();
1092 syntaxStack.push( x );
1093 if( op2.asOperator() == Token::Minus )
1094 d->codes.append( Opcode( Opcode::Neg ) );
1095 }
1096 }
1097
1098 // auxilary rule for unary operator: (op) X -> X
1099 // conditions: op is unary, op is first in syntax stack, token is not '('
1100 // action: push (op) to result
1101 if( !ruleFound )
1102 if( token.asOperator() != Token::LeftPar )
1103 if( syntaxStack.itemCount() == 2 )
1104 {
1105 Token x = syntaxStack.top();
1106 Token op = syntaxStack.top( 1 );
1107 if( !x.isOperator() )
1108 if( op.isOperator() )
1109 if( ( op.asOperator() == Token::Plus ) ||
1110 ( op.asOperator() == Token::Minus ) )
1111 {
1112 ruleFound = true;
1113 syntaxStack.pop();
1114 syntaxStack.pop();
1115 syntaxStack.push( x );
1116 if( op.asOperator() == Token::Minus )
1117 d->codes.append( Opcode( Opcode::Neg ) );
1118 }
1119 }
1120
1121 if( !ruleFound ) break;
1122 }
1123
1124 // can't apply rules anymore, push the token
1125 if( token.asOperator() != Token::Percent )
1126 syntaxStack.push( token );
1127 }
1128 }
1129
1130 // syntaxStack must left only one operand and end-of-formula (i.e. InvalidOp)
1131 d->valid = false;
1132 if( syntaxStack.itemCount() == 2 )
1133 if( syntaxStack.top().isOperator() )
1134 if( syntaxStack.top().asOperator() == Token::InvalidOp )
1135 if( !syntaxStack.top(1).isOperator() )
1136 d->valid = true;
1137
1138 // bad parsing ? clean-up everything
1139 if( !d->valid )
1140 {
1141 d->constants.clear();
1142 d->codes.clear();
1143 }
1144}
1145
1146bool Formula::isNamedArea( const TQString& expr ) const
1147{
1148 TQString tokenText( expr );
1149 // check for named areas ...
1150 if (d->sheet) {
1151 const TQValueList<Reference> areas = d->sheet->doc()->listArea();
1152 TQValueList<Reference>::const_iterator it;
1153 for (it = areas.begin(); it != areas.end(); ++it) {
1154 if ((*it).ref_name.lower() == tokenText.lower()) {
1155 // we got a named area
1156 return true;
1157 }
1158 }
1159 }
1160 return false;
1161}
1162
1163
1164// Evaluates the formula, returns the result.
1165
1166struct stackEntry {
1167 void reset () { row1 = col1 = row2 = col2 = -1; };
1168 Value val;
1169 int row1, col1, row2, col2;
1170};
1171
1173{
1174 TQValueStack<stackEntry> stack;
1175 stackEntry entry;
1176 unsigned index;
1177 Value val1, val2;
1178 TQString c;
1179 TQValueVector<Value> args;
1180
1181 Sheet *sheet = 0;
1182 ValueParser* parser = 0;
1183 ValueConverter* converter = 0;
1184 ValueCalc* calc = 0;
1185
1186 if (d->sheet)
1187 {
1188 sheet = d->sheet;
1189 converter = sheet->doc()->converter();
1190 calc = sheet->doc()->calc();
1191 }
1192 else
1193 {
1194 parser = new ValueParser( TDEGlobal::locale() );
1195 converter = new ValueConverter( parser );
1196 calc = new ValueCalc( converter );
1197 }
1198
1199 Function* function;
1200 FuncExtra fe;
1201 fe.mycol = fe.myrow = 0;
1202 if (d->cell) {
1203 fe.mycol = d->cell->column();
1204 fe.myrow = d->cell->row();
1205 }
1206
1207 if( d->dirty )
1208 {
1209 Tokens tokens = scan( d->expression );
1210 d->valid = tokens.valid();
1211 if( tokens.valid() )
1212 compile( tokens );
1213 }
1214
1215 if( !d->valid )
1216 return Value::errorVALUE();
1217
1218 for( unsigned pc = 0; pc < d->codes.count(); pc++ )
1219 {
1220 Value ret; // for the function caller
1221 Opcode& opcode = d->codes[pc];
1222 index = opcode.index;
1223 switch( opcode.type )
1224 {
1225 // no operation
1226 case Opcode::Nop:
1227 break;
1228
1229 // load a constant, push to stack
1230 case Opcode::Load:
1231 entry.reset();
1232 entry.val = d->constants[index];
1233 stack.push (entry);
1234 break;
1235
1236 // unary operation
1237 case Opcode::Neg:
1238 entry.reset();
1239 entry.val = stack.pop().val;
1240 if (!entry.val.isError()) // do nothing if we got an error
1241 entry.val = calc->mul (entry.val, -1);
1242 stack.push (entry);
1243 break;
1244
1245 // binary operation: take two values from stack, do the operation,
1246 // push the result to stack
1247 case Opcode::Add:
1248 entry.reset();
1249 val2 = stack.pop().val;
1250 val1 = stack.pop().val;
1251 val2 = calc->add( val1, val2 );
1252 entry.reset();
1253 entry.val = val2;
1254 stack.push (entry);
1255 break;
1256
1257 case Opcode::Sub:
1258 val2 = stack.pop().val;
1259 val1 = stack.pop().val;
1260 val2 = calc->sub( val1, val2 );
1261 entry.reset();
1262 entry.val = val2;
1263 stack.push (entry);
1264 break;
1265
1266 case Opcode::Mul:
1267 val2 = stack.pop().val;
1268 val1 = stack.pop().val;
1269 val2 = calc->mul( val1, val2 );
1270 entry.reset();
1271 entry.val = val2;
1272 stack.push (entry);
1273 break;
1274
1275 case Opcode::Div:
1276 val2 = stack.pop().val;
1277 val1 = stack.pop().val;
1278 val2 = calc->div( val1, val2 );
1279 entry.reset();
1280 entry.val = val2;
1281 stack.push (entry);
1282 break;
1283
1284 case Opcode::Pow:
1285 val2 = stack.pop().val;
1286 val1 = stack.pop().val;
1287 val2 = calc->pow( val1, val2 );
1288 entry.reset();
1289 entry.val = val2;
1290 stack.push (entry);
1291 break;
1292
1293 // string concatenation
1294 case Opcode::Concat:
1295 val1 = converter->asString (stack.pop().val);
1296 val2 = converter->asString (stack.pop().val);
1297 if (val1.isError() || val2.isError())
1298 val1 = Value::errorVALUE();
1299 else
1300 val1.setValue( val2.asString().append( val1.asString() ) );
1301 entry.reset();
1302 entry.val = val1;
1303 stack.push (entry);
1304 break;
1305
1306 // logical not
1307 case Opcode::Not:
1308 val1 = converter->asBoolean (stack.pop().val);
1309 if( val1.isError() )
1310 val1 = Value::errorVALUE();
1311 else
1312 val1.setValue( !val1.asBoolean() );
1313 entry.reset();
1314 entry.val = val1;
1315 stack.push (entry);
1316 break;
1317
1318 // comparison
1319 case Opcode::Equal:
1320 val1 = stack.pop().val;
1321 val2 = stack.pop().val;
1322 if( !val1.allowComparison( val2 ) )
1323 val1 = Value::errorNA();
1324 else if( val2.compare( val1 ) == 0 )
1325 val1 = Value (true);
1326 else
1327 val1 = Value (false);
1328 entry.reset();
1329 entry.val = val1;
1330 stack.push (entry);
1331 break;
1332
1333 // less than
1334 case Opcode::Less:
1335 val1 = stack.pop().val;
1336 val2 = stack.pop().val;
1337 if( !val1.allowComparison( val2 ) )
1338 val1 = Value::errorNA();
1339 else if( val2.compare( val1 ) < 0 )
1340 val1 = Value (true);
1341 else
1342 val1 = Value (false);
1343 entry.reset();
1344 entry.val = val1;
1345 stack.push (entry);
1346 break;
1347
1348 // greater than
1349 case Opcode::Greater:
1350 val1 = stack.pop().val;
1351 val2 = stack.pop().val;
1352 if( !val1.allowComparison( val2 ) )
1353 val1 = Value::errorNA();
1354 else if( val2.compare( val1 ) > 0 )
1355 val1 = Value (true);
1356 else
1357 val1 = Value (false);
1358 entry.reset();
1359 entry.val = val1;
1360 stack.push (entry);
1361 break;
1362
1363
1364 case Opcode::Cell:
1365 c = d->constants[index].asString();
1366 val1 = Value::empty();
1367 entry.reset();
1368 if (sheet)
1369 {
1370 Point cell (c, sheet->workbook(), sheet);
1371 if (cell.isValid())
1372 {
1373 val1 = cell.sheet()->value (cell.column(), cell.row());
1374 // store the reference, so we can use it within functions
1375 entry.col1 = entry.col2 = cell.column();
1376 entry.row1 = entry.row2 = cell.row();
1377 }
1378 }
1379 entry.val = val1;
1380 stack.push (entry);
1381 break;
1382
1383 case Opcode::Range:
1384 c = d->constants[index].asString();
1385 val1 = Value::empty();
1386 entry.reset();
1387 if (sheet)
1388 {
1389 Range range (c, sheet->workbook(), sheet);
1390 if (range.isValid())
1391 {
1392 val1 = range.sheet()->valueRange (range.startCol(), range.startRow(),
1393 range.endCol(), range.endRow());
1394 // store the reference, so we can use it within functions
1395 entry.col1 = range.startCol();
1396 entry.row1 = range.startRow();
1397 entry.col2 = range.endCol();
1398 entry.row2 = range.endRow();
1399 }
1400 }
1401 entry.val = val1;
1402 stack.push (entry);
1403 break;
1404
1405 case Opcode::Ref:
1406 val1 = d->constants[index];
1407 entry.reset();
1408 entry.val = val1;
1409 stack.push (entry);
1410 break;
1411
1412 // calling function
1413 case Opcode::Function:
1414 if( stack.count() < index )
1415 // (Tomas) umm, how could that be ? I mean, the index value
1416 // is computed from the stack *confused*
1417 return Value::errorVALUE(); // not enough arguments
1418
1419 args.clear();
1420 fe.ranges.clear ();
1421 fe.ranges.reserve (index);
1422 fe.sheet = sheet;
1423 for( ; index; index-- )
1424 {
1425 stackEntry e = stack.pop();
1426 args.insert (args.begin(), e.val);
1427 // TODO: create and fill a FunctionExtra object, if needed
1428 // problem: we don't know if we need it, as we don't have the
1429 // fuction name yet ...
1430 fe.ranges[index - 1].col1 = e.col1;
1431 fe.ranges[index - 1].row1 = e.row1;
1432 fe.ranges[index - 1].col2 = e.col2;
1433 fe.ranges[index - 1].row2 = e.row2;
1434 }
1435
1436 // function name as string value
1437 val1 = converter->asString (stack.pop().val);
1438 if( val1.isError() )
1439 return Value::errorVALUE();
1440 function = FunctionRepository::self()->function ( val1.asString() );
1441 if( !function )
1442 return Value::errorVALUE(); // no such function
1443
1444 ret = function->exec (args, calc, &fe);
1445 entry.reset();
1446 entry.val = ret;
1447 stack.push (entry);
1448
1449 break;
1450
1451 default:
1452 break;
1453 }
1454 }
1455
1456 if (!d->sheet) {
1457 delete parser;
1458 delete converter;
1459 delete calc;
1460 }
1461
1462 // more than one value in stack ? unsuccesful execution...
1463 if( stack.count() != 1 )
1464 return Value::errorVALUE();
1465
1466 return stack.pop().val;
1467
1468}
1469
1470// Debugging aid
1471
1472TQString Formula::dump() const
1473{
1474 TQString result;
1475
1476 if( d->dirty )
1477 {
1478 Tokens tokens = scan( d->expression );
1479 compile( tokens );
1480 }
1481
1482 result = TQString("Expression: [%1]\n").arg( d->expression );
1483#if 0
1484 Value value = eval();
1485 result.append( TQString("Result: %1\n").arg(
1486 converter->asString(value).asString() ) );
1487#endif
1488
1489 result.append(" Constants:\n");
1490 for( unsigned c = 0; c < d->constants.count(); c++ )
1491 {
1492 TQString vtext;
1493 Value val = d->constants[c];
1494 if( val.isString() ) vtext = TQString("[%1]").arg( val.asString() );
1495 else if( val.isNumber() ) vtext = TQString("%1").arg( val.asFloat() );
1496 else if( val.isBoolean() ) vtext = TQString("%1").arg( val.asBoolean() ? "True":"False");
1497 else if( val.isError() ) vtext = "error";
1498 else vtext = "???";
1499 result += TQString(" #%1 = %2\n").arg(c).arg( vtext );
1500 }
1501
1502 result.append("\n");
1503 result.append(" Code:\n");
1504 for( unsigned i = 0; i < d->codes.count(); i++ )
1505 {
1506 TQString ctext;
1507 switch( d->codes[i].type )
1508 {
1509 case Opcode::Load: ctext = TQString("Load #%1").arg( d->codes[i].index ); break;
1510 case Opcode::Ref: ctext = TQString("Ref #%1").arg( d->codes[i].index ); break;
1511 case Opcode::Function: ctext = TQString("Function (%1)").arg( d->codes[i].index ); break;
1512 case Opcode::Add: ctext = "Add"; break;
1513 case Opcode::Sub: ctext = "Sub"; break;
1514 case Opcode::Mul: ctext = "Mul"; break;
1515 case Opcode::Div: ctext = "Div"; break;
1516 case Opcode::Neg: ctext = "Neg"; break;
1517 case Opcode::Concat: ctext = "Concat"; break;
1518 case Opcode::Pow: ctext = "Pow"; break;
1519 case Opcode::Equal: ctext = "Equal"; break;
1520 case Opcode::Not: ctext = "Not"; break;
1521 case Opcode::Less: ctext = "Less"; break;
1522 case Opcode::Greater: ctext = "Greater"; break;
1523 default: ctext = "Unknown"; break;
1524 }
1525 result.append( " " ).append( ctext ).append("\n");
1526 }
1527
1528 return result;
1529}
1530
1531TQTextStream& operator<<( TQTextStream& ts, Formula formula )
1532{
1533 ts << formula.dump();
1534 return ts;
1535}
For every cell in the spread sheet there is a Cell object.
Class Formula encapsulates a formula for a cell.
Definition formula.h:239
Tokens tokens() const
Returns list of tokens associated with this formula.
Definition formula.cpp:456
void setExpression(const TQString &expr)
Sets the expression for this formula.
Definition formula.cpp:407
Formula()
Creates a formula that is not owned by any sheet.
Definition formula.cpp:378
Sheet * sheet() const
Returns the cell which owns this formula.
Definition formula.cpp:398
~Formula()
Destroys the formula.
Definition formula.cpp:388
Cell * cell() const
Returns the cell which owns this formula.
Definition formula.cpp:393
Value eval() const
Evaluates the formula and returns the result.
Definition formula.cpp:1172
void clear()
Clears everything, makes as like a newly constructed formula.
Definition formula.cpp:442
TQString expression() const
Gets the expression of this formula.
Definition formula.cpp:416
bool isValid() const
Returns true if the specified expression is valid, i.e.
Definition formula.cpp:424
Tokens scan(const TQString &expr, TDELocale *locale=0) const
Given an expression, this function separates it into tokens.
Definition formula.cpp:464
bool isNamedArea(const TQString &expr) const
helper function: return true for valid named area
Definition formula.cpp:1146
Represents the position of a single cell in a workbook.
Represents a region within a workbook.
bool isValid() const
Returns true if this Range represents a valid region of a spreadsheet.
TQString description() const
Returns a short description of the token.
Definition formula.cpp:276
long asInteger() const
Returns integer value for an integer token.
Definition formula.cpp:243
bool isOperator() const
Returns true if token is an operator token.
Definition formula.h:135
double asFloat() const
Returns floating-point value for a floating-point token.
Definition formula.cpp:249
TQString sheetName() const
Returns sheet name in a cell reference token.
Definition formula.cpp:267
bool asBoolean() const
Returns boolean value for an boolean token.
Definition formula.cpp:236
bool isRange() const
Returns true if token is a range reference token.
Definition formula.h:145
Op
operator types
Definition formula.h:60
@ GreaterEqual
>=
Definition formula.h:77
@ Slash
/ (division)
Definition formula.h:65
@ InvalidOp
invalid operator
Definition formula.h:61
@ Ampersand
& (string concat)
Definition formula.h:71
@ Semicolon
; (argument separator)
Definition formula.h:70
@ Caret
^ (power)
Definition formula.h:66
bool isString() const
Returns true if token is a string token.
Definition formula.h:130
bool isInteger() const
Returns true if token is a integer token.
Definition formula.h:115
TQString text() const
Returns text associated with the token.
Definition formula.h:103
Type type() const
Returns type of the token.
Definition formula.h:94
Token(Type type=Unknown, const TQString &text=TQString(), int pos=-1)
Creates a token.
Definition formula.cpp:212
bool isCell() const
Returns true if token is a cell reference token.
Definition formula.h:140
bool isBoolean() const
Returns true if token is a boolean token.
Definition formula.h:110
TQString asString() const
Returns string value for a string token.
Definition formula.cpp:255
Type
token types
Definition formula.h:44
@ Identifier
function name or named area
Definition formula.h:53
@ Integer
14, 3, 1977
Definition formula.h:47
@ Cell
$A$1, F4, Sheet2!B5, 'Sales Forecast'!Sum
Definition formula.h:51
@ Boolean
True, False (also i18n-ized).
Definition formula.h:46
@ Float
3.141592, 1e10, 5.9e-7
Definition formula.h:48
@ Range
C1:C100.
Definition formula.h:52
@ Operator
+, *, /, -
Definition formula.h:50
@ String
"KOffice", "The quick brown fox..."
Definition formula.h:49
@ Unknown
unknown type
Definition formula.h:45
Op asOperator() const
Returns operator value for an operator token.
Definition formula.cpp:261
bool isFloat() const
Returns true if token is a floating-point token.
Definition formula.h:120
Class Tokens represents array of tokens.
Definition formula.h:220
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
Value add(const Value &a, const Value &b)
basic arithmetic operations
The ValueConverter class can convert Value between various different formats.
The ValueParser parses a text input from the user, generating Value in the desired format.
Definition valueparser.h:40
Provides a wrapper for cell value.
static const Value & empty()
Returns constant reference to empty value.
void setValue(const Value &v)
Sets this value to another value.
bool isBoolean() const
Returns true if the type of this value is Boolean.
bool isNumber() const
Returns true if the type of this value is either integer or floating-point.
bool allowComparison(const Value &v) const
Returns true if it is OK to compare this value with v.
double asFloat() const
Returns the floating-point value of this value.
TQString asString() const
Returns the string value of this value.
static const Value & errorVALUE()
Returns constant reference to VALUE!
bool isError() const
Returns true if this value holds error information.
int compare(const Value &v) const
Returns -1, 0, 1, depends whether this value is less than, equal to, or greater than v.
bool isString() const
Returns true if the type of this value is string.
bool asBoolean() const
Returns the boolean value of this value.
static const Value & errorNA()
Returns constant reference to N/A error.
The KSpread namespace.
Definition commands.h:39
bool isIdentifier(TQChar ch)
helper function: return true for valid identifier character
Definition formula.cpp:356
Token::Op matchOperator(const TQString &text)
helper function: return operator of given token text e.g.
Definition formula.cpp:130
TQTextStream & operator<<(TQTextStream &ts, Formula formula)
Dumps the formula, should be used only to assist debugging.