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"
30#include "valueconverter.h"
31#include "valueparser.h"
39#include <tqvaluevector.h>
40#include <tqvaluestack.h>
82 enum { Nop = 0, Load, Ref, Cell, Range, Function, Add, Sub, Neg, Mul, Div,
83 Pow, Concat, Not, Equal, Less, Greater };
88 Opcode(): type(Nop), index(0) {};
89 Opcode(
unsigned t ): type(t), index(0) {};
90 Opcode(
unsigned t,
unsigned i ): type(t), index(i) {};
102 TQValueVector<Opcode> codes;
103 TQValueVector<Value> constants;
106class TokenStack :
public TQValueVector<Token>
110 bool isEmpty()
const;
111 unsigned itemCount()
const;
112 void push(
const Token& token );
115 const Token& top(
unsigned index );
126const Token Token::null;
134 if( text.length() == 1 )
137 switch( p.unicode() )
157 if( text.length() == 2 )
191 default: prec = -1;
break;
197static Value tokenAsValue(
const Token& token )
222 m_type = token.m_type;
223 m_text = token.m_text;
230 m_type = token.m_type;
231 m_text = token.m_text;
239 return m_text.lower() ==
"true";
245 if(
isInteger() )
return m_text.toLong();
251 if(
isFloat() )
return m_text.toDouble();
257 if(
isString() )
return m_text.mid( 1, m_text.length()-2 );
258 else return TQString();
270 int i = m_text.find(
'!' );
271 if( i < 0 )
return TQString();
272 TQString sheet = m_text.left( i );
282 case Boolean: desc =
"Boolean";
break;
283 case Integer: desc =
"Integer";
break;
284 case Float: desc =
"Float";
break;
285 case String: desc =
"String";
break;
287 case Cell: desc =
"Cell";
break;
288 case Range: desc =
"Range";
break;
289 case Operator: desc =
"Operator";
break;
290 default: desc =
"Unknown";
break;
293 while( desc.length() < 10 ) desc.prepend(
' ' );
295 desc.prepend( TQString::number( m_pos ) );
296 desc.append(
" : " ).append( m_text );
306TokenStack::TokenStack(): TQValueVector<
Token>()
312bool TokenStack::isEmpty()
const
314 return topIndex == 0;
317unsigned TokenStack::itemCount()
const
322void TokenStack::push(
const Token& token )
325 at( topIndex++ ) = token;
328Token TokenStack::pop()
330 return (topIndex > 0 ) ? Token( at( --topIndex ) ) : Token();
333const Token& TokenStack::top()
338const Token& TokenStack::top(
unsigned index )
340 if( topIndex > index )
341 return at( topIndex-index-1 );
345void TokenStack::ensureSpace()
347 while( topIndex >= size() )
348 resize( size() + 10 );
358 return ( ch.unicode() ==
'_' ) || (ch.unicode() ==
'$' ) || ( ch.isLetter() );
409 d->expression = expr;
418 return d->expression;
428 TDELocale* locale = d->cell ? d->cell->locale() : 0;
429 if ((!locale) && d->sheet)
430 locale = d->sheet->doc()->locale();
444 d->expression = TQString();
447 d->constants.clear();
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 );
470 enum { Start, Finish, Bad, InNumber, InDecimal, InExpIndicator, InExponent,
471 InString, InIdentifier, InCell, InRange, InSheetOrAreaName } state;
474 TQString thousand = locale ? locale->thousandsSeparator() :
"";
475 TQString decimal = locale ? locale->decimalSymbol() :
".";
492 ex.append( TQChar() );
495 while( (state != Bad) && (state != Finish) && (i < ex.length()) )
507 if( ch.isSpace() ) i++;
510 else if( ch.isDigit() )
516 else if ( ch ==
'"' )
518 tokenText.append( ex[i++] );
526 state = InIdentifier;
530 else if ( ch.unicode() ==
'\'' )
533 state = InSheetOrAreaName;
537 else if ( ch == decimal )
539 tokenText.append( ex[i++] );
544 else if ( ch == TQChar::null )
554 s.append( ch ).append( ex[i+1] );
567 int len = s.length();
578 if(
isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
583 tokenText.append( ex[i++] );
600 TQRegExp exp(
"(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
601 int n = exp.search( tokenText );
620 if(
isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
629 for(
int j = tokenText.length()-1; j>=0; j-- )
630 if( tokenText[j] ==
'!' )
633 cell.prepend( tokenText[j] );
634 TQRegExp exp(
"(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
635 if( exp.search(
cell ) != 0 )
652 tokenText.append( ex[i++] );
669 if(
isIdentifier( ch ) || ch.isDigit() ) tokenText.append( ex[i++] );
680 case InSheetOrAreaName:
683 if ( ch.unicode() !=
'\'' )
684 tokenText.append( ex[i++] );
693 tokenText.append( ex[i++] );
712 if( ch.isDigit() ) tokenText.append( ex[i++] );
715 else if( !thousand.isEmpty() && ( ch ==thousand[0] ) ) i++;
719 else if(( !decimal.isEmpty() && ( ch == decimal[0] ) ) || (ch ==
'.'))
721 tokenText.append(
'.' );
727 else if( ch.upper() ==
'E' )
729 tokenText.append(
'E' );
731 state = InExpIndicator;
737 tokenText.append( ex[i++] );
745 state = InIdentifier;
760 if( ch.isDigit() ) tokenText.append( ex[i++] );
763 else if( ch.upper() ==
'E' )
765 tokenText.append(
'E' );
767 state = InExpIndicator;
782 if( ( ch ==
'+' ) || ( ch ==
'-' ) ) tokenText.append( ex[i++] );
785 else if( ch.isDigit() ) state = InExponent;
795 if( ch.isDigit() ) tokenText.append( ex[i++] );
809 if( ch !=
'"' ) tokenText.append( ex[i++] );
813 tokenText.append( ch ); i++;
833void Formula::compile(
const Tokens& tokens )
const
839 d->constants.clear();
842 if(
tokens.count() == 0 )
return;
844 TokenStack syntaxStack;
845 TQValueStack<int> argStack;
846 unsigned argCount = 1;
848 for(
unsigned i = 0; i <=
tokens.count(); i++ )
862 syntaxStack.push( token );
863 d->constants.append( tokenAsValue( token ) );
864 d->codes.append( Opcode( Opcode::Load, d->constants.count()-1 ) );
872 syntaxStack.push( token );
873 d->constants.append(
Value( token.
text() ) );
875 d->codes.append( Opcode( Opcode::Cell, d->constants.count()-1 ) );
877 d->codes.append( Opcode( Opcode::Range, d->constants.count()-1 ) );
879 d->codes.append( Opcode( Opcode::Ref, d->constants.count()-1 ) );
885 if( syntaxStack.itemCount() >= 3 )
887 Token arg = syntaxStack.top();
888 Token par = syntaxStack.top( 1 );
889 Token
id = syntaxStack.top( 2 );
894 argStack.push( argCount );
902 if( syntaxStack.itemCount() >= 1 )
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 ) );
917 bool ruleFound =
false;
922 if( syntaxStack.itemCount() >= 5 )
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 );
947 if( syntaxStack.itemCount() >= 4 )
949 Token par2 = syntaxStack.top();
950 Token arg = syntaxStack.top( 1 );
951 Token par1 = syntaxStack.top( 2 );
952 Token
id = syntaxStack.top( 3 );
963 syntaxStack.push( arg );
964 d->codes.append( Opcode( Opcode::Function, argCount ) );
965 argCount = argStack.empty() ? 0 : argStack.pop();
972 if( syntaxStack.itemCount() >= 3 )
974 Token par2 = syntaxStack.top();
975 Token par1 = syntaxStack.top( 1 );
976 Token
id = syntaxStack.top( 2 );
986 d->codes.append( Opcode( Opcode::Function, 0 ) );
992 if( syntaxStack.itemCount() >= 3 )
994 Token right = syntaxStack.top();
995 Token y = syntaxStack.top( 1 );
996 Token left = syntaxStack.top( 2 );
1007 syntaxStack.push( y );
1018 if( syntaxStack.itemCount() >= 3 )
1020 Token b = syntaxStack.top();
1021 Token op = syntaxStack.top( 1 );
1022 Token a = syntaxStack.top( 2 );
1034 syntaxStack.push( b );
1038 case Token::Plus: d->codes.append( Opcode::Add );
break;
1039 case Token::Minus: d->codes.append( Opcode::Sub );
break;
1041 case Token::Slash: d->codes.append( Opcode::Div );
break;
1042 case Token::Caret: d->codes.append( Opcode::Pow );
break;
1046 case Token::Equal: d->codes.append( Opcode::Equal );
break;
1047 case Token::Less: d->codes.append( Opcode::Less );
break;
1052 d->codes.append( Opcode::Equal );
1053 d->codes.append( Opcode::Not );
1058 d->codes.append( Opcode::Greater );
1059 d->codes.append( Opcode::Not );
1064 d->codes.append( Opcode::Less );
1065 d->codes.append( Opcode::Not );
1078 if( syntaxStack.itemCount() >= 3 )
1080 Token x = syntaxStack.top();
1081 Token op2 = syntaxStack.top( 1 );
1082 Token op1 = syntaxStack.top( 2 );
1092 syntaxStack.push( x );
1094 d->codes.append( Opcode( Opcode::Neg ) );
1103 if( syntaxStack.itemCount() == 2 )
1105 Token x = syntaxStack.top();
1106 Token op = syntaxStack.top( 1 );
1115 syntaxStack.push( x );
1117 d->codes.append( Opcode( Opcode::Neg ) );
1121 if( !ruleFound )
break;
1126 syntaxStack.push( token );
1132 if( syntaxStack.itemCount() == 2 )
1141 d->constants.clear();
1148 TQString tokenText( expr );
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()) {
1167 void reset () { row1 = col1 = row2 = col2 = -1; };
1169 int row1, col1, row2, col2;
1174 TQValueStack<stackEntry> stack;
1179 TQValueVector<Value> args;
1189 converter =
sheet->doc()->converter();
1190 calc =
sheet->doc()->calc();
1201 fe.mycol = fe.myrow = 0;
1203 fe.mycol = d->cell->column();
1204 fe.myrow = d->cell->row();
1210 d->valid =
tokens.valid();
1218 for(
unsigned pc = 0; pc < d->codes.count(); pc++ )
1221 Opcode& opcode = d->codes[pc];
1222 index = opcode.index;
1223 switch( opcode.type )
1232 entry.val = d->constants[index];
1239 entry.val = stack.pop().val;
1241 entry.val = calc->mul (entry.val, -1);
1249 val2 = stack.pop().val;
1250 val1 = stack.pop().val;
1251 val2 = calc->
add( val1, val2 );
1258 val2 = stack.pop().val;
1259 val1 = stack.pop().val;
1260 val2 = calc->sub( val1, val2 );
1267 val2 = stack.pop().val;
1268 val1 = stack.pop().val;
1269 val2 = calc->mul( val1, val2 );
1276 val2 = stack.pop().val;
1277 val1 = stack.pop().val;
1278 val2 = calc->div( val1, val2 );
1285 val2 = stack.pop().val;
1286 val1 = stack.pop().val;
1287 val2 = calc->pow( val1, val2 );
1294 case Opcode::Concat:
1295 val1 = converter->asString (stack.pop().val);
1296 val2 = converter->asString (stack.pop().val);
1308 val1 = converter->asBoolean (stack.pop().val);
1320 val1 = stack.pop().val;
1321 val2 = stack.pop().val;
1324 else if( val2.
compare( val1 ) == 0 )
1325 val1 =
Value (
true);
1327 val1 =
Value (
false);
1335 val1 = stack.pop().val;
1336 val2 = stack.pop().val;
1339 else if( val2.
compare( val1 ) < 0 )
1340 val1 =
Value (
true);
1342 val1 =
Value (
false);
1349 case Opcode::Greater:
1350 val1 = stack.pop().val;
1351 val2 = stack.pop().val;
1354 else if( val2.
compare( val1 ) > 0 )
1355 val1 =
Value (
true);
1357 val1 =
Value (
false);
1365 c = d->constants[index].asString();
1373 val1 =
cell.sheet()->value (
cell.column(),
cell.row());
1375 entry.col1 = entry.col2 =
cell.column();
1376 entry.row1 = entry.row2 =
cell.row();
1384 c = d->constants[index].asString();
1392 val1 = range.sheet()->valueRange (range.startCol(), range.startRow(),
1393 range.endCol(), range.endRow());
1395 entry.col1 = range.startCol();
1396 entry.row1 = range.startRow();
1397 entry.col2 = range.endCol();
1398 entry.row2 = range.endRow();
1406 val1 = d->constants[index];
1413 case Opcode::Function:
1414 if( stack.count() < index )
1421 fe.ranges.reserve (index);
1423 for( ; index; index-- )
1425 stackEntry e = stack.pop();
1426 args.insert (args.begin(), e.val);
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;
1437 val1 = converter->asString (stack.pop().val);
1440 function = FunctionRepository::self()->function ( val1.
asString() );
1444 ret = function->exec (args, calc, &fe);
1463 if( stack.count() != 1 )
1466 return stack.pop().val;
1472TQString Formula::dump()
const
1482 result = TQString(
"Expression: [%1]\n").arg( d->expression );
1485 result.append( TQString(
"Result: %1\n").arg(
1486 converter->asString(value).asString() ) );
1489 result.append(
" Constants:\n");
1490 for(
unsigned c = 0; c < d->constants.count(); c++ )
1493 Value val = d->constants[c];
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";
1499 result += TQString(
" #%1 = %2\n").arg(c).arg( vtext );
1502 result.append(
"\n");
1503 result.append(
" Code:\n");
1504 for(
unsigned i = 0; i < d->codes.count(); i++ )
1507 switch( d->codes[i].type )
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;
1525 result.append(
" " ).append( ctext ).append(
"\n");
1533 ts << formula.dump();
For every cell in the spread sheet there is a Cell object.
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.
long asInteger() const
Returns integer value for an integer token.
bool isOperator() const
Returns true if token is an operator token.
double asFloat() const
Returns floating-point value for a floating-point token.
TQString sheetName() const
Returns sheet name in a cell reference token.
bool asBoolean() const
Returns boolean value for an boolean token.
bool isRange() const
Returns true if token is a range reference token.
@ InvalidOp
invalid operator
@ Ampersand
& (string concat)
@ Semicolon
; (argument separator)
bool isString() const
Returns true if token is a string token.
bool isInteger() const
Returns true if token is a integer token.
TQString text() const
Returns text associated with the token.
Type type() const
Returns type of the token.
Token(Type type=Unknown, const TQString &text=TQString(), int pos=-1)
Creates a token.
bool isCell() const
Returns true if token is a cell reference token.
bool isBoolean() const
Returns true if token is a boolean token.
TQString asString() const
Returns string value for a string token.
@ Identifier
function name or named area
@ Cell
$A$1, F4, Sheet2!B5, 'Sales Forecast'!Sum
@ Boolean
True, False (also i18n-ized).
@ Float
3.141592, 1e10, 5.9e-7
@ String
"KOffice", "The quick brown fox..."
Op asOperator() const
Returns operator value for an operator token.
bool isFloat() const
Returns true if token is a floating-point token.
Class Tokens represents array of tokens.
The ValueCalc class is used to perform all sorts of calculations.
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.
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.
bool isIdentifier(TQChar ch)
helper function: return true for valid identifier character
Token::Op matchOperator(const TQString &text)
helper function: return operator of given token text e.g.
TQTextStream & operator<<(TQTextStream &ts, Formula formula)
Dumps the formula, should be used only to assist debugging.