kspread/qpro/libqpro/src/formula.cpp
1#include <qpro/common.h>
2
3#include <iostream>
4#include <sstream>
5
6#include <string.h>
7
8#include <qpro/formula.h>
9#include <qpro/stream.h>
10
11// ------------------------------------------------------------------
12
13
14QpFormulaStack::QpFormulaStack()
15 : cIdx(-1)
16 , cMax(3)
17{
18 cStack = new char*[cMax];
19}
20
21QpFormulaStack::~QpFormulaStack()
22{
23}
24
25void
26QpFormulaStack::bracket(const char* pBefore, const char* pAfter)
27{
28 if( cIdx >= 0 )
29 {
30 int lLen = strlen(cStack[cIdx]) + 1;
31
32 if( pBefore ) lLen += strlen(pBefore);
33
34 if( pAfter ) lLen += strlen(pAfter);
35
36 char* lNew = new char[ lLen ];
37
38 lNew[0] = '\0';
39
40 if( pBefore ) strcpy( lNew, pBefore );
41
42 strcat( lNew, cStack[cIdx] );
43
44 if( pAfter ) strcat( lNew, pAfter );
45
46 delete [] cStack[cIdx];
47 cStack[cIdx] = lNew;
48 }
49}
50
51void
52QpFormulaStack::join(int pCnt, const char* pSeparator)
53{
54 int lFirstIdx = 1 - pCnt; // really 0 - pCnt +1
55
56 if( pCnt > 0 && (cIdx-lFirstIdx) >=0 )
57 {
58 int lSepLen = strlen(pSeparator);
59 int lLen = lSepLen * (pCnt-1) +1; // +1 for null terminator
60
61 for(int lIdx=lFirstIdx; lIdx <= 0; ++lIdx)
62 {
63 lLen += strlen( cStack[cIdx + lIdx] );
64 }
65
66 char* lNew = new char[lLen];
67
68 lNew[0] = '\0';
69
70 for(int lIdx=lFirstIdx; lIdx <= 0; ++lIdx)
71 {
72 strcat( lNew, cStack[cIdx + lIdx] );
73 if( lIdx != 0 )
74 {
75 strcat( lNew, pSeparator );
76 }
77 }
78
79 pop(pCnt);
80
81 push(lNew);
82
83 delete [] lNew;
84 }
85}
86
87void
88QpFormulaStack::pop(int pCnt)
89{
90 while( cIdx >= 0 && pCnt-- )
91 {
92 delete [] cStack[cIdx--];
93 }
94}
95
96void
97QpFormulaStack::push(const char* pString)
98{
99 ++cIdx;
100
101 if(cIdx == cMax)
102 {
103 cMax += 10;
104
105 char** cTemp = new char*[cMax];
106
107 for(int lIdx=0; lIdx<cIdx; ++lIdx)
108 {
109 cTemp[lIdx] = cStack[lIdx];
110 }
111
112 delete [] cStack;
113 cStack = cTemp;
114 }
115
116 cStack[cIdx] = strcpy(new char[strlen(pString)+1], pString);
117}
118
119const char*
120QpFormulaStack::top()
121{
122 return (cIdx >=0 ? cStack[cIdx] : 0);
123}
124
125const char*
126QpFormulaStack::operator [] (int pIdx)
127{
128 char* lResult = 0;
129
130 if( pIdx <= 0 && (cIdx + pIdx) >= 0 )
131 {
132 lResult = cStack[cIdx + pIdx];
133 }
134
135 return lResult;
136}
137
138// ------------------------------------------------------------------
139
140static const QpFormulaConv gConv[] =
141{
142 {0, QpFormula::floatFunc, 0},
143 {1, QpFormula::ref, 0},
144 {2, QpFormula::ref, 0},
145 {4, QpFormula::func1, "("},
146 {5, QpFormula::intFunc, 0},
147 {6, QpFormula::stringFunc, 0},
148// {7, default ??? don't know what this is ???
149 {8, QpFormula::unaryOperand, "-"},
150 {9, QpFormula::binaryOperand, "+"},
151 {10, QpFormula::binaryOperand, "-"},
152 {11, QpFormula::binaryOperand, "*"},
153 {12, QpFormula::binaryOperand, "/"},
154 {13, QpFormula::binaryOperand, "^"},
155 {14, QpFormula::binaryOperand, "="},
156 {15, QpFormula::binaryOperand, "<>"},
157 {16, QpFormula::binaryOperand, "<="},
158 {17, QpFormula::binaryOperand, ">="},
159 {18, QpFormula::binaryOperand, "<"},
160 {19, QpFormula::binaryOperand, ">"},
161 {20, QpFormula::binaryOperand, "#AND#"},
162 {21, QpFormula::binaryOperand, "#OR#"},
163 {22, QpFormula::unaryOperand, "#NOT#"},
164 {23, QpFormula::unaryOperand, "+"},
165 {24, QpFormula::binaryOperand, "&"},
166// {25, Halt ??? don't know what this is ???
167// {26, dll ??? don't know what this is ???
168// {27, extended no operands ??? don't know what this is ???
169// {28, extended operands ??? don't know what this is ???
170// {29, Reserved
171// {30, Reserved
172// {31, NA
173 {32, QpFormula::func0, "@err"},
174 {33, QpFormula::func1, "@abs("},
175 {34, QpFormula::func1, "@int("},
176 {35, QpFormula::func1, "@sqrt("},
177 {36, QpFormula::func1, "@log("},
178 {37, QpFormula::func1, "@ln("},
179 {38, QpFormula::func0, "@pi"},
180 {39, QpFormula::func1, "@sin("},
181 {40, QpFormula::func1, "@cos("},
182 {41, QpFormula::func1, "@tan("},
183 {42, QpFormula::func2, "@atan2("},
184 {43, QpFormula::func1, "@atan("},
185 {44, QpFormula::func1, "@asin("},
186 {45, QpFormula::func1, "@acos("},
187 {46, QpFormula::func1, "@exp("},
188 {47, QpFormula::func2, "@mod("},
189 {48, QpFormula::funcV, "@choose("},
190 {49, QpFormula::func1, "@isna("},
191 {50, QpFormula::func1, "@iserr("},
192 {51, QpFormula::func0, "@false"},
193 {52, QpFormula::func0, "@true"},
194 {53, QpFormula::func0, "@rand"},
195 {54, QpFormula::func3, "@date("},
196 {55, QpFormula::func0, "@now"},
197 {56, QpFormula::func3, "@pmt("},
198 {57, QpFormula::func3, "@pv("},
199 {58, QpFormula::func3, "@fv("},
200 {59, QpFormula::func3, "@if("},
201 {60, QpFormula::func1, "@day("},
202 {61, QpFormula::func1, "@month("},
203 {62, QpFormula::func1, "@year("},
204 {63, QpFormula::func2, "@round("},
205 {64, QpFormula::func3, "@time("},
206 {65, QpFormula::func1, "@hour("},
207 {66, QpFormula::func1, "@minute("},
208 {67, QpFormula::func1, "@second("},
209 {68, QpFormula::func1, "@isnumber("},
210 {69, QpFormula::func1, "@isstring("},
211 {70, QpFormula::func1, "@length("},
212 {71, QpFormula::func1, "@value("},
213 {72, QpFormula::func2, "@string("},
214 {73, QpFormula::func3, "@mid("},
215 {74, QpFormula::func1, "@char("},
216 {75, QpFormula::func1, "@code("},
217 {76, QpFormula::func3, "@find("},
218 {77, QpFormula::func1, "@dateVal("},
219 {78, QpFormula::func1, "@timeVal("},
220 {79, QpFormula::func1, "@cellPtr("},
221 {80, QpFormula::funcV, "@sum("},
222 {81, QpFormula::funcV, "@avg("},
223 {82, QpFormula::funcV, "@count("},
224 {83, QpFormula::funcV, "@min("},
225 {84, QpFormula::funcV, "@max("},
226 {85, QpFormula::func3, "@vlookup("}, // would have expected func4 ???
227 {86, QpFormula::func2, "@npv("},
228 {87, QpFormula::funcV, "@var("},
229 {88, QpFormula::funcV, "@std("},
230 {89, QpFormula::func2, "@irr("},
231 {90, QpFormula::func3, "@hlookup("}, // would have expected func4 ???
232 {91, QpFormula::func3, "@dsum("},
233 {92, QpFormula::func3, "@davg("},
234 {93, QpFormula::func3, "@dcount("},
235 {94, QpFormula::func3, "@dmin("},
236 {95, QpFormula::func3, "@dmax("},
237 {96, QpFormula::func3, "@dvar("},
238 {97, QpFormula::func3, "@dstd("},
239 {98, QpFormula::func3, "@index("},
240 {99, QpFormula::func1, "@cols("},
241 {100, QpFormula::func1, "@rows("},
242 {101, QpFormula::func2, "@repeat("},
243 {102, QpFormula::func1, "@upper("},
244 {103, QpFormula::func1, "@lower("},
245 {104, QpFormula::func2, "@left("},
246 {105, QpFormula::func2, "@right("},
247 {106, QpFormula::func4, "@replace("},
248 {107, QpFormula::func1, "@proper("},
249 {108, QpFormula::func2, "@cell("},
250 {109, QpFormula::func1, "@trim("},
251 {110, QpFormula::func1, "@clean("},
252 {111, QpFormula::func1, "@s("},
253 {112, QpFormula::func1, "@n("},
254 {113, QpFormula::func1, "@exact("},
255// {114, QpFormula::func1, "@call("},
256 {115, QpFormula::func1, "@@("},
257 {116, QpFormula::func3, "@rate("},
258 {117, QpFormula::func3, "@term("},
259 {118, QpFormula::func3, "@cterm("},
260 {119, QpFormula::func3, "@sln("},
261 {120, QpFormula::func4, "@syd("},
262 {121, QpFormula::func4, "@ddb("},
263 {122, QpFormula::funcV, "@stds("},
264 {123, QpFormula::funcV, "@vars("},
265 {124, QpFormula::func1, "@dstds("},
266 {125, QpFormula::func1, "@dvars("},
267 {126, QpFormula::func1, "@pval("},
268 {127, QpFormula::func1, "@paymt("},
269 {128, QpFormula::func1, "@fval("},
270 {129, QpFormula::func1, "@nper("},
271 {130, QpFormula::func1, "@irate("},
272 {131, QpFormula::func1, "@ipaymt("},
273 {132, QpFormula::func1, "@ppaymt("},
274 {133, QpFormula::func1, "@sumproduct("},
275 {134, QpFormula::func1, "@memavail("},
276 {135, QpFormula::func1, "@mememsavail("},
277 {136, QpFormula::func1, "@fileexists("},
278 {137, QpFormula::func1, "@curval("},
279 {138, QpFormula::func1, "@degrees("},
280 {139, QpFormula::func1, "@radians("},
281 {140, QpFormula::func1, "@hextonum("},
282 {141, QpFormula::func1, "@numtohex("},
283 {142, QpFormula::func1, "@today("},
284 {143, QpFormula::func3, "@npv("},
285 {144, QpFormula::func1, "@cellindex2d("},
286 {145, QpFormula::func1, "@version("},
287 {154, QpFormula::func1, "@sheets("},
288 {157, QpFormula::func1, "@index3d("},
289 {158, QpFormula::func1, "@cellindex3d("},
290 {159, QpFormula::func1, "@property("},
291 {160, QpFormula::func1, "@ddelink("},
292 {161, QpFormula::func1, "@command("},
293 {0, 0, 0}
294};
295
296QpFormula::QpFormula(QpRecFormulaCell& pCell, QpTableNames& pTable)
297 : cArgSeparator(strcpy(new char[2],","))
298 , cCell(pCell)
299 , cFormula( (unsigned char*)pCell.formula(), (unsigned int)pCell.formulaLen() )
300 , cFormulaRefs( (unsigned char*)&pCell.formula()[pCell.formulaReferences()]
301 , (unsigned)(pCell.formulaLen()-pCell.formulaReferences())
302 )
303 , cReplaceFunc(0)
304 , cFormulaStart(strcpy(new char[2],"+"))
305 , cIdx(0)
306 , cDropLeadingAt(0)
307 , cTable(pTable)
308{
309}
310
311QpFormula::~QpFormula()
312{
313 delete [] cArgSeparator;
314 cArgSeparator = 0;
315
316 delete [] cFormulaStart;
317 cFormulaStart = 0;
318
319 cReplaceFunc = 0;
320}
321
322void
323QpFormula::argSeparator(const char* pArg)
324{
325 delete [] cArgSeparator;
326 cArgSeparator = strcpy(new char[strlen(pArg)+1], pArg);
327}
328
329char*
330QpFormula::formula()
331{
332 TQP_INT8 lOperand;
333
334 cStack.push(cFormulaStart);
335
336 while( cFormula >> lOperand, cFormula && lOperand != 3 )
337 {
338 int lFound = 0;
339 int lIdx;
340
341 if(cReplaceFunc != 0)
342 {
343 // search through override list for this function/operand
344 for( lIdx=0
345 ; !lFound && cReplaceFunc[lIdx].cFunc != 0
346 ; ++lIdx
347 )
348 {
349 if( cReplaceFunc[lIdx].cOperand == lOperand )
350 {
351 lFound = -1;
352 TQP_DEBUG("Processing " << (int)lOperand << endl);
353 (*cReplaceFunc[lIdx].cFunc)(*this, cReplaceFunc[lIdx].cArg);
354 }
355 }
356 }
357
358 // if no override then find the default
359 for( lIdx=0
360 ; !lFound && gConv[lIdx].cFunc != 0
361 ; ++lIdx
362 )
363 {
364 if( gConv[lIdx].cOperand == lOperand )
365 {
366 lFound = -1;
367 TQP_DEBUG("Processing " << (int)lOperand << endl);
368 (*gConv[lIdx].cFunc)(*this, gConv[lIdx].cArg);
369 }
370 }
371
372 TQP_DEBUG("Top = " << cStack.top() << endl);
373 }
374
375 cStack.join(2, "");
376
377 TQP_DEBUG("Formula = " << cStack.top() << endl);
378 return strcpy(new char[strlen(cStack.top())+1], cStack.top());
379}
380
381void
382QpFormula::formulaStart(const char* pFirstChar)
383{
384 delete [] cFormulaStart;
385 cFormulaStart = strcpy(new char[strlen(pFirstChar)+1], pFirstChar);
386}
387
388void
389QpFormula::binaryOperandReal(const char* pOper)
390{
391 cStack.join( 2, pOper );
392}
393
394void
395QpFormula::absKludgeReal(const char*/*pOper*/)
396{
397 // kspread doesn't (yet) have the abs function so do it ourselves
398 // using 'if( (arg) < 0, -(arg), arg )'
399
400 cStack.bracket();
401
402 char* lArg = strcpy(new char[strlen(cStack.top())+1], cStack.top());
403
404 cStack.bracket("", "<0");
405
406 cStack.push(lArg);
407 cStack.bracket("-", "");
408
409 cStack.push(lArg);
410
411 cStack.join(3, cArgSeparator);
412
413 cStack.bracket("if(");
414
415 delete [] lArg;
416}
417
418void
419QpFormula::func0Real(const char* pFunc)
420{
421 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
422
423 cStack.push( lFunc );
424}
425
426void
427QpFormula::func1Real(const char* pFunc)
428{
429 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
430
431 cStack.bracket( lFunc );
432}
433
434void
435QpFormula::func2Real(const char* pFunc)
436{
437 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
438
439 cStack.join( 2, cArgSeparator );
440 cStack.bracket( lFunc );
441}
442
443void
444QpFormula::func3Real(const char* pFunc)
445{
446 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
447
448 cStack.join( 3, cArgSeparator );
449 cStack.bracket( lFunc );
450}
451
452void
453QpFormula::func4Real(const char* pFunc)
454{
455 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
456
457 cStack.join( 4, cArgSeparator );
458 cStack.bracket( lFunc );
459}
460
461void
462QpFormula::funcVReal(const char* pFunc)
463{
464 TQP_INT8 lCnt;
465 const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
466
467 cFormula >> lCnt;
468
469 cStack.join( lCnt, cArgSeparator );
470 cStack.bracket( lFunc );
471}
472
473void
474QpFormula::floatFuncReal(const char*)
475{
476 TQP_INT64 lFloat;
477 std::ostringstream lNum;
478
479 cFormula >> lFloat;
480
481 lNum << lFloat << ends;
482
483 cStack.push( lNum.str().c_str() );
484}
485
486void
487QpFormula::intFuncReal(const char*)
488{
489 TQP_INT16 lInt;
490 std::ostringstream lNum;
491
492 cFormula >> lInt;
493
494 lNum << lInt << ends;
495
496 cStack.push( lNum.str().c_str() );
497}
498
499void
500QpFormula::dropLeadingAt(int pBool)
501{
502 cDropLeadingAt = pBool;
503}
504
505void
506QpFormula::refReal(const char*)
507{
508 char lRef[100]; // ??? hard coded length
509
510 cCell.cellRef( lRef, cTable, cFormulaRefs );
511
512 cStack.push( lRef );
513}
514
515void
516QpFormula::replaceFunc(QpFormulaConv* pFuncEntry)
517{
518 cReplaceFunc = pFuncEntry;
519}
520
521void
522QpFormula::stringFuncReal(const char*)
523{
524 char* lString = 0;
525
526 cFormula >> lString;
527
528 char* lQuoteString = new char[strlen(lString)+3];
529
530 lQuoteString[0] = '"';
531 strcpy(&lQuoteString[1], lString);
532 strcat(lQuoteString, "\"");
533
534 cStack.push( lQuoteString );
535
536 delete [] lString;
537 delete [] lQuoteString;
538}
539
540void
541QpFormula::unaryOperandReal(const char* pOper)
542{
543 cStack.bracket( pOper, "" );
544}
545
546