summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx24
-rw-r--r--formula/source/ui/dlg/formula.cxx2
-rw-r--r--include/formula/FormulaCompiler.hxx16
-rw-r--r--sc/inc/compiler.hxx16
-rw-r--r--sc/qa/unit/ucalc.hxx10
-rw-r--r--sc/qa/unit/ucalc_formula.cxx197
-rw-r--r--sc/source/core/data/column.cxx3
-rw-r--r--sc/source/core/data/column4.cxx3
-rw-r--r--sc/source/core/data/formulacell.cxx60
-rw-r--r--sc/source/core/data/simpleformulacalc.cxx2
-rw-r--r--sc/source/core/tool/compiler.cxx178
-rw-r--r--sc/source/core/tool/interpr4.cxx53
-rw-r--r--sc/source/filter/oox/formulabuffer.cxx4
-rw-r--r--sc/source/ui/view/viewfunc.cxx2
14 files changed, 449 insertions, 121 deletions
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index f5e06e77deba..671d2c2b9f62 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -697,7 +697,7 @@ void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode
// class FormulaCompiler
-FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr )
+FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
:
nCurrentFactorParam(0),
pArr( &rArr ),
@@ -714,13 +714,15 @@ FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr )
glSubTotal( false ),
needsRPNTokenCheck( false ),
mbJumpCommandReorder(true),
- mbStopOnError(true)
+ mbStopOnError(true),
+ mbComputeII(bComputeII),
+ mbMatrixFlag(bMatrixFlag)
{
}
FormulaTokenArray FormulaCompiler::smDummyTokenArray;
-FormulaCompiler::FormulaCompiler()
+FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
:
nCurrentFactorParam(0),
pArr( nullptr ),
@@ -737,7 +739,9 @@ FormulaCompiler::FormulaCompiler()
glSubTotal( false ),
needsRPNTokenCheck( false ),
mbJumpCommandReorder(true),
- mbStopOnError(true)
+ mbStopOnError(true),
+ mbComputeII(bComputeII),
+ mbMatrixFlag(bMatrixFlag)
{
}
@@ -1549,6 +1553,7 @@ void FormulaCompiler::Factor()
else
{
// standard handling of 1-parameter opcodes
+ OpCode eMyLastOp = eOp;
pFacToken = mpToken;
eOp = NextToken();
if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
@@ -1564,7 +1569,14 @@ void FormulaCompiler::Factor()
if (eOp != ocClose)
SetError( FormulaError::PairExpected);
else if ( pArr->GetCodeError() == FormulaError::NONE )
+ {
pFacToken->SetByte( 1 );
+ if (mbComputeII && IsIIOpCode(eMyLastOp))
+ {
+ FormulaToken** pArg = pCode - 1;
+ HandleIIOpCode(eMyLastOp, pFacToken->GetInForceArray(), &pArg, 1);
+ }
+ }
PutCode( pFacToken );
NextToken();
}
@@ -1606,7 +1618,7 @@ void FormulaCompiler::Factor()
sal_uInt32 nSepCount = 0;
if( !bNoParam )
{
- bool bDoIICompute = IsIIOpCode(eMyLastOp);
+ bool bDoIICompute = mbComputeII && IsIIOpCode(eMyLastOp);
// Array of FormulaToken double pointers to collect the parameters of II opcodes.
FormulaToken*** pArgArray = nullptr;
if (bDoIICompute)
@@ -1633,7 +1645,7 @@ void FormulaCompiler::Factor()
pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
}
if (bDoIICompute)
- HandleIIOpCode(eMyLastOp, pArgArray,
+ HandleIIOpCode(eMyLastOp, pFacToken->GetInForceArray(), pArgArray,
std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
}
if (bBadName)
diff --git a/formula/source/ui/dlg/formula.cxx b/formula/source/ui/dlg/formula.cxx
index e5b07260915a..5abc04622e15 100644
--- a/formula/source/ui/dlg/formula.cxx
+++ b/formula/source/ui/dlg/formula.cxx
@@ -831,6 +831,8 @@ void FormulaDlg_Impl::UpdateTokenArray( const OUString& rStrExp)
// #i101512# Disable special handling of jump commands.
pCompiler->EnableJumpCommandReorder(false);
pCompiler->EnableStopOnError(false);
+ pCompiler->SetComputeIIFlag(true);
+ pCompiler->SetMatrixFlag(m_bUserMatrixFlag);
pCompiler->CompileTokenArray();
}
diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx
index 321e288fcc55..1f0bb0d95c5b 100644
--- a/include/formula/FormulaCompiler.hxx
+++ b/include/formula/FormulaCompiler.hxx
@@ -76,8 +76,8 @@ private:
FormulaCompiler(const FormulaCompiler&) = delete;
FormulaCompiler& operator=(const FormulaCompiler&) = delete;
public:
- FormulaCompiler();
- FormulaCompiler(FormulaTokenArray& _rArr);
+ FormulaCompiler(bool bComputeII = false, bool bMatrixFlag = false);
+ FormulaCompiler(FormulaTokenArray& _rArr, bool bComputeII = false, bool bMatrixFlag = false);
virtual ~FormulaCompiler();
/** Mappings from strings to OpCodes and vice versa. */
@@ -269,6 +269,12 @@ public:
static void ResetNativeSymbols();
static void SetNativeSymbols( const OpCodeMapPtr& xMap );
+ /** Sets the implicit intersection compute flag */
+ void SetComputeIIFlag(bool bSet) { mbComputeII = bSet; }
+
+ /** Sets the matrix flag for the formula*/
+ void SetMatrixFlag(bool bSet) { mbMatrixFlag = bSet; }
+
/** Separators mapped when loading opcodes from the resource, values other
than RESOURCE_BASE may override the resource strings. Used by OpCodeList
implementation via loadSymbols().
@@ -327,7 +333,8 @@ protected:
// This is no-op for this class.
virtual bool IsIIOpCode(OpCode /*nOpCode*/) const { return false; }
// Handles II opcode and passes the parameter array and number of parameters.
- virtual void HandleIIOpCode(OpCode /*nOpCode*/, FormulaToken*** /*pppToken*/, sal_uInt8 /*nNumParams*/) {}
+ virtual void HandleIIOpCode(OpCode /*nOpCode*/, formula::ParamClass /*eClass*/,
+ FormulaToken*** /*pppToken*/, sal_uInt8 /*nNumParams*/) {}
OUString aCorrectedFormula; // autocorrected Formula
OUString aCorrectedSymbol; // autocorrected Symbol
@@ -359,6 +366,9 @@ protected:
bool mbJumpCommandReorder; /// Whether or not to reorder RPN for jump commands.
bool mbStopOnError; /// Whether to stop compilation on first encountered error.
+ bool mbComputeII; // whether to attempt computing implicit intersection ranges while building the RPN array.
+ bool mbMatrixFlag; // whether the formula is a matrix formula (needed for II computation)
+
private:
void InitSymbolsNative() const; /// only SymbolsNative, on first document creation
void InitSymbolsEnglish() const; /// only SymbolsEnglish, maybe later
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index ffd783a32506..767d08b36e62 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -335,24 +335,25 @@ private:
static void InitCharClassEnglish();
public:
- ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, const ScInterpreterContext* pContext = nullptr );
+ ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
+ bool bComputeII = false, bool bMatrixFlag = false, const ScInterpreterContext* pContext = nullptr );
/** If eGrammar == GRAM_UNSPECIFIED then the grammar of pDocument is used,
if pDocument==nullptr then GRAM_DEFAULT.
*/
ScCompiler( ScDocument* pDocument, const ScAddress&,
formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED,
- const ScInterpreterContext* pContext = nullptr );
+ bool bComputeII = false, bool bMatrixFlag = false, const ScInterpreterContext* pContext = nullptr );
ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr,
- const ScInterpreterContext* pContext = nullptr );
+ bool bComputeII = false, bool bMatrixFlag = false, const ScInterpreterContext* pContext = nullptr );
/** If eGrammar == GRAM_UNSPECIFIED then the grammar of pDocument is used,
if pDocument==nullptr then GRAM_DEFAULT.
*/
ScCompiler( ScDocument* pDocument, const ScAddress&, ScTokenArray& rArr,
formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED,
- const ScInterpreterContext* pContext = nullptr );
+ bool bComputeII = false, bool bMatrixFlag = false, const ScInterpreterContext* pContext = nullptr );
virtual ~ScCompiler() override;
@@ -451,6 +452,10 @@ public:
static bool IsCharFlagAllConventions(
OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags );
+ /** TODO : Move this to somewhere appropriate. */
+ static bool DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr,
+ const ScAddress& rFormulaPos);
+
private:
// FormulaCompiler
virtual OUString FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const override;
@@ -480,7 +485,8 @@ private:
{ return c < 128 ? pConv->getCharTableFlags(c, cLast) : ScCharFlags::NONE; }
bool IsIIOpCode(OpCode nOpCode) const override;
- void HandleIIOpCode(OpCode nOpCode, formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+ void HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+ void ReplaceDoubleRefII(formula::FormulaToken** ppDoubleRefTok);
bool AdjustSumRangeShape(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange);
void CorrectSumRange(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange, formula::FormulaToken** ppSumRangeToken);
};
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index f1d6acb476eb..03305a8ad74e 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -143,7 +143,10 @@ public:
void testFormulaRefData();
void testFormulaCompiler();
void testFormulaCompilerJumpReordering();
- void testFormulaCompilerImplicitIntersection();
+ void testFormulaCompilerImplicitIntersection2Param();
+ void testFormulaCompilerImplicitIntersection1ParamNoChange();
+ void testFormulaCompilerImplicitIntersection1ParamWithChange();
+ void testFormulaCompilerImplicitIntersection1NoGroup();
void testFormulaRefUpdate();
void testFormulaRefUpdateRange();
void testFormulaRefUpdateSheets();
@@ -568,7 +571,10 @@ public:
CPPUNIT_TEST(testFormulaRefData);
CPPUNIT_TEST(testFormulaCompiler);
CPPUNIT_TEST(testFormulaCompilerJumpReordering);
- CPPUNIT_TEST(testFormulaCompilerImplicitIntersection);
+ CPPUNIT_TEST(testFormulaCompilerImplicitIntersection2Param);
+ CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamNoChange);
+ CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamWithChange);
+ CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1NoGroup);
CPPUNIT_TEST(testFormulaRefUpdate);
CPPUNIT_TEST(testFormulaRefUpdateRange);
CPPUNIT_TEST(testFormulaRefUpdateSheets);
diff --git a/sc/qa/unit/ucalc_formula.cxx b/sc/qa/unit/ucalc_formula.cxx
index 126bed6c9289..5900e3555ada 100644
--- a/sc/qa/unit/ucalc_formula.cxx
+++ b/sc/qa/unit/ucalc_formula.cxx
@@ -1105,7 +1105,7 @@ void Test::testFormulaCompilerJumpReordering()
}
}
-void Test::testFormulaCompilerImplicitIntersection()
+void Test::testFormulaCompilerImplicitIntersection2Param()
{
struct TestCaseFormula
{
@@ -1231,6 +1231,201 @@ void Test::testFormulaCompilerImplicitIntersection()
}
}
+void Test::testFormulaCompilerImplicitIntersection1ParamNoChange()
+{
+ struct TestCaseFormulaNoChange
+ {
+ OUString aFormula;
+ ScAddress aCellAddress;
+ bool bMatrixFormula;
+ bool bForcedArray;
+ };
+
+ m_pDoc->InsertTab(0, "Formula");
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
+
+ {
+ ScAddress aStartAddr(4, 5, 0);
+ TestCaseFormulaNoChange aCasesNoChange[] =
+ {
+ {
+ OUString("=COS(A$2:A$100)"), // No change because of abs col ref.
+ aStartAddr,
+ false,
+ false
+ },
+ {
+ OUString("=COS($A7:$A100)"), // No intersection
+ aStartAddr,
+ false,
+ false
+ },
+ {
+ OUString("=COS($A5:$C7)"), // No intersection 2-D range
+ aStartAddr,
+ false,
+ false
+ },
+ {
+ OUString("=SUMPRODUCT(COS(A6:A10))"), // COS() in forced array mode
+ aStartAddr,
+ false,
+ true
+ },
+ {
+ OUString("=COS(A6:A10)"), // Matrix formula
+ aStartAddr,
+ true,
+ false
+ }
+ };
+
+ for (auto& rCase : aCasesNoChange)
+ {
+ if (rCase.bMatrixFormula)
+ {
+ ScMarkData aMark;
+ aMark.SelectOneTable(0);
+ SCCOL nColStart = rCase.aCellAddress.Col();
+ SCROW nRowStart = rCase.aCellAddress.Row();
+ m_pDoc->InsertMatrixFormula(nColStart, nRowStart, nColStart, nRowStart + 4,
+ aMark, rCase.aFormula);
+ }
+ else
+ m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
+
+ const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
+ const ScTokenArray* pCode = pCell->GetCode();
+ CPPUNIT_ASSERT(pCode);
+
+ sal_uInt16 nRPNLen = pCode->GetCodeLen();
+ sal_uInt16 nRawLen = pCode->GetLen();
+ sal_uInt16 nRawArgPos;
+ if (rCase.bForcedArray)
+ {
+ nRawArgPos = 4;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(7), nRawLen);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(3), nRPNLen);
+ }
+ else
+ {
+ nRawArgPos = 2;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(4), nRawLen);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
+ }
+
+ FormulaToken** ppRawTokens = pCode->GetArray();
+ FormulaToken** ppRPNTokens = pCode->GetCode();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef, ppRawTokens[nRawArgPos]->GetType());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef, ppRPNTokens[0]->GetType());
+
+ ScComplexRefData aArgRangeRaw = *ppRawTokens[nRawArgPos]->GetDoubleRef();
+ ScComplexRefData aArgRangeRPN = *ppRPNTokens[0]->GetDoubleRef();
+ bool bRawMatchRPNToken(aArgRangeRaw == aArgRangeRPN);
+ CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken);
+ }
+ }
+}
+
+void Test::testFormulaCompilerImplicitIntersection1ParamWithChange()
+{
+ struct TestCaseFormula
+ {
+ OUString aFormula;
+ ScAddress aCellAddress;
+ ScAddress aArgAddr;
+ };
+
+ m_pDoc->InsertTab(0, "Formula");
+ m_pDoc->InsertTab(1, "Formula1");
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
+
+ {
+ ScAddress aStartAddr(10, 5, 0);
+ TestCaseFormula aCasesWithChange[] =
+ {
+ {
+ OUString("=COS($A6:$A100)"), // Corner case with intersection
+ aStartAddr,
+ ScAddress(0, 5, 0)
+ },
+ {
+ OUString("=COS($A2:$A6)"), // Corner case with intersection
+ aStartAddr,
+ ScAddress(0, 5, 0)
+ },
+ {
+ OUString("=COS($A2:$A100)"), // Typical 1D case
+ aStartAddr,
+ ScAddress(0, 5, 0)
+ },
+ {
+ OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
+ ScAddress(0, 0, 1), // Formula in sheet 1
+ ScAddress(0, 0, 0)
+ },
+ {
+ OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
+ ScAddress(0, 2, 1), // Formula in sheet 1
+ ScAddress(0, 2, 0)
+ },
+ {
+ OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
+ ScAddress(2, 0, 1), // Formula in sheet 1
+ ScAddress(2, 0, 0)
+ },
+ {
+ OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
+ ScAddress(2, 2, 1), // Formula in sheet 1
+ ScAddress(2, 2, 0)
+ },
+ {
+ OUString("=COS($Formula.$A1:$C3)"), // Typical 2D case
+ ScAddress(1, 1, 1), // Formula in sheet 1
+ ScAddress(1, 1, 0)
+ }
+ };
+
+ for (auto& rCase : aCasesWithChange)
+ {
+ m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
+
+ const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
+ const ScTokenArray* pCode = pCell->GetCode();
+ CPPUNIT_ASSERT(pCode);
+
+ sal_uInt16 nRPNLen = pCode->GetCodeLen();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
+
+ FormulaToken** ppRPNTokens = pCode->GetCode();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
+
+ ScSingleRefData aArgAddrRPN = *ppRPNTokens[0]->GetSingleRef();
+ ScAddress aArgAddrActual = aArgAddrRPN.toAbs(rCase.aCellAddress);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase.aArgAddr, aArgAddrActual);
+ }
+ }
+}
+
+void Test::testFormulaCompilerImplicitIntersection1NoGroup()
+{
+ m_pDoc->InsertTab(0, "Formula");
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
+
+ m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
+ m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
+
+ // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
+ // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
+ // otherwise B4's formula would then be "=COS(A2:A6)".
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "COS(A1:A5)", "Formula in B3 has changed.");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,3,0), "COS(A1:A5)", "Formula in B4 has changed.");
+
+ m_pDoc->DeleteTab(0);
+}
+
void Test::testFormulaRefUpdate()
{
m_pDoc->InsertTab(0, "Formula");
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index d8f164e0c916..268ec1b7793c 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -2112,7 +2112,8 @@ class UpdateRefOnNonCopy
// We need to re-compile the token array when a range name is
// modified, to correctly reflect the new references in the
// name.
- ScCompiler aComp(&mpCxt->mrDoc, rTopCell.aPos, *rTopCell.GetCode(), mpCxt->mrDoc.GetGrammar());
+ ScCompiler aComp(&mpCxt->mrDoc, rTopCell.aPos, *rTopCell.GetCode(), mpCxt->mrDoc.GetGrammar(),
+ true, rTopCell.GetMatrixFlag() != ScMatrixMode::NONE);
aComp.CompileTokenArray();
}
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 832351d275a7..34566ac15bb1 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -928,7 +928,8 @@ public:
ScTokenArray* pNewCode = aComp.CompileString(aFormula);
// Generate RPN tokens.
- ScCompiler aComp2(mpDoc, pCell->aPos, *pNewCode);
+ ScCompiler aComp2(mpDoc, pCell->aPos, *pNewCode, formula::FormulaGrammar::GRAM_UNSPECIFIED,
+ true, pCell->GetMatrixFlag() != ScMatrixMode::NONE);
aComp2.CompileTokenArray();
pCell->SetCode(pNewCode);
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index e489f60936b7..3800397a9ca7 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -562,7 +562,8 @@ void ScFormulaCellGroup::compileCode(
if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
{
- ScCompiler aComp(&rDoc, rPos, *mpCode, eGram);
+ bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
+ ScCompiler aComp(&rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
mbSubTotal = aComp.CompileTokenArray();
mnFormatType = aComp.GetNumFormatType();
}
@@ -700,7 +701,7 @@ ScFormulaCell::ScFormulaCell(
// Generate RPN token array.
if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
{
- ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar);
+ ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
bSubTotal = aComp.CompileTokenArray();
nFormatType = aComp.GetNumFormatType();
}
@@ -747,7 +748,7 @@ ScFormulaCell::ScFormulaCell(
// RPN array generation
if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
{
- ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar);
+ ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
bSubTotal = aComp.CompileTokenArray();
nFormatType = aComp.GetNumFormatType();
}
@@ -993,7 +994,7 @@ void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
}
else
{
- ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, pContext );
+ ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
aComp.CreateStringFromTokenArray( rBuffer );
}
}
@@ -1004,7 +1005,7 @@ void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
}
else
{
- ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, pContext );
+ ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
aComp.CreateStringFromTokenArray( rBuffer );
}
@@ -1031,7 +1032,7 @@ OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, const ScInt
{
ScTokenArray aCode;
aCode.AddToken( FormulaErrorToken( pCode->GetCodeError()));
- ScCompiler aComp(rCxt, aPos, aCode, pContext);
+ ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
aComp.CreateStringFromTokenArray(aBuf);
return aBuf.makeStringAndClear();
}
@@ -1058,7 +1059,7 @@ OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, const ScInt
}
else
{
- ScCompiler aComp(rCxt, aPos, *pCode, pContext);
+ ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
aComp.CreateStringFromTokenArray(aBuf);
}
}
@@ -1069,7 +1070,7 @@ OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, const ScInt
}
else
{
- ScCompiler aComp(rCxt, aPos, *pCode, pContext);
+ ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
aComp.CreateStringFromTokenArray(aBuf);
}
@@ -1199,7 +1200,7 @@ void ScFormulaCell::CompileTokenArray( bool bNoListening )
if( !bNoListening && pCode->GetCodeLen() )
EndListeningTo( pDocument );
- ScCompiler aComp(pDocument, aPos, *pCode, pDocument->GetGrammar());
+ ScCompiler aComp(pDocument, aPos, *pCode, pDocument->GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
bSubTotal = aComp.CompileTokenArray();
if( pCode->GetCodeError() == FormulaError::NONE )
{
@@ -1239,7 +1240,7 @@ void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNo
if( !bNoListening && pCode->GetCodeLen() )
EndListeningTo( pDocument );
- ScCompiler aComp(rCxt, aPos, *pCode);
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
bSubTotal = aComp.CompileTokenArray();
if( pCode->GetCodeError() == FormulaError::NONE )
{
@@ -1277,7 +1278,7 @@ void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rPr
if (bWasInFormulaTree)
pDocument->RemoveFromFormulaTree( this);
rCxt.setGrammar(eTempGrammar);
- ScCompiler aComp(rCxt, aPos, *pCode);
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
OUString aFormula, aFormulaNmsp;
aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
@@ -1395,7 +1396,7 @@ void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartL
// The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
{
- ScCompiler aComp(rCxt, aPos, *pCode);
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
bSubTotal = aComp.CompileTokenArray();
nFormatType = aComp.GetNumFormatType();
bDirty = true;
@@ -4110,6 +4111,39 @@ ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( const ScFormulaC
switch (pThisTok->GetType())
{
+ // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
+ // resulting in identical RPN references that could lead to creating
+ // a formula group from formulas that should not be merged into a group,
+ // so check also the formula itself.
+ case formula::svSingleRef:
+ {
+ // Single cell reference.
+ const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
+ if (rRef != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef.IsRowRel())
+ bInvariant = false;
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ // Range reference.
+ const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
+ const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
+ if (rRef1 != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef2 != *pOtherTok->GetSingleRef2())
+ return NotEqual;
+
+ if (rRef1.IsRowRel())
+ bInvariant = false;
+
+ if (rRef2.IsRowRel())
+ bInvariant = false;
+ }
+ break;
// All index tokens are names. Different categories already had
// different OpCode values.
case formula::svIndex:
@@ -4802,7 +4836,7 @@ bool ScFormulaCell::InterpretInvariantFormulaGroup()
}
}
- ScCompiler aComp(pDocument, aPos, aCode, pDocument->GetGrammar());
+ ScCompiler aComp(pDocument, aPos, aCode, pDocument->GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
aComp.CompileTokenArray(); // Create RPN token array.
ScInterpreter aInterpreter(this, pDocument, pDocument->GetNonThreadedContext(), aPos, aCode);
aInterpreter.Interpret();
diff --git a/sc/source/core/data/simpleformulacalc.cxx b/sc/source/core/data/simpleformulacalc.cxx
index a710d8a34cbf..9882fd7a352f 100644
--- a/sc/source/core/data/simpleformulacalc.cxx
+++ b/sc/source/core/data/simpleformulacalc.cxx
@@ -28,7 +28,7 @@ ScSimpleFormulaCalculator::ScSimpleFormulaCalculator( ScDocument* pDoc, const Sc
, mbMatrixFormula(bMatrixFormula)
{
// compile already here
- ScCompiler aComp(mpDoc, maAddr, eGram);
+ ScCompiler aComp(mpDoc, maAddr, eGram, true, bMatrixFormula);
mpCode.reset(aComp.CompileString(rFormula));
if(mpCode->GetCodeError() == FormulaError::NONE && mpCode->GetLen())
aComp.CompileTokenArray();
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 4ec04d968042..d8a68b50fb43 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -1733,8 +1733,8 @@ struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
};
ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr,
- const ScInterpreterContext* pContext )
- : FormulaCompiler(rArr),
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
pDoc(rCxt.getDoc()),
aPos(rPos),
mpFormatter(pContext? pContext->GetFormatTable() : pDoc->GetFormatTable()),
@@ -1753,8 +1753,9 @@ ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
}
ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos, ScTokenArray& rArr,
- formula::FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext )
- : FormulaCompiler(rArr),
+ formula::FormulaGrammar::Grammar eGrammar,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
pDoc( pDocument ),
aPos( rPos ),
mpFormatter(pContext ? pContext->GetFormatTable() : pDoc->GetFormatTable()),
@@ -1774,8 +1775,10 @@ ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos, ScTokenArr
eGrammar );
}
-ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, const ScInterpreterContext* pContext )
- : pDoc(rCxt.getDoc()),
+ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(bComputeII, bMatrixFlag),
+ pDoc(rCxt.getDoc()),
aPos(rPos),
mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr),
mnCurrentSheetTab(-1),
@@ -1793,8 +1796,9 @@ ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
}
ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,
- formula::FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext )
- :
+ formula::FormulaGrammar::Grammar eGrammar,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(bComputeII, bMatrixFlag),
pDoc( pDocument ),
aPos( rPos ),
mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr),
@@ -5791,50 +5795,154 @@ formula::ParamClass ScCompiler::GetForceArrayParameter( const formula::FormulaTo
bool ScCompiler::IsIIOpCode(OpCode nOpCode) const
{
- if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
+ if (nOpCode == ocSumIf || nOpCode == ocAverageIf || (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR))
return true;
return false;
}
-void ScCompiler::HandleIIOpCode(OpCode nOpCode, FormulaToken*** pppToken, sal_uInt8 nNumParams)
+void ScCompiler::HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, FormulaToken*** pppToken, sal_uInt8 nNumParams)
{
- switch (nOpCode)
+ if (!mbComputeII)
+ return;
+
+ if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
{
- case ocSumIf:
- case ocAverageIf:
+ if (nNumParams != 3)
+ return;
+
+ if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
+ return;
+
+ if ((*pppToken[0])->GetType() != svDoubleRef)
+ return;
+
+ const StackVar eSumRangeType = (*pppToken[2])->GetType();
+
+ if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
+ return;
+
+ const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
+
+ ScComplexRefData aSumRange;
+ if (eSumRangeType == svSingleRef)
{
- if (nNumParams != 3)
- return;
+ aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
+ aSumRange.Ref2 = aSumRange.Ref1;
+ }
+ else
+ aSumRange = *(*pppToken[2])->GetDoubleRef();
- if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
- return;
+ CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
+ }
+ else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
+ {
+ if (nNumParams != 1)
+ return;
- if ((*pppToken[0])->GetType() != svDoubleRef)
- return;
+ if (eClass == formula::ForceArray || mbMatrixFlag)
+ return;
- const StackVar eSumRangeType = (*pppToken[2])->GetType();
+ if ((*pppToken[0])->GetType() != svDoubleRef)
+ return;
- if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
- return;
+ ReplaceDoubleRefII(pppToken[0]);
+ }
+}
- const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
+void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok)
+{
+ const ScComplexRefData& rRange = *(*ppDoubleRefTok)->GetDoubleRef();
+
+ // Can't do optimization reliably in this case (when row references are absolute).
+ // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
+ // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
+ // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
+ // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
+ // fix this, but that is unknown at this stage, so skip such cases.
+ if (!rRange.Ref1.IsRowRel() && !rRange.Ref2.IsRowRel())
+ return;
- ScComplexRefData aSumRange;
- if (eSumRangeType == svSingleRef)
- {
- aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
- aSumRange.Ref2 = aSumRange.Ref1;
- }
- else
- aSumRange = *(*pppToken[2])->GetDoubleRef();
+ ScRange aAbsRange = rRange.toAbs(aPos);
+ if (aAbsRange.aStart == aAbsRange.aEnd)
+ return; // Nothing to do (trivial case).
+
+ ScAddress aAddr;
+
+ if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos))
+ return;
- CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
+ ScSingleRefData aSingleRef;
+ aSingleRef.InitFlags();
+ aSingleRef.SetColRel(rRange.Ref1.IsColRel());
+ aSingleRef.SetRowRel(true);
+ aSingleRef.SetTabRel(rRange.Ref1.IsTabRel());
+ aSingleRef.SetAddress(aAddr, aPos);
+
+ // Replace the original doubleref token with computed singleref token
+ FormulaToken* pNewSingleRefTok = new ScSingleRefToken(aSingleRef);
+ (*ppDoubleRefTok)->DecRef();
+ *ppDoubleRefTok = pNewSingleRefTok;
+ pNewSingleRefTok->IncRef();
+}
+
+bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr, const ScAddress& rFormulaPos)
+{
+ assert(rRange.aStart != rRange.aEnd);
+
+ bool bOk = false;
+ SCCOL nMyCol = rFormulaPos.Col();
+ SCROW nMyRow = rFormulaPos.Row();
+ SCTAB nMyTab = rFormulaPos.Tab();
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ SCTAB nTab;
+ nTab = rRange.aStart.Tab();
+ if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
+ {
+ nRow = rRange.aStart.Row();
+ if ( nRow == rRange.aEnd.Row() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ }
+ else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
+ && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ nRow = nMyRow;
+ }
+ }
+ else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
+ {
+ nCol = rRange.aStart.Col();
+ if ( nCol == rRange.aEnd.Col() )
+ {
+ bOk = true;
+ nRow = nMyRow;
+ }
+ else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
+ && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ nRow = nMyRow;
}
- break;
- default:
- ;
}
+ if ( bOk )
+ {
+ if ( nTab == rRange.aEnd.Tab() )
+ ; // all done
+ else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
+ nTab = nMyTab;
+ else
+ bOk = false;
+ if ( bOk )
+ rAdr.Set( nCol, nRow, nTab );
+ }
+
+ return bOk;
}
static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta)
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index c600cdca34b1..6194fa5abe02 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -65,6 +65,7 @@
#include <doubleref.hxx>
#include <queryparam.hxx>
#include <tokenarray.hxx>
+#include <compiler.hxx>
#include <math.h>
#include <float.h>
@@ -2004,56 +2005,8 @@ bool ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& r
return bOk;
}
- SCCOL nMyCol = aPos.Col();
- SCROW nMyRow = aPos.Row();
- SCTAB nMyTab = aPos.Tab();
- SCCOL nCol = 0;
- SCROW nRow = 0;
- SCTAB nTab;
- nTab = rRange.aStart.Tab();
- if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
- {
- nRow = rRange.aStart.Row();
- if ( nRow == rRange.aEnd.Row() )
- {
- bOk = true;
- nCol = nMyCol;
- }
- else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
- && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
- {
- bOk = true;
- nCol = nMyCol;
- nRow = nMyRow;
- }
- }
- else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
- {
- nCol = rRange.aStart.Col();
- if ( nCol == rRange.aEnd.Col() )
- {
- bOk = true;
- nRow = nMyRow;
- }
- else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
- && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
- {
- bOk = true;
- nCol = nMyCol;
- nRow = nMyRow;
- }
- }
- if ( bOk )
- {
- if ( nTab == rRange.aEnd.Tab() )
- ; // all done
- else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
- nTab = nMyTab;
- else
- bOk = false;
- if ( bOk )
- rAdr.Set( nCol, nRow, nTab );
- }
+ bOk = ScCompiler::DoubleRefToPosSingleRefScalarCase(rRange, rAdr, aPos);
+
if ( !bOk )
SetError( FormulaError::NoValue );
return bOk;
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
index 87529dcad0d0..9f7977b5ddb2 100644
--- a/sc/source/filter/oox/formulabuffer.cxx
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -121,7 +121,7 @@ void applySharedFormulas(
sal_Int32 nId = rEntry.mnSharedId;
const OUString& rTokenStr = rEntry.maTokenStr;
- ScCompiler aComp(&rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML);
+ ScCompiler aComp(&rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
aComp.SetNumberFormatter(&rFormatter);
ScTokenArray* pArray = aComp.CompileString(rTokenStr);
if (pArray)
@@ -206,7 +206,7 @@ void applyCellFormulas(
continue;
}
- ScCompiler aCompiler(&rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML);
+ ScCompiler aCompiler(&rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
aCompiler.SetNumberFormatter(&rFormatter);
aCompiler.SetExternalLinks(rExternalLinks);
ScTokenArray* pCode = aCompiler.CompileString(rItem.maTokenStr);
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index 306d020ec947..9e29d72da734 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -400,7 +400,7 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
{ // formula, compile with autoCorrection
i = rMark.GetFirstSelected();
ScAddress aPos( nCol, nRow, i );
- ScCompiler aComp( pDoc, aPos, pDoc->GetGrammar());
+ ScCompiler aComp( pDoc, aPos, pDoc->GetGrammar(), true, false );
//2do: enable/disable autoCorrection via calcoptions
aComp.SetAutoCorrection( true );
if ( rString[0] == '+' || rString[0] == '-' )