summaryrefslogtreecommitdiffstats
path: root/filters/kspread/qpro/libqpro/src/formula.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/kspread/qpro/libqpro/src/formula.cpp')
-rw-r--r--filters/kspread/qpro/libqpro/src/formula.cpp546
1 files changed, 546 insertions, 0 deletions
diff --git a/filters/kspread/qpro/libqpro/src/formula.cpp b/filters/kspread/qpro/libqpro/src/formula.cpp
new file mode 100644
index 000000000..aab0bb8d4
--- /dev/null
+++ b/filters/kspread/qpro/libqpro/src/formula.cpp
@@ -0,0 +1,546 @@
+#include <qpro/common.h>
+
+#include <iostream>
+#include <sstream>
+
+#include <string.h>
+
+#include <qpro/formula.h>
+#include <qpro/stream.h>
+
+// ------------------------------------------------------------------
+
+
+QpFormulaStack::QpFormulaStack()
+ : cIdx(-1)
+ , cMax(3)
+{
+ cStack = new char*[cMax];
+}
+
+QpFormulaStack::~QpFormulaStack()
+{
+}
+
+void
+QpFormulaStack::bracket(const char* pBefore, const char* pAfter)
+{
+ if( cIdx >= 0 )
+ {
+ int lLen = strlen(cStack[cIdx]) + 1;
+
+ if( pBefore ) lLen += strlen(pBefore);
+
+ if( pAfter ) lLen += strlen(pAfter);
+
+ char* lNew = new char[ lLen ];
+
+ lNew[0] = '\0';
+
+ if( pBefore ) strcpy( lNew, pBefore );
+
+ strcat( lNew, cStack[cIdx] );
+
+ if( pAfter ) strcat( lNew, pAfter );
+
+ delete [] cStack[cIdx];
+ cStack[cIdx] = lNew;
+ }
+}
+
+void
+QpFormulaStack::join(int pCnt, const char* pSeparator)
+{
+ int lFirstIdx = 1 - pCnt; // really 0 - pCnt +1
+
+ if( pCnt > 0 && (cIdx-lFirstIdx) >=0 )
+ {
+ int lSepLen = strlen(pSeparator);
+ int lLen = lSepLen * (pCnt-1) +1; // +1 for null terminator
+
+ for(int lIdx=lFirstIdx; lIdx <= 0; ++lIdx)
+ {
+ lLen += strlen( cStack[cIdx + lIdx] );
+ }
+
+ char* lNew = new char[lLen];
+
+ lNew[0] = '\0';
+
+ for(int lIdx=lFirstIdx; lIdx <= 0; ++lIdx)
+ {
+ strcat( lNew, cStack[cIdx + lIdx] );
+ if( lIdx != 0 )
+ {
+ strcat( lNew, pSeparator );
+ }
+ }
+
+ pop(pCnt);
+
+ push(lNew);
+
+ delete [] lNew;
+ }
+}
+
+void
+QpFormulaStack::pop(int pCnt)
+{
+ while( cIdx >= 0 && pCnt-- )
+ {
+ delete [] cStack[cIdx--];
+ }
+}
+
+void
+QpFormulaStack::push(const char* pString)
+{
+ ++cIdx;
+
+ if(cIdx == cMax)
+ {
+ cMax += 10;
+
+ char** cTemp = new char*[cMax];
+
+ for(int lIdx=0; lIdx<cIdx; ++lIdx)
+ {
+ cTemp[lIdx] = cStack[lIdx];
+ }
+
+ delete [] cStack;
+ cStack = cTemp;
+ }
+
+ cStack[cIdx] = strcpy(new char[strlen(pString)+1], pString);
+}
+
+const char*
+QpFormulaStack::top()
+{
+ return (cIdx >=0 ? cStack[cIdx] : 0);
+}
+
+const char*
+QpFormulaStack::operator [] (int pIdx)
+{
+ char* lResult = 0;
+
+ if( pIdx <= 0 && (cIdx + pIdx) >= 0 )
+ {
+ lResult = cStack[cIdx + pIdx];
+ }
+
+ return lResult;
+}
+
+// ------------------------------------------------------------------
+
+static const QpFormulaConv gConv[] =
+{
+ {0, QpFormula::floatFunc, 0},
+ {1, QpFormula::ref, 0},
+ {2, QpFormula::ref, 0},
+ {4, QpFormula::func1, "("},
+ {5, QpFormula::intFunc, 0},
+ {6, QpFormula::stringFunc, 0},
+// {7, default ??? don't know what this is ???
+ {8, QpFormula::unaryOperand, "-"},
+ {9, QpFormula::binaryOperand, "+"},
+ {10, QpFormula::binaryOperand, "-"},
+ {11, QpFormula::binaryOperand, "*"},
+ {12, QpFormula::binaryOperand, "/"},
+ {13, QpFormula::binaryOperand, "^"},
+ {14, QpFormula::binaryOperand, "="},
+ {15, QpFormula::binaryOperand, "<>"},
+ {16, QpFormula::binaryOperand, "<="},
+ {17, QpFormula::binaryOperand, ">="},
+ {18, QpFormula::binaryOperand, "<"},
+ {19, QpFormula::binaryOperand, ">"},
+ {20, QpFormula::binaryOperand, "#AND#"},
+ {21, QpFormula::binaryOperand, "#OR#"},
+ {22, QpFormula::unaryOperand, "#NOT#"},
+ {23, QpFormula::unaryOperand, "+"},
+ {24, QpFormula::binaryOperand, "&"},
+// {25, Halt ??? don't know what this is ???
+// {26, dll ??? don't know what this is ???
+// {27, extended no operands ??? don't know what this is ???
+// {28, extended operands ??? don't know what this is ???
+// {29, Reserved
+// {30, Reserved
+// {31, NA
+ {32, QpFormula::func0, "@err"},
+ {33, QpFormula::func1, "@abs("},
+ {34, QpFormula::func1, "@int("},
+ {35, QpFormula::func1, "@sqrt("},
+ {36, QpFormula::func1, "@log("},
+ {37, QpFormula::func1, "@ln("},
+ {38, QpFormula::func0, "@pi"},
+ {39, QpFormula::func1, "@sin("},
+ {40, QpFormula::func1, "@cos("},
+ {41, QpFormula::func1, "@tan("},
+ {42, QpFormula::func2, "@atan2("},
+ {43, QpFormula::func1, "@atan("},
+ {44, QpFormula::func1, "@asin("},
+ {45, QpFormula::func1, "@acos("},
+ {46, QpFormula::func1, "@exp("},
+ {47, QpFormula::func2, "@mod("},
+ {48, QpFormula::funcV, "@choose("},
+ {49, QpFormula::func1, "@isna("},
+ {50, QpFormula::func1, "@iserr("},
+ {51, QpFormula::func0, "@false"},
+ {52, QpFormula::func0, "@true"},
+ {53, QpFormula::func0, "@rand"},
+ {54, QpFormula::func3, "@date("},
+ {55, QpFormula::func0, "@now"},
+ {56, QpFormula::func3, "@pmt("},
+ {57, QpFormula::func3, "@pv("},
+ {58, QpFormula::func3, "@fv("},
+ {59, QpFormula::func3, "@if("},
+ {60, QpFormula::func1, "@day("},
+ {61, QpFormula::func1, "@month("},
+ {62, QpFormula::func1, "@year("},
+ {63, QpFormula::func2, "@round("},
+ {64, QpFormula::func3, "@time("},
+ {65, QpFormula::func1, "@hour("},
+ {66, QpFormula::func1, "@minute("},
+ {67, QpFormula::func1, "@second("},
+ {68, QpFormula::func1, "@isnumber("},
+ {69, QpFormula::func1, "@isstring("},
+ {70, QpFormula::func1, "@length("},
+ {71, QpFormula::func1, "@value("},
+ {72, QpFormula::func2, "@string("},
+ {73, QpFormula::func3, "@mid("},
+ {74, QpFormula::func1, "@char("},
+ {75, QpFormula::func1, "@code("},
+ {76, QpFormula::func3, "@find("},
+ {77, QpFormula::func1, "@dateVal("},
+ {78, QpFormula::func1, "@timeVal("},
+ {79, QpFormula::func1, "@cellPtr("},
+ {80, QpFormula::funcV, "@sum("},
+ {81, QpFormula::funcV, "@avg("},
+ {82, QpFormula::funcV, "@count("},
+ {83, QpFormula::funcV, "@min("},
+ {84, QpFormula::funcV, "@max("},
+ {85, QpFormula::func3, "@vlookup("}, // would have expected func4 ???
+ {86, QpFormula::func2, "@npv("},
+ {87, QpFormula::funcV, "@var("},
+ {88, QpFormula::funcV, "@std("},
+ {89, QpFormula::func2, "@irr("},
+ {90, QpFormula::func3, "@hlookup("}, // would have expected func4 ???
+ {91, QpFormula::func3, "@dsum("},
+ {92, QpFormula::func3, "@davg("},
+ {93, QpFormula::func3, "@dcount("},
+ {94, QpFormula::func3, "@dmin("},
+ {95, QpFormula::func3, "@dmax("},
+ {96, QpFormula::func3, "@dvar("},
+ {97, QpFormula::func3, "@dstd("},
+ {98, QpFormula::func3, "@index("},
+ {99, QpFormula::func1, "@cols("},
+ {100, QpFormula::func1, "@rows("},
+ {101, QpFormula::func2, "@repeat("},
+ {102, QpFormula::func1, "@upper("},
+ {103, QpFormula::func1, "@lower("},
+ {104, QpFormula::func2, "@left("},
+ {105, QpFormula::func2, "@right("},
+ {106, QpFormula::func4, "@replace("},
+ {107, QpFormula::func1, "@proper("},
+ {108, QpFormula::func2, "@cell("},
+ {109, QpFormula::func1, "@trim("},
+ {110, QpFormula::func1, "@clean("},
+ {111, QpFormula::func1, "@s("},
+ {112, QpFormula::func1, "@n("},
+ {113, QpFormula::func1, "@exact("},
+// {114, QpFormula::func1, "@call("},
+ {115, QpFormula::func1, "@@("},
+ {116, QpFormula::func3, "@rate("},
+ {117, QpFormula::func3, "@term("},
+ {118, QpFormula::func3, "@cterm("},
+ {119, QpFormula::func3, "@sln("},
+ {120, QpFormula::func4, "@syd("},
+ {121, QpFormula::func4, "@ddb("},
+ {122, QpFormula::funcV, "@stds("},
+ {123, QpFormula::funcV, "@vars("},
+ {124, QpFormula::func1, "@dstds("},
+ {125, QpFormula::func1, "@dvars("},
+ {126, QpFormula::func1, "@pval("},
+ {127, QpFormula::func1, "@paymt("},
+ {128, QpFormula::func1, "@fval("},
+ {129, QpFormula::func1, "@nper("},
+ {130, QpFormula::func1, "@irate("},
+ {131, QpFormula::func1, "@ipaymt("},
+ {132, QpFormula::func1, "@ppaymt("},
+ {133, QpFormula::func1, "@sumproduct("},
+ {134, QpFormula::func1, "@memavail("},
+ {135, QpFormula::func1, "@mememsavail("},
+ {136, QpFormula::func1, "@fileexists("},
+ {137, QpFormula::func1, "@curval("},
+ {138, QpFormula::func1, "@degrees("},
+ {139, QpFormula::func1, "@radians("},
+ {140, QpFormula::func1, "@hextonum("},
+ {141, QpFormula::func1, "@numtohex("},
+ {142, QpFormula::func1, "@today("},
+ {143, QpFormula::func3, "@npv("},
+ {144, QpFormula::func1, "@cellindex2d("},
+ {145, QpFormula::func1, "@version("},
+ {154, QpFormula::func1, "@sheets("},
+ {157, QpFormula::func1, "@index3d("},
+ {158, QpFormula::func1, "@cellindex3d("},
+ {159, QpFormula::func1, "@property("},
+ {160, QpFormula::func1, "@ddelink("},
+ {161, QpFormula::func1, "@command("},
+ {0, 0, 0}
+};
+
+QpFormula::QpFormula(QpRecFormulaCell& pCell, QpTableNames& pTable)
+ : cArgSeparator(strcpy(new char[2],","))
+ , cCell(pCell)
+ , cFormula( (unsigned char*)pCell.formula(), (unsigned int)pCell.formulaLen() )
+ , cFormulaRefs( (unsigned char*)&pCell.formula()[pCell.formulaReferences()]
+ , (unsigned)(pCell.formulaLen()-pCell.formulaReferences())
+ )
+ , cReplaceFunc(0)
+ , cFormulaStart(strcpy(new char[2],"+"))
+ , cIdx(0)
+ , cDropLeadingAt(0)
+ , cTable(pTable)
+{
+}
+
+QpFormula::~QpFormula()
+{
+ delete [] cArgSeparator;
+ cArgSeparator = 0;
+
+ delete [] cFormulaStart;
+ cFormulaStart = 0;
+
+ cReplaceFunc = 0;
+}
+
+void
+QpFormula::argSeparator(const char* pArg)
+{
+ delete [] cArgSeparator;
+ cArgSeparator = strcpy(new char[strlen(pArg)+1], pArg);
+}
+
+char*
+QpFormula::formula()
+{
+ TQP_INT8 lOperand;
+
+ cStack.push(cFormulaStart);
+
+ while( cFormula >> lOperand, cFormula && lOperand != 3 )
+ {
+ int lFound = 0;
+ int lIdx;
+
+ if(cReplaceFunc != 0)
+ {
+ // search through override list for this function/operand
+ for( lIdx=0
+ ; !lFound && cReplaceFunc[lIdx].cFunc != 0
+ ; ++lIdx
+ )
+ {
+ if( cReplaceFunc[lIdx].cOperand == lOperand )
+ {
+ lFound = -1;
+ TQP_DEBUG("Processing " << (int)lOperand << endl);
+ (*cReplaceFunc[lIdx].cFunc)(*this, cReplaceFunc[lIdx].cArg);
+ }
+ }
+ }
+
+ // if no override then find the default
+ for( lIdx=0
+ ; !lFound && gConv[lIdx].cFunc != 0
+ ; ++lIdx
+ )
+ {
+ if( gConv[lIdx].cOperand == lOperand )
+ {
+ lFound = -1;
+ TQP_DEBUG("Processing " << (int)lOperand << endl);
+ (*gConv[lIdx].cFunc)(*this, gConv[lIdx].cArg);
+ }
+ }
+
+ TQP_DEBUG("Top = " << cStack.top() << endl);
+ }
+
+ cStack.join(2, "");
+
+ TQP_DEBUG("Formula = " << cStack.top() << endl);
+ return strcpy(new char[strlen(cStack.top())+1], cStack.top());
+}
+
+void
+QpFormula::formulaStart(const char* pFirstChar)
+{
+ delete [] cFormulaStart;
+ cFormulaStart = strcpy(new char[strlen(pFirstChar)+1], pFirstChar);
+}
+
+void
+QpFormula::binaryOperandReal(const char* pOper)
+{
+ cStack.join( 2, pOper );
+}
+
+void
+QpFormula::absKludgeReal(const char*/*pOper*/)
+{
+ // kspread doesn't (yet) have the abs function so do it ourselves
+ // using 'if( (arg) < 0, -(arg), arg )'
+
+ cStack.bracket();
+
+ char* lArg = strcpy(new char[strlen(cStack.top())+1], cStack.top());
+
+ cStack.bracket("", "<0");
+
+ cStack.push(lArg);
+ cStack.bracket("-", "");
+
+ cStack.push(lArg);
+
+ cStack.join(3, cArgSeparator);
+
+ cStack.bracket("if(");
+
+ delete [] lArg;
+}
+
+void
+QpFormula::func0Real(const char* pFunc)
+{
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cStack.push( lFunc );
+}
+
+void
+QpFormula::func1Real(const char* pFunc)
+{
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cStack.bracket( lFunc );
+}
+
+void
+QpFormula::func2Real(const char* pFunc)
+{
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cStack.join( 2, cArgSeparator );
+ cStack.bracket( lFunc );
+}
+
+void
+QpFormula::func3Real(const char* pFunc)
+{
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cStack.join( 3, cArgSeparator );
+ cStack.bracket( lFunc );
+}
+
+void
+QpFormula::func4Real(const char* pFunc)
+{
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cStack.join( 4, cArgSeparator );
+ cStack.bracket( lFunc );
+}
+
+void
+QpFormula::funcVReal(const char* pFunc)
+{
+ TQP_INT8 lCnt;
+ const char* lFunc = (cDropLeadingAt && pFunc[0] == '@' ? &pFunc[1] : pFunc);
+
+ cFormula >> lCnt;
+
+ cStack.join( lCnt, cArgSeparator );
+ cStack.bracket( lFunc );
+}
+
+void
+QpFormula::floatFuncReal(const char*)
+{
+ TQP_INT64 lFloat;
+ std::ostringstream lNum;
+
+ cFormula >> lFloat;
+
+ lNum << lFloat << ends;
+
+ cStack.push( lNum.str().c_str() );
+}
+
+void
+QpFormula::intFuncReal(const char*)
+{
+ TQP_INT16 lInt;
+ std::ostringstream lNum;
+
+ cFormula >> lInt;
+
+ lNum << lInt << ends;
+
+ cStack.push( lNum.str().c_str() );
+}
+
+void
+QpFormula::dropLeadingAt(int pBool)
+{
+ cDropLeadingAt = pBool;
+}
+
+void
+QpFormula::refReal(const char*)
+{
+ char lRef[100]; // ??? hard coded length
+
+ cCell.cellRef( lRef, cTable, cFormulaRefs );
+
+ cStack.push( lRef );
+}
+
+void
+QpFormula::replaceFunc(QpFormulaConv* pFuncEntry)
+{
+ cReplaceFunc = pFuncEntry;
+}
+
+void
+QpFormula::stringFuncReal(const char*)
+{
+ char* lString = 0;
+
+ cFormula >> lString;
+
+ char* lQuoteString = new char[strlen(lString)+3];
+
+ lQuoteString[0] = '"';
+ strcpy(&lQuoteString[1], lString);
+ strcat(lQuoteString, "\"");
+
+ cStack.push( lQuoteString );
+
+ delete [] lString;
+ delete [] lQuoteString;
+}
+
+void
+QpFormula::unaryOperandReal(const char* pOper)
+{
+ cStack.bracket( pOper, "" );
+}
+
+