More Implicit intersection computation
Do implicit intersection computation for
for single parameter functions while generating
RPN token array when the argument is a doubleref
with relative row references. This optimization
is not done when under forced array mode or
matrix formula mode.
The computation logic was already present in ScInterpreter,
so factored it out and reused.
This also adds unit tests to ensure correctness of
II computation for various cases.
Change-Id: I509c3f6f811aa036b1dc3296e8f68904b26c3c49
Reviewed-on: https://gerrit.libreoffice.org/53885
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index f5e06e7..671d2c2 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 e5b0726..5abc046 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 321e288..1f0bb0d 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 ffd783a..767d08b 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 f1d6acb..03305a8 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 126bed6..5900e35 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 d8f164e..268ec1b 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 832351d27..34566ac 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 e489f60..3800397 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 a710d8a..9882fd7 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 4ec04d9..d8a68b5 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;
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)
{
aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
aSumRange.Ref2 = aSumRange.Ref1;
}
else
aSumRange = *(*pppToken[2])->GetDoubleRef();
CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
aSumRange.Ref2 = aSumRange.Ref1;
}
break;
default:
;
else
aSumRange = *(*pppToken[2])->GetDoubleRef();
CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
}
else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
{
if (nNumParams != 1)
return;
if (eClass == formula::ForceArray || mbMatrixFlag)
return;
if ((*pppToken[0])->GetType() != svDoubleRef)
return;
ReplaceDoubleRefII(pppToken[0]);
}
}
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;
ScRange aAbsRange = rRange.toAbs(aPos);
if (aAbsRange.aStart == aAbsRange.aEnd)
return; // Nothing to do (trivial case).
ScAddress aAddr;
if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos))
return;
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;
}
}
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 c600cdc..6194fa5 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 87529dc..9f7977b 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 306d020..9e29d72 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] == '-' )