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:

Reply via email to