include/formula/FormulaCompiler.hxx | 5 ++ sc/inc/compiler.hxx | 3 + sc/qa/unit/data/xlsx/tdf137543.xlsx |binary sc/qa/unit/subsequent_export_test2.cxx | 5 ++ sc/source/core/tool/compiler.cxx | 60 ++++++++++++++++++++++++++++----- 5 files changed, 63 insertions(+), 10 deletions(-)
New commits: commit d557d2ae8c3c259bcf01465e6380cad77ed4bdb9 Author: Balazs Varga <[email protected]> AuthorDate: Thu Jun 6 17:38:39 2024 +0200 Commit: Balazs Varga <[email protected]> CommitDate: Thu Jun 6 23:39:32 2024 +0200 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 <[email protected]> diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx index d70dcb4c09d8..11a49f4680a0 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 e95e5ee78e31..ba53dbb9cb83 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 16801b21a2e1..2a0854755a33 100644 Binary files a/sc/qa/unit/data/xlsx/tdf137543.xlsx and b/sc/qa/unit/data/xlsx/tdf137543.xlsx differ diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx index 1ae4d22fc441..81ad2857269c 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 98a91f133674..738ed67fc9b9 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:
