/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
*
|
* This file is part of xlslib -- A multiplatform, C/C++ library
|
* for dynamic generation of Excel(TM) files.
|
*
|
* Copyright 2010-2013 Ger Hobbelt All Rights Reserved.
|
*
|
* Redistribution and use in source and binary forms, with or without modification, are
|
* permitted provided that the following conditions are met:
|
*
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
* conditions and the following disclaimer.
|
*
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
* provided with the distribution.
|
*
|
* THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
#include "../xlslib/formula_expr.h"
|
#include "../xlslib/globalrec.h"
|
#include "../xlslib/datast.h"
|
#include "../xlslib/formula_estimate.h"
|
#include "../xlslib/formula_cell.h"
|
#include "../xlslib/formula.h"
|
#include "../xlslib/common.h"
|
#include "../xlslib/sheetrec.h"
|
|
#ifdef __BCPLUSPLUS__
|
#include <malloc.h>
|
// malloc.h needed for calloc. RLN 111208
|
#include <memory.h>
|
// memory.h needed for memset. RLN 111215
|
// These may be needed for other compilers as well.
|
#endif
|
|
using namespace xlslib_core;
|
using namespace xlslib_strings;
|
|
expression_node_t::expression_node_t(CGlobalRecords& gRecords)
|
{
|
(void)gRecords;
|
}
|
|
expression_node_t::~expression_node_t()
|
{
|
}
|
|
expression_node_t* expression_node_t::GetChild(unsigned16_t index) const
|
{
|
(void)index;
|
return NULL;
|
}
|
|
unsigned16_t expression_node_t::GetNumberOfChilds(void) const
|
{
|
return 0;
|
}
|
|
void expression_node_t::DestroyAST(void)
|
{
|
// first destroy the children, bottom-up recursively.
|
unsigned16_t chn = GetNumberOfChilds();
|
while (chn-- > 0) {
|
expression_node_t* ch = GetChild(chn);
|
|
XL_ASSERT(ch);
|
if (ch) {
|
ch->DestroyAST();
|
}
|
}
|
|
// then destroy this instance itself
|
delete this;
|
}
|
|
void expression_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
(void)dst;
|
XL_ASSERTS("Should never get here! MUST be handled by the derived class' method of similar name!");
|
|
// do nothing: propagate the currently set expected value.
|
}
|
|
size_t expression_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
XL_ASSERTS("Should never get here! MUST be handled by the derived class' method of similar name!");
|
return 0;
|
}
|
|
signed8_t expression_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)stack;
|
(void)include_subtree;
|
XL_ASSERTS("Should never get here! MUST be handled by the derived class' method of similar name!");
|
return NO_ERRORS;
|
}
|
|
terminal_node_t::terminal_node_t(CGlobalRecords& gRecords) :
|
expression_node_t(gRecords)
|
{
|
}
|
|
terminal_node_t::~terminal_node_t()
|
{
|
}
|
|
boolean_value_node_t::boolean_value_node_t(CGlobalRecords& gRecords, bool v) :
|
terminal_node_t(gRecords),
|
value(v)
|
{
|
}
|
|
boolean_value_node_t::~boolean_value_node_t()
|
{
|
}
|
|
void boolean_value_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetBoolean(value);
|
}
|
|
size_t boolean_value_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 2;
|
}
|
|
signed8_t boolean_value_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
|
return stack.PushBoolean(value);
|
}
|
|
integer_value_node_t::integer_value_node_t(CGlobalRecords& gRecords, signed32_t v) :
|
terminal_node_t(gRecords),
|
value(v)
|
{
|
}
|
|
integer_value_node_t::~integer_value_node_t()
|
{
|
}
|
|
void integer_value_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetInteger(value);
|
}
|
|
size_t integer_value_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
if (value >= 0 && value <= 65535) {
|
return 2+1; // ptgInt
|
}
|
return 8+1; // ptgNum
|
}
|
|
signed8_t integer_value_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushInteger(value);
|
}
|
|
float_value_node_t::float_value_node_t(CGlobalRecords& gRecords, double v) :
|
terminal_node_t(gRecords),
|
value(v)
|
{
|
}
|
|
float_value_node_t::~float_value_node_t()
|
{
|
}
|
|
void float_value_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetFloatingPoint(value);
|
}
|
|
size_t float_value_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 8+1;
|
}
|
|
signed8_t float_value_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushFloatingPoint(value);
|
}
|
|
error_value_node_t::error_value_node_t(CGlobalRecords& gRecords, errcode_t v) :
|
terminal_node_t(gRecords),
|
value(v)
|
{
|
}
|
|
error_value_node_t::~error_value_node_t()
|
{
|
}
|
|
void error_value_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetErrorCode(value);
|
}
|
|
size_t error_value_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 1+1;
|
}
|
|
signed8_t error_value_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushError(value);
|
}
|
|
missing_arg_node_t::missing_arg_node_t(CGlobalRecords& gRecords) :
|
terminal_node_t(gRecords)
|
{
|
}
|
|
missing_arg_node_t::~missing_arg_node_t()
|
{
|
}
|
|
void missing_arg_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetErrorCode(XLERR_N_A);
|
}
|
|
size_t missing_arg_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 1;
|
}
|
|
signed8_t missing_arg_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushMissingArgument();
|
}
|
|
text_value_node_t::text_value_node_t(CGlobalRecords& gRecords, const std::string& v) :
|
terminal_node_t(gRecords),
|
m_GlobalRecords(gRecords)
|
{
|
m_GlobalRecords.char2str16(v, this->value);
|
}
|
|
text_value_node_t::text_value_node_t(CGlobalRecords& gRecords, const ustring& v) :
|
terminal_node_t(gRecords),
|
m_GlobalRecords(gRecords)
|
{
|
m_GlobalRecords.wide2str16(v, this->value);
|
}
|
|
#ifndef __FRAMEWORK__
|
text_value_node_t::text_value_node_t(CGlobalRecords& gRecords, const u16string& v) :
|
terminal_node_t(gRecords),
|
value(v),
|
m_GlobalRecords(gRecords)
|
{
|
}
|
|
#endif
|
text_value_node_t::~text_value_node_t()
|
{
|
}
|
|
text_value_node_t& text_value_node_t::operator =(const text_value_node_t &src)
|
{
|
(void)src;
|
throw std::string("Should never have invoked the text_value_node_t copy operator!");
|
}
|
|
void text_value_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
dst.SetText(value);
|
}
|
|
size_t text_value_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 1 + 1 + CGlobalRecords::IsASCII(value) * value.length() + value.length();
|
}
|
|
signed8_t text_value_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushText(value);
|
}
|
|
cell_deref_node_t::cell_deref_node_t(CGlobalRecords& gRecords, const cell_t& v, cell_addr_mode_t opt, cell_op_class_t opclass) :
|
terminal_node_t(gRecords),
|
row_(v.GetRow()),
|
col_(v.GetCol()),
|
idx_(v.GetWorksheet() ? v.GetWorksheet()->GetIndex() : invalidIndex),
|
//worksheet_ref(NULL),
|
attr(opt),
|
operand_class(opclass)
|
{
|
}
|
|
/* Ger had something in mind, but this is not needed for multi-sheet references. If we ever figure out the intent, turn it back on
|
cell_deref_node_t::cell_deref_node_t(CGlobalRecords& gRecords, const cell_t& v, const worksheet* ws, cell_addr_mode_t opt, cell_op_class_t opclass) :
|
terminal_node_t(gRecords),
|
row_(v.GetRow()),
|
col_(v.GetCol()),
|
idx_(v.GetWorksheet() ? v.GetWorksheet()->GetIndex() : invalidIndex),
|
worksheet_ref(ws),
|
attr(opt),
|
operand_class(opclass)
|
{
|
}
|
*/
|
|
cell_deref_node_t::~cell_deref_node_t()
|
{
|
}
|
|
void cell_deref_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
/*
|
* We don't want to spend the effort to 'know' (~ make this code aware) which cell reference produces what result, neither in value nor in type, so we
|
* fake it and make it easy to ourselves: we 'guestimate' the cell/reference will return an
|
* error code and we mark the expression as 'calc on load' to mask our ignorance.
|
*/
|
dst.SetCalcOnLoad();
|
dst.SetErrorCode(XLERR_VALUE);
|
}
|
|
size_t cell_deref_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 1+2+2;
|
}
|
|
signed8_t cell_deref_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree;
|
return stack.PushReference(row_, col_, idx_, attr, operand_class);
|
}
|
|
cellarea_deref_node_t::cellarea_deref_node_t(CGlobalRecords& gRecords, const cell_t& u_l_c, const cell_t& l_r_c, cell_addr_mode_t attr,
|
cell_op_class_t opclass) :
|
cell_deref_node_t(gRecords, u_l_c, attr, opclass),
|
lrrow_(l_r_c.GetRow()),
|
lrcol_(l_r_c.GetCol()),
|
lridx_(l_r_c.GetWorksheet() ? l_r_c.GetWorksheet()->GetIndex() : invalidIndex)
|
{
|
}
|
|
/* See other Ger comment
|
cellarea_deref_node_t::cellarea_deref_node_t(CGlobalRecords& gRecords, const cell_t& u_l_c, const cell_t& l_r_c, const worksheet* ws, cell_addr_mode_t attr,
|
cell_op_class_t opclass) :
|
cell_deref_node_t(gRecords, u_l_c, ws, attr, opclass),
|
lrrow_(l_r_c.GetRow()),
|
lrcol_(l_r_c.GetCol()),
|
lridx_(l_r_c.GetWorksheet() ? l_r_c.GetWorksheet()->GetIndex() : invalidIndex)
|
{
|
}
|
*/
|
|
cellarea_deref_node_t::~cellarea_deref_node_t()
|
{
|
}
|
|
size_t cellarea_deref_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
|
return 1+2*(2+2);
|
}
|
|
signed8_t cellarea_deref_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)include_subtree; // stop warning
|
return stack.PushAreaReference(row_, col_, idx_, lrrow_, lrcol_, lridx_, attr, operand_class);
|
}
|
|
void cellarea_deref_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
/*
|
* We don't want to spend the effort to 'know' (~ make this code aware) which cell reference produces what result, neither in value nor in type, so we
|
* fake it and make it easy to ourselves: we 'guestimate' the cell/reference will return an
|
* error code and we mark the expression as 'calc on load' to mask our ignorance.
|
*/
|
dst.SetCalcOnLoad();
|
dst.SetErrorCode(XLERR_VALUE);
|
}
|
|
operator_basenode_t::operator_basenode_t(CGlobalRecords& gRecords, expr_operator_code_t o) :
|
expression_node_t(gRecords),
|
op(o)
|
{
|
}
|
|
operator_basenode_t::~operator_basenode_t()
|
{
|
}
|
|
void operator_basenode_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
/*
|
* We don't want to spend the effort to 'know' (~ make this code aware) which operation produces what result, neither in value nor in type, so we
|
* fake it and make it easy to ourselves: we 'guestimate' the operation will return an
|
* error code and we mark the expression as 'calc on load' to mask our ignorance.
|
*
|
* Remark: there's just a few operations we care about: those we are willing to take to the dance.
|
*/
|
dst.SetCalcOnLoad();
|
dst.SetErrorCode(XLERR_VALUE);
|
}
|
|
unary_op_node_t::unary_op_node_t(CGlobalRecords& gRecords, expr_operator_code_t op, expression_node_t* a) :
|
operator_basenode_t(gRecords, op),
|
arg(a)
|
{
|
XL_ASSERT(a);
|
|
switch (op) {
|
default:
|
throw std::string("Not a valid unary operator");
|
|
case OP_UPLUS: // Unary Plus
|
case OP_UMINUS: // Unary Minus
|
case OP_PERCENT: // Percent Sign
|
case OP_PAREN: // Enclose into parentheses
|
// okay
|
break;
|
}
|
}
|
|
unary_op_node_t::~unary_op_node_t()
|
{
|
}
|
|
expression_node_t* unary_op_node_t::GetChild(unsigned16_t index) const
|
{
|
if (index == 0) {
|
return arg;
|
}
|
return NULL;
|
}
|
|
unsigned16_t unary_op_node_t::GetNumberOfChilds(void) const
|
{
|
return 1;
|
}
|
|
size_t unary_op_node_t::GetSize(bool include_subtree) const
|
{
|
size_t len = 1;
|
|
if (include_subtree) {
|
XL_ASSERT(GetChild(0));
|
len += GetChild(0)->GetSize(include_subtree);
|
}
|
return len;
|
}
|
|
signed8_t unary_op_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
signed8_t errcode = NO_ERRORS;
|
|
if (include_subtree) {
|
XL_ASSERT(GetChild(0));
|
errcode |= GetChild(0)->DumpData(stack, include_subtree);
|
}
|
|
errcode |= stack.PushOperator(op);
|
|
#ifdef XL_WITH_ASSERTIONS
|
switch (op) {
|
default:
|
XL_ASSERTS("Should never get here!");
|
|
case OP_UPLUS: // Unary Plus
|
case OP_UMINUS: // Unary Minus
|
case OP_PERCENT: // Percent Sign
|
case OP_PAREN: // Enclose into parentheses
|
// okay
|
break;
|
}
|
#endif
|
|
return errcode;
|
}
|
|
binary_op_node_t::binary_op_node_t(CGlobalRecords& gRecords, expr_operator_code_t op, expression_node_t* arg1, expression_node_t* arg2) :
|
operator_basenode_t(gRecords, op)
|
{
|
args[0] = arg1;
|
args[1] = arg2;
|
|
switch (op) {
|
default:
|
throw std::string("Not a valid binary operator");
|
|
case OP_ADD:
|
case OP_SUB:
|
case OP_MUL:
|
case OP_DIV:
|
case OP_POWER:
|
case OP_CONCAT:
|
case OP_LE:
|
case OP_LT:
|
case OP_EQ:
|
case OP_GE:
|
case OP_GT:
|
case OP_NE:
|
case OP_ISECT: // intersection ~ Excel 'space' operator
|
case OP_UNION: // union ~ Excel 'comma' operator
|
case OP_RANGE: // minimal bounding rectangle ~ Excel 'colon' operator
|
// okay
|
break;
|
}
|
}
|
|
binary_op_node_t::~binary_op_node_t()
|
{
|
}
|
|
expression_node_t* binary_op_node_t::GetChild(unsigned16_t index) const
|
{
|
if (index <= 1) {
|
return args[index];
|
}
|
return NULL;
|
}
|
|
unsigned16_t binary_op_node_t::GetNumberOfChilds(void) const
|
{
|
return 2;
|
}
|
|
size_t binary_op_node_t::GetSize(bool include_subtree) const
|
{
|
size_t len = 1;
|
|
if (include_subtree) {
|
XL_ASSERT(GetChild(0));
|
len += GetChild(0)->GetSize(include_subtree);
|
XL_ASSERT(GetChild(1));
|
len += GetChild(1)->GetSize(include_subtree);
|
}
|
return len;
|
}
|
|
signed8_t binary_op_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
signed8_t errcode = NO_ERRORS;
|
|
if (include_subtree) {
|
XL_ASSERT(GetChild(0));
|
errcode |= GetChild(0)->DumpData(stack, include_subtree);
|
XL_ASSERT(GetChild(1));
|
errcode |= GetChild(1)->DumpData(stack, include_subtree);
|
}
|
|
errcode |= stack.PushOperator(op);
|
|
#ifdef XL_WITH_ASSERTIONS
|
switch (op) {
|
default:
|
XL_ASSERTS("Not a valid binary operator");
|
|
case OP_ADD:
|
case OP_SUB:
|
case OP_MUL:
|
case OP_DIV:
|
case OP_POWER:
|
case OP_CONCAT:
|
case OP_LE:
|
case OP_LT:
|
case OP_EQ:
|
case OP_GE:
|
case OP_GT:
|
case OP_NE:
|
case OP_ISECT: // intersection ~ Excel 'space' operator
|
case OP_UNION: // union ~ Excel 'comma' operator
|
case OP_RANGE: // minimal bounding rectangle ~ Excel 'colon' operator
|
// okay
|
break;
|
}
|
#endif
|
|
return errcode;
|
}
|
|
function_basenode_t::function_basenode_t(CGlobalRecords& gRecords, expr_function_code_t f, cell_op_class_t op_c) :
|
expression_node_t(gRecords),
|
func(f),
|
op_class(op_c)
|
{
|
}
|
|
function_basenode_t::~function_basenode_t()
|
{
|
}
|
|
// Note: commented out case statements because code is too new for older Excel programs
|
void function_basenode_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
/*
|
* We don't want to spend the effort to 'know' (~ make this code aware) which function produces what result, neither in value nor in type, so we
|
* fake it and make it easy to ourselves: we 'guestimate' the UDF will return an
|
* error code and we mark the expression as 'calc on load' to mask our ignorance.
|
*
|
* Remark: there's just a few functions we care about: RANDOM() and a few others, which we are willing to take to the dance.
|
*/
|
switch (func) {
|
default:
|
dst.SetCalcOnLoad();
|
dst.SetErrorCode(XLERR_VALUE);
|
break;
|
|
case FUNC_UDF:
|
XL_ASSERTS("Should've been handled by the udf class!");
|
|
case FUNC_IF:
|
case FUNC_ISNA:
|
case FUNC_ISERROR:
|
case FUNC_TRUE:
|
case FUNC_FALSE:
|
case FUNC_AND:
|
case FUNC_OR:
|
case FUNC_NOT:
|
case FUNC_ISREF:
|
case FUNC_ISERR:
|
case FUNC_ISTEXT:
|
case FUNC_ISNUMBER:
|
case FUNC_ISBLANK:
|
case FUNC_T:
|
case FUNC_N:
|
case FUNC_ISNONTEXT:
|
case FUNC_ISLOGICAL:
|
case FUNC_ISPMT:
|
case FUNC_ISTHAIDIGIT:
|
dst.SetCalcOnLoad();
|
dst.SetBoolean(false); // faked value estimate!
|
break;
|
|
case FUNC_COUNT:
|
case FUNC_ROW:
|
case FUNC_COLUMN:
|
case FUNC_DCOUNT:
|
case FUNC_DAY:
|
case FUNC_MONTH:
|
case FUNC_YEAR:
|
case FUNC_WEEKDAY:
|
case FUNC_HOUR:
|
case FUNC_MINUTE:
|
case FUNC_SECOND:
|
case FUNC_COUNTA:
|
case FUNC_DCOUNTA:
|
case FUNC_COUNTIF:
|
case FUNC_COUNTBLANK:
|
case FUNC_WEEKNUM:
|
case FUNC_COUNTIFS:
|
dst.SetCalcOnLoad();
|
dst.SetInteger(42); // faked value estimate!
|
break;
|
|
case FUNC_PI:
|
dst.SetCalcOnLoad();
|
dst.SetFloatingPoint(3.1415); // faked value estimate!
|
break;
|
|
case FUNC_SUM:
|
case FUNC_AVERAGE:
|
case FUNC_MIN:
|
case FUNC_MAX:
|
case FUNC_STDEV:
|
case FUNC_SIN:
|
case FUNC_COS:
|
case FUNC_TAN:
|
case FUNC_ATAN:
|
case FUNC_SQRT:
|
case FUNC_EXP:
|
case FUNC_LN:
|
case FUNC_LOG10:
|
case FUNC_ABS:
|
case FUNC_ROUND:
|
case FUNC_MOD:
|
case FUNC_DSUM:
|
case FUNC_DAVERAGE:
|
case FUNC_DMIN:
|
case FUNC_DMAX:
|
case FUNC_DSTDEV:
|
case FUNC_VAR:
|
case FUNC_DVAR:
|
case FUNC_DATE:
|
case FUNC_TIME:
|
case FUNC_ATAN2:
|
case FUNC_ASIN:
|
case FUNC_ACOS:
|
case FUNC_LOG:
|
case FUNC_PRODUCT:
|
case FUNC_FACT:
|
case FUNC_STDEVP:
|
case FUNC_VARP:
|
case FUNC_DSTDEVP:
|
case FUNC_ROUNDUP:
|
case FUNC_ROUNDDOWN:
|
case FUNC_DAYS360:
|
case FUNC_MEDIAN:
|
case FUNC_SUMPRODUCT:
|
case FUNC_SINH:
|
case FUNC_COSH:
|
case FUNC_TANH:
|
case FUNC_ASINH:
|
case FUNC_ACOSH:
|
case FUNC_ATANH:
|
case FUNC_FLOOR:
|
case FUNC_KURT:
|
case FUNC_SKEW:
|
case FUNC_POWER:
|
case FUNC_RADIANS:
|
case FUNC_DEGREES:
|
case FUNC_SUMIF:
|
case FUNC_AVERAGEA:
|
case FUNC_MAXA:
|
case FUNC_MINA:
|
case FUNC_STDEVPA:
|
case FUNC_VARPA:
|
case FUNC_STDEVA:
|
case FUNC_VARA:
|
case FUNC_SERIESSUM:
|
case FUNC_ERF:
|
case FUNC_ERFC:
|
case FUNC_WORKDAY:
|
case FUNC_NETWORKDAYS:
|
case FUNC_GCD:
|
case FUNC_SUMIFS:
|
case FUNC_AVERAGEIF:
|
case FUNC_AVERAGEIFS:
|
#if 0
|
// these should not be provided to an older excel
|
case FUNC_ERF_PRECISE:
|
case FUNC_ERFC_PRECISE:
|
case FUNC_GAMMALN_PRECISE:
|
case FUNC_CEILING_PRECISE:
|
case FUNC_FLOOR_PRECISE:
|
#endif
|
dst.SetCalcOnLoad();
|
dst.SetFloatingPoint(42.0); // faked value estimate!
|
break;
|
|
case FUNC_LOWER:
|
case FUNC_UPPER:
|
case FUNC_LEFT:
|
case FUNC_RIGHT:
|
case FUNC_TRIM:
|
case FUNC_REPLACE:
|
case FUNC_CONCATENATE:
|
case FUNC_DATESTRING:
|
case FUNC_NUMBERSTRING:
|
case FUNC_ROMAN:
|
case FUNC_HYPERLINK:
|
case FUNC_PHONETIC:
|
case FUNC_BAHTTEXT:
|
case FUNC_HEX2BIN:
|
case FUNC_HEX2DEC:
|
case FUNC_HEX2OCT:
|
case FUNC_DEC2BIN:
|
case FUNC_DEC2HEX:
|
case FUNC_DEC2OCT:
|
case FUNC_OCT2BIN:
|
case FUNC_OCT2HEX:
|
case FUNC_OCT2DEC:
|
case FUNC_BIN2DEC:
|
case FUNC_BIN2OCT:
|
case FUNC_BIN2HEX:
|
dst.SetCalcOnLoad();
|
dst.SetText("???"); // faked value estimate!
|
break;
|
|
case FUNC_NOW:
|
case FUNC_TODAY:
|
case FUNC_RAND:
|
case FUNC_VOLATILE:
|
case FUNC_RANDBETWEEN:
|
dst.SetCalcAlways();
|
dst.SetCalcOnLoad();
|
dst.SetFloatingPoint(0.5); // faked random value estimate!
|
break;
|
|
case FUNC_OFFSET:
|
case FUNC_INDIRECT:
|
dst.SetCalcAlways();
|
dst.SetCalcOnLoad();
|
dst.SetInteger(42); // faked value estimate!
|
break;
|
|
case FUNC_CELL:
|
dst.SetCalcAlways();
|
dst.SetCalcOnLoad();
|
dst.SetText("???"); // faked value estimate!
|
break;
|
}
|
}
|
|
size_t function_basenode_t::GetSize(bool include_subtree) const
|
{
|
size_t len = 1+2; // OP_FUNC
|
unsigned32_t argcntmask = NumberOfArgsForExcelFunction(func);
|
size_t chcnt = GetNumberOfChilds();
|
|
// XL_ASSERT(argcntmask & (1U << (chcnt > 15 ? 15 : chcnt)));
|
// if the function allows more than one possible argument count
|
if (argcntmask == A_UNKNOWN || (argcntmask & ~(1U << chcnt))) {
|
len += 1; // with the Function with variable args, we push ONE additional byte
|
}
|
|
if (include_subtree) {
|
while (chcnt-- > 0) {
|
XL_ASSERT(GetChild((unsigned16_t)chcnt));
|
len += GetChild((unsigned16_t)chcnt)->GetSize(include_subtree);
|
}
|
}
|
return len;
|
}
|
|
signed8_t function_basenode_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
signed8_t errcode = NO_ERRORS;
|
unsigned32_t argcntmask = NumberOfArgsForExcelFunction(func);
|
size_t chcnt = GetNumberOfChilds();
|
|
if (include_subtree) {
|
size_t idx;
|
|
for (idx = 0; idx < chcnt; idx++) {
|
XL_ASSERT(GetChild((unsigned16_t)idx));
|
errcode |= GetChild((unsigned16_t)idx)->DumpData(stack, include_subtree);
|
}
|
}
|
|
// XL_ASSERT(argcntmask & (1U << (chcnt > 15 ? 15 : chcnt)));
|
// if the function allows more than one possible argument count
|
if (argcntmask == A_UNKNOWN || (argcntmask & ~(1U << chcnt))) {
|
errcode |= stack.PushFunction(func, chcnt, op_class);
|
} else {
|
errcode |= stack.PushFunction(func, op_class);
|
}
|
|
return errcode;
|
}
|
|
z_ary_func_node_t::z_ary_func_node_t(CGlobalRecords& gRecords, expr_function_code_t func, cell_op_class_t op_c) :
|
function_basenode_t(gRecords, func, op_c)
|
{
|
}
|
|
z_ary_func_node_t::~z_ary_func_node_t()
|
{
|
}
|
|
unary_func_node_t::unary_func_node_t(CGlobalRecords& gRecords, expr_function_code_t op, cell_op_class_t op_c, expression_node_t* a) :
|
function_basenode_t(gRecords, op, op_c),
|
arg(a)
|
{
|
}
|
|
unary_func_node_t::~unary_func_node_t()
|
{
|
}
|
|
expression_node_t* unary_func_node_t::GetChild(unsigned16_t index) const
|
{
|
(void)index; // stop warning
|
return arg;
|
}
|
|
unsigned16_t unary_func_node_t::GetNumberOfChilds(void) const
|
{
|
return 1;
|
}
|
|
binary_func_node_t::binary_func_node_t(CGlobalRecords& gRecords, expr_function_code_t op, cell_op_class_t op_c, expression_node_t* arg1, expression_node_t* arg2) :
|
function_basenode_t(gRecords, op, op_c)
|
{
|
args[0] = arg1;
|
args[1] = arg2;
|
}
|
|
binary_func_node_t::~binary_func_node_t()
|
{
|
}
|
|
expression_node_t* binary_func_node_t::GetChild(unsigned16_t index) const
|
{
|
return args[index];
|
}
|
|
unsigned16_t binary_func_node_t::GetNumberOfChilds(void) const
|
{
|
return 2;
|
}
|
|
n_ary_func_node_t::n_ary_func_node_t(CGlobalRecords& gRecords, expr_function_code_t func, cell_op_class_t op_c, size_t count, expression_node_t** arr) :
|
function_basenode_t(gRecords, func, op_c),
|
arg_arrsize((unsigned16_t)count),
|
arg_count(0),
|
arg_arr(NULL)
|
{
|
if (count > 0) {
|
//XL_ASSERT(arr);
|
arg_arr = (expression_node_t **)calloc(count, sizeof(arg_arr[0]));
|
//arg_arrsize = count;
|
|
if (arr) {
|
arg_count = (unsigned16_t)count;
|
|
while (count-- > 0) {
|
arg_arr[count] = arr[count];
|
}
|
}
|
}
|
}
|
|
n_ary_func_node_t::~n_ary_func_node_t()
|
{
|
if (arg_arr) {
|
free((void *)arg_arr);
|
}
|
}
|
|
expression_node_t* n_ary_func_node_t::GetChild(unsigned16_t index) const
|
{
|
return arg_arr[index];
|
}
|
|
unsigned16_t n_ary_func_node_t::GetNumberOfChilds(void) const
|
{
|
return arg_count;
|
}
|
|
function_basenode_t& n_ary_func_node_t::PushArg(expression_node_t* arg)
|
{
|
if (arg_arr == NULL) {
|
arg_arrsize = 2;
|
XL_ASSERT(arg_count == 0);
|
arg_arr = (expression_node_t **)calloc(2, sizeof(arg_arr[0]));
|
} else
|
if (arg_count >= arg_arrsize) {
|
while (arg_count >= arg_arrsize) {
|
arg_arrsize += 2;
|
}
|
arg_arr = (expression_node_t **)realloc((void *)arg_arr, arg_arrsize * sizeof(arg_arr[0]));
|
for (int i = arg_count; i < arg_arrsize; i++) {
|
arg_arr[i] = NULL;
|
}
|
}
|
|
arg_arr[arg_count++] = arg;
|
|
return *this;
|
}
|
|
#if 0
|
// Requires the addition of VBA (which is HUGE)
|
|
userdef_func_node_t::userdef_func_node_t(CGlobalRecords& gRecords, int udf_num, cell_op_class_t op_class, size_t count, expression_node_t** arr) :
|
n_ary_func_node_t(gRecords, FUNC_UDF, op_class, count, arr),
|
expr_user_function_code(udf_num)
|
{
|
}
|
|
userdef_func_node_t::~userdef_func_node_t()
|
{
|
}
|
|
void userdef_func_node_t::GetResultEstimate(estimated_formula_result_t &dst) const
|
{
|
/*
|
* We don't know what the heck a UDF will spit out as a result, not in value nor in type, so we
|
* take a pod shot at this and make it easy to ourselves: we 'guestimate' the UDF will return an
|
* error code and we mark the expression as 'calc on load' to mask our ignorance.
|
*/
|
dst.SetCalcOnLoad();
|
dst.SetErrorCode(XLERR_VALUE);
|
}
|
|
size_t userdef_func_node_t::GetSize(bool include_subtree) const
|
{
|
(void)include_subtree;
|
return 0;
|
}
|
|
signed8_t userdef_func_node_t::DumpData(formula_t &stack, bool include_subtree) const
|
{
|
(void)stack; // stop warnings
|
(void)include_subtree;
|
//signed8_t errcode = NO_ERRORS;
|
|
/*
|
* looks like the UDF is encoded like this:
|
*
|
* first argument is a ptgNameX record pointing at the name of the UDF, then
|
* followed by the arguments pushed onto the stack, and then at the
|
* very end the ptgFuncVarV record with function id 0x00FF (UDF) and the total number
|
* of arguments there INCLUDING that extra ptgNameX argument at the start.
|
*
|
* In other words: UDF is a single function, which does indirection through the initial
|
* argument to the actual user defined function, i.e. UDF(<referenced_user_function_name>, <arguments>...)
|
*/
|
|
return GENERAL_ERROR; // not supported yet...
|
}
|
#endif
|
|
expression_node_factory_t::expression_node_factory_t(CGlobalRecords& glbl) :
|
m_GlobalRecords(glbl)
|
{
|
}
|
|
expression_node_factory_t::~expression_node_factory_t()
|
{
|
}
|
|
expression_node_factory_t &expression_node_factory_t::operator =(const expression_node_factory_t &src)
|
{
|
(void)src; // stop warning
|
throw std::string("Should never have invoked the expression_node_factory_t copy operator!");
|
}
|
|
boolean_value_node_t *expression_node_factory_t::boolean(bool value)
|
{
|
return new boolean_value_node_t(m_GlobalRecords, value);
|
}
|
|
integer_value_node_t *expression_node_factory_t::integer(signed32_t value)
|
{
|
return new integer_value_node_t(m_GlobalRecords, value);
|
}
|
|
float_value_node_t *expression_node_factory_t::floating_point(double value)
|
{
|
return new float_value_node_t(m_GlobalRecords, value);
|
}
|
|
error_value_node_t *expression_node_factory_t::error_value(errcode_t value)
|
{
|
return new error_value_node_t(m_GlobalRecords, value);
|
}
|
|
missing_arg_node_t *expression_node_factory_t::missing_arg(void)
|
{
|
return new missing_arg_node_t(m_GlobalRecords);
|
}
|
|
text_value_node_t *expression_node_factory_t::text(const std::string& value)
|
{
|
return new text_value_node_t(m_GlobalRecords, value);
|
}
|
|
text_value_node_t *expression_node_factory_t::text(const u16string& value)
|
{
|
return new text_value_node_t(m_GlobalRecords, value);
|
}
|
|
cell_deref_node_t *expression_node_factory_t::cell(const cell_t& cellref, cell_addr_mode_t attr, cell_op_class_t opclass)
|
{
|
return new cell_deref_node_t(m_GlobalRecords, cellref, attr, opclass);
|
}
|
|
/* Ger had something in mind
|
cell_deref_node_t *expression_node_factory_t::cell(const cell_t& cellref, const worksheet* ws, cell_addr_mode_t attr, cell_op_class_t opclass)
|
{
|
return new cell_deref_node_t(m_GlobalRecords, cellref, ws, attr, opclass);
|
}
|
*/
|
cellarea_deref_node_t *expression_node_factory_t::area(const cell_t& upper_left_corner, const cell_t& lower_right_corner, cell_addr_mode_t attr,
|
cell_op_class_t opclass)
|
{
|
return new cellarea_deref_node_t(m_GlobalRecords, upper_left_corner, lower_right_corner, attr, opclass);
|
}
|
|
/* Ger had something in mind
|
cellarea_deref_node_t *expression_node_factory_t::area(const cell_t& upper_left_corner, const cell_t& lower_right_corner, const worksheet* ws,
|
cell_addr_mode_t attr, cell_op_class_t opclass)
|
{
|
return new cellarea_deref_node_t(m_GlobalRecords, upper_left_corner, lower_right_corner, ws, attr, opclass);
|
}
|
*/
|
|
unary_op_node_t *expression_node_factory_t::op(expr_operator_code_t op, expression_node_t* arg)
|
{
|
return new unary_op_node_t(m_GlobalRecords, op, arg);
|
}
|
|
binary_op_node_t *expression_node_factory_t::op(expr_operator_code_t op, expression_node_t* arg1, expression_node_t* arg2)
|
{
|
return new binary_op_node_t(m_GlobalRecords, op, arg1, arg2);
|
}
|
|
z_ary_func_node_t *expression_node_factory_t::f(expr_function_code_t func, cell_op_class_t op_class)
|
{
|
return new z_ary_func_node_t(m_GlobalRecords, func, op_class);
|
}
|
|
unary_func_node_t *expression_node_factory_t::f(expr_function_code_t func, expression_node_t* arg, cell_op_class_t op_class)
|
{
|
return new unary_func_node_t(m_GlobalRecords, func, op_class, arg);
|
}
|
|
binary_func_node_t *expression_node_factory_t::f(expr_function_code_t func, expression_node_t* arg1, expression_node_t* arg2, cell_op_class_t op_class)
|
{
|
return new binary_func_node_t(m_GlobalRecords, func, op_class, arg1, arg2);
|
}
|
|
n_ary_func_node_t *expression_node_factory_t::f(expr_function_code_t func, size_t argcount, expression_node_t** arg_arr, cell_op_class_t op_class)
|
{
|
return new n_ary_func_node_t(m_GlobalRecords, func, op_class, argcount, arg_arr);
|
}
|
|
#if 0
|
userdef_func_node_t *expression_node_factory_t::udf(int expr_user_function, size_t argcount, expression_node_t** arg_arr, cell_op_class_t op_class)
|
{
|
return new userdef_func_node_t(m_GlobalRecords, expr_user_function, op_class, argcount, arg_arr);
|
}
|
#endif
|