Related: tdf#137543 - Add new LET function to Calc
Fix parsing name strings in Let function, so the invalid names
will be parsed as an ocBad - svString like before.
follow-up commit: 521a56d8d1e12b7471fda6b62b21d51776c9fbaf
Change-Id: If4645584500ffd85556695b12fa7c99eaa8f7662
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168503
Tested-by: Jenkins
Reviewed-by: Balazs Varga <balazs.varga.extern@allotropia.de>
diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx
index d70dcb4..11a49f4 100644
--- a/include/formula/FormulaCompiler.hxx
+++ b/include/formula/FormulaCompiler.hxx
@@ -424,7 +424,10 @@ protected:
{
bool bInLambdaFunction = false;
short nBracketPos = 0;
} mLambda;
short nParaPos = 0;
short nParaCount = 3; // minimum required parameter count: 3
std::unordered_set<OUString> aNameSet;
} m_aLambda;
public:
enum InitSymbols
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index e95e5ee..ba53dbb 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -331,6 +331,7 @@ private:
bool NextNewToken(bool bInArray);
bool ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rOrg ) const;
short GetPossibleParaCount( const std::u16string_view& rLambdaFormula ) const;
virtual void SetError(FormulaError nError) override;
@@ -359,7 +360,7 @@ private:
bool ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef );
bool ParseMacro( const OUString& );
bool ParseNamedRange( const OUString&, bool onlyCheck = false );
bool ParseLambdaFuncName( const OUString&, bool bLambdaFunction = false );
bool ParseLambdaFuncName( const OUString& );
bool ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange );
bool ParseDBRange( const OUString& );
bool ParseColRowName( const OUString& );
diff --git a/sc/qa/unit/data/xlsx/tdf137543.xlsx b/sc/qa/unit/data/xlsx/tdf137543.xlsx
index 16801b2..2a08547 100644
--- a/sc/qa/unit/data/xlsx/tdf137543.xlsx
+++ b/sc/qa/unit/data/xlsx/tdf137543.xlsx
Binary files differ
diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx
index 1ae4d22..81ad285 100644
--- a/sc/qa/unit/subsequent_export_test2.cxx
+++ b/sc/qa/unit/subsequent_export_test2.cxx
@@ -1323,6 +1323,11 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf137543XLSX)
assertXPathContent(
pSheet, "/x:worksheet/x:sheetData/x:row/x:c/x:f"_ostr,
u"_xlfn.LET(_xlpm.first,15,_xlpm.second,10,SUM(_xlpm.first,_xlpm.second))"_ustr);
// test with an unknown (for Calc) function inside the LET function
assertXPathContent(
pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[5]/x:f"_ostr,
u"_xlfn.LET(_xlpm.first,B5:E15,_xlfn.chooserows(_xlpm.first, 1, 3, 5, 7, 9, 11))"_ustr);
}
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 98a91f1..738ed67 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -3653,13 +3653,22 @@ bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck )
return false;
}
bool ScCompiler::ParseLambdaFuncName( const OUString& aOrg, bool bLambdaFunction )
bool ScCompiler::ParseLambdaFuncName( const OUString& aOrg )
{
if (bLambdaFunction && !aOrg.isEmpty())
if (m_aLambda.bInLambdaFunction && !aOrg.isEmpty())
{
OUString aName = aOrg;
if (aOrg.startsWithIgnoreAsciiCase(u"_xlpm."))
aName = aName.copy(6);
if (m_aLambda.nParaPos % 2 == 1 && m_aLambda.nParaCount > m_aLambda.nParaPos)
m_aLambda.aNameSet.insert(aName);
else
{
// should already exist the name
if (m_aLambda.aNameSet.find(aName) == m_aLambda.aNameSet.end())
return false;
}
svl::SharedString aSS = rDoc.GetSharedStringPool().intern(aName);
maRawToken.SetStringName(aSS.getData(), aSS.getDataIgnoreCase());
return true;
@@ -4362,6 +4371,36 @@ bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rO
}
}
short ScCompiler::GetPossibleParaCount( const std::u16string_view& rLambdaFormula ) const
{
sal_Unicode cSep = mxSymbols->getSymbolChar(ocSep);
sal_Unicode cOpen = mxSymbols->getSymbolChar(ocOpen);
sal_Unicode cClose = mxSymbols->getSymbolChar(ocClose);
sal_Unicode cArrayOpen = mxSymbols->getSymbolChar(ocArrayOpen);
sal_Unicode cArrayClose = mxSymbols->getSymbolChar(ocArrayClose);
short nBrackets = 0;
short nCount = std::count_if(rLambdaFormula.begin(), rLambdaFormula.end(),
[&](sal_Unicode c) {
if (c == cOpen || c == cArrayOpen || c == '[') {
nBrackets++;
return false;
}
else if (c == cClose || c == cArrayClose || c == ']') {
nBrackets--;
return false;
}
else {
if (nBrackets == 1)
return c == cSep;
else
return false;
}
});
return static_cast<short>(nCount + 1);
}
bool ScCompiler::NextNewToken( bool bInArray )
{
if (!maPendingOpCodes.empty())
@@ -4629,7 +4668,7 @@ Label_Rewind:
if (bMayBeFuncName && ParseOpCode2( aUpper ))
return true;
if (ParseLambdaFuncName(aOrg, mLambda.bInLambdaFunction))
if (ParseLambdaFuncName( aOrg ))
return true;
} while (mbRewind);
@@ -4764,8 +4803,10 @@ std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormul
{
if (eLastOp == ocLet)
{
mLambda.bInLambdaFunction = true;
mLambda.nBracketPos = nBrackets;
m_aLambda.bInLambdaFunction = true;
m_aLambda.nBracketPos = nBrackets;
m_aLambda.nParaPos++;
m_aLambda.nParaCount = GetPossibleParaCount(rFormula.subView(nSrcPos - 1));
}
++nBrackets;
@@ -4792,10 +4833,10 @@ std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormul
else
{
nBrackets--;
if (mLambda.bInLambdaFunction && mLambda.nBracketPos == nBrackets)
if (m_aLambda.bInLambdaFunction && m_aLambda.nBracketPos == nBrackets)
{
mLambda.bInLambdaFunction = false;
mLambda.nBracketPos = nBrackets;
m_aLambda.bInLambdaFunction = false;
m_aLambda.nBracketPos = nBrackets;
}
}
if (bUseFunctionStack && nFunction)
@@ -4806,6 +4847,9 @@ std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormul
{
if (bUseFunctionStack)
++pFunctionStack[ nFunction ].nSep;
if (m_aLambda.bInLambdaFunction && m_aLambda.nBracketPos + 1 == nBrackets)
m_aLambda.nParaPos++;
}
break;
case ocArrayOpen: