diff --git a/formula/inc/core_resource.hrc b/formula/inc/core_resource.hrc index 081f71b56772..45e0aa62507d 100644 --- a/formula/inc/core_resource.hrc +++ b/formula/inc/core_resource.hrc @@ -282,6 +282,8 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] = { "COM.MICROSOFT.SORT" , SC_OPCODE_SORT }, { "COM.MICROSOFT.SORTBY" , SC_OPCODE_SORTBY }, { "COM.MICROSOFT.UNIQUE" , SC_OPCODE_UNIQUE }, + { "COM.MICROSOFT.LET" , SC_OPCODE_LET }, + { "_xlpm." , SC_OPCODE_STRINGNAME }, { "ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union) { "OFFSET" , SC_OPCODE_OFFSET }, { "INDEX" , SC_OPCODE_INDEX }, @@ -737,6 +739,8 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] = { "_xlfn._xlws.SORT" , SC_OPCODE_SORT }, { "_xlfn.SORTBY" , SC_OPCODE_SORTBY }, { "_xlfn.UNIQUE" , SC_OPCODE_UNIQUE }, + { "_xlfn.LET" , SC_OPCODE_LET }, + { "_xlpm." , SC_OPCODE_STRINGNAME }, { "_xlfn.ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union) { "OFFSET" , SC_OPCODE_OFFSET }, { "INDEX" , SC_OPCODE_INDEX }, @@ -1195,6 +1199,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] = { "SORT" , SC_OPCODE_SORT }, { "SORTBY" , SC_OPCODE_SORTBY }, { "UNIQUE" , SC_OPCODE_UNIQUE }, + { "LET" , SC_OPCODE_LET }, { "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union) { "OFFSET" , SC_OPCODE_OFFSET }, { "INDEX" , SC_OPCODE_INDEX }, @@ -1654,6 +1659,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] = { "SORT" , SC_OPCODE_SORT }, { "SORTBY" , SC_OPCODE_SORTBY }, { "UNIQUE" , SC_OPCODE_UNIQUE }, + { "LET" , SC_OPCODE_LET }, { "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union) { "OFFSET" , SC_OPCODE_OFFSET }, { "INDEX" , SC_OPCODE_INDEX }, // ?? first character = I ?? @@ -2111,6 +2117,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH[] = { "SORT" , SC_OPCODE_SORT }, { "SORTBY" , SC_OPCODE_SORTBY }, { "UNIQUE" , SC_OPCODE_UNIQUE }, + { "LET" , SC_OPCODE_LET }, { "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, { "OFFSET" , SC_OPCODE_OFFSET }, { "INDEX" , SC_OPCODE_INDEX }, @@ -2549,6 +2556,7 @@ const std::pair RID_STRLIST_FUNCTION_NAMES[] = { NC_("RID_STRLIST_FUNCTION_NAMES", "SORT") , SC_OPCODE_SORT }, { NC_("RID_STRLIST_FUNCTION_NAMES", "SORTBY") , SC_OPCODE_SORTBY }, { NC_("RID_STRLIST_FUNCTION_NAMES", "UNIQUE") , SC_OPCODE_UNIQUE }, + { NC_("RID_STRLIST_FUNCTION_NAMES", "LET") , SC_OPCODE_LET }, { NC_("RID_STRLIST_FUNCTION_NAMES", "MULTIRANGE") , SC_OPCODE_MULTI_AREA }, // legacy for range list (union) { NC_("RID_STRLIST_FUNCTION_NAMES", "OFFSET") , SC_OPCODE_OFFSET }, { NC_("RID_STRLIST_FUNCTION_NAMES", "INDEX") , SC_OPCODE_INDEX }, // ?? first character = I ?? diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx index 71ca0e8f8bd7..453c73094f54 100644 --- a/formula/source/core/api/FormulaCompiler.cxx +++ b/formula/source/core/api/FormulaCompiler.cxx @@ -626,6 +626,7 @@ uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::create SC_OPCODE_IF_ERROR, SC_OPCODE_IF_NA, SC_OPCODE_CHOOSE, + SC_OPCODE_LET, SC_OPCODE_AND, SC_OPCODE_OR }; @@ -1221,6 +1222,7 @@ bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp ) case ocIfError: case ocIfNA: case ocChoose: + case ocLet: return true; default: ; @@ -1269,6 +1271,7 @@ bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode ) case ocSortBy : case ocRandArray : case ocUnique : + case ocLet : return true; default: { @@ -1565,6 +1568,11 @@ bool FormulaCompiler::GetToken() case ocAggregate: glSubTotal = true; break; + case ocStringName: + if( HandleStringName()) + return true; + else + return false; case ocName: if( HandleRange()) { @@ -1946,6 +1954,9 @@ void FormulaCompiler::Factor() case ocChoose: pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1; break; + case ocLet: + pFacToken->GetJump()[0] = SAL_MAX_UINT8 + 1; + break; case ocIfError: case ocIfNA: pFacToken->GetJump()[ 0 ] = 2; // if, behind @@ -1978,6 +1989,9 @@ void FormulaCompiler::Factor() case ocChoose: nJumpMax = FORMULA_MAXJUMPCOUNT; break; + case ocLet: + nJumpMax = SAL_MAX_UINT8; + break; case ocIfError: case ocIfNA: nJumpMax = 2; @@ -1993,7 +2007,7 @@ void FormulaCompiler::Factor() assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case"); } short nJumpCount = 0; - while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep) + while ( (nJumpCount < (SAL_MAX_UINT8 - 1)) && (eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)) { if ( ++nJumpCount <= nJumpMax ) @@ -2022,6 +2036,9 @@ void FormulaCompiler::Factor() case ocChoose: bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT); break; + case ocLet: + bLimitOk = (nJumpCount < SAL_MAX_UINT8); + break; case ocIfError: case ocIfNA: bLimitOk = (nJumpCount <= 2); @@ -2598,7 +2615,7 @@ const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuf break; case svString: - if( eOp == ocBad || eOp == ocStringXML ) + if( eOp == ocBad || eOp == ocStringXML || eOp == ocStringName ) rBuffer.append( t->GetString().getString()); else AppendString( rBuffer, t->GetString().getString() ); @@ -2920,6 +2937,11 @@ bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/) return true; } +bool FormulaCompiler::HandleStringName() +{ + return true; +} + bool FormulaCompiler::HandleRange() { return true; diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx index a7fc6a06868d..d722d767e065 100644 --- a/formula/source/core/api/token.cxx +++ b/formula/source/core/api/token.cxx @@ -435,6 +435,8 @@ bool FormulaTokenArray::AddFormulaToken( AddBad( aStrVal ); else if ( eOpCode == ocStringXML ) AddStringXML( aStrVal ); + else if ( eOpCode == ocStringName ) + AddStringName( aStrVal ); else if ( eOpCode == ocExternal || eOpCode == ocMacro ) Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) ); else if ( eOpCode == ocWhitespace ) @@ -914,6 +916,10 @@ FormulaToken* FormulaTokenArray::AddStringXML( const OUString& rStr ) return Add( new FormulaStringOpToken( ocStringXML, svl::SharedString( rStr ) ) ); // string not interned } +FormulaToken* FormulaTokenArray::AddStringName( const OUString& rStr ) +{ + return Add( new FormulaStringOpToken( ocStringName, svl::SharedString( rStr ) ) ); // string not interned +} void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits ) { @@ -1576,12 +1582,15 @@ FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp ) case ocIfError: case ocIfNA: case ocChoose: + case ocLet: { - short nJump[FORMULA_MAXJUMPCOUNT + 1]; + short nJump[SAL_MAX_UINT8 + 1]; if ( eOp == ocIf ) nJump[ 0 ] = 3; else if ( eOp == ocChoose ) nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1; + else if ( eOp == ocLet ) + nJump[0] = SAL_MAX_UINT8 + 1; else nJump[ 0 ] = 2; pRet = new FormulaJumpToken( eOp, nJump ); @@ -1660,6 +1669,20 @@ FormulaToken* FormulaTokenArrayPlainIterator::GetNextName() return nullptr; } +FormulaToken* FormulaTokenArrayPlainIterator::GetNextStringName() +{ + if (mpFTA->GetArray()) + { + while (mnIndex < mpFTA->GetLen()) + { + FormulaToken* t = mpFTA->GetArray()[mnIndex++]; + if (t->GetType() == svString && t->GetOpCode() == ocStringName) + return t; + } + } + return nullptr; +} + const FormulaToken* FormulaTokenIterator::Next() { const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC ); diff --git a/formula/source/ui/dlg/parawin.cxx b/formula/source/ui/dlg/parawin.cxx index 979125866bb9..3c6d333a10f0 100644 --- a/formula/source/ui/dlg/parawin.cxx +++ b/formula/source/ui/dlg/parawin.cxx @@ -27,6 +27,7 @@ #include #include #include +#include namespace formula { @@ -150,7 +151,10 @@ void ParaWin::UpdateArgDesc( sal_uInt16 nArg ) aArgName = pFuncDesc->getParameterName(nRealArg); sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart(); if ( nArg >= nVarArgsStart ) - aArgName += OUString::number( (nArg-nVarArgsStart)/2 + 1 ); + { + sal_Int16 nShifted = pFuncDesc->getFunctionName().equalsIgnoreAsciiCase(u"LET") ? nPos / 2 : 0; + aArgName += OUString::number( (nArg-nVarArgsStart)/2 + 1 + nShifted ); + } aArgName += " " + ((nArg > (nFix+1) || pFuncDesc->isParameterOptional(nRealArg)) ? m_sOptional : m_sRequired) ; } @@ -209,8 +213,9 @@ void ParaWin::UpdateArgInput( sal_uInt16 nOffset, sal_uInt16 i ) sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart(); if ( nArg >= nVarArgsStart ) { + sal_Int16 nShifted = pFuncDesc->getFunctionName().equalsIgnoreAsciiCase(u"LET") ? nPos / 2 : 0; OUString aArgName = pFuncDesc->getParameterName(nRealArg) + - OUString::number( (nArg-nVarArgsStart)/2 + 1 ); + OUString::number( (nArg-nVarArgsStart)/2 + 1 + nShifted); SetArgName( i, aArgName ); } else diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx index 08710f561b5a..d70dcb4c09d8 100644 --- a/include/formula/FormulaCompiler.hxx +++ b/include/formula/FormulaCompiler.hxx @@ -344,6 +344,7 @@ protected: virtual void SetError(FormulaError nError); virtual FormulaTokenRef ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 ); virtual bool HandleExternalReference(const FormulaToken& _aToken); + virtual bool HandleStringName(); virtual bool HandleRange(); virtual bool HandleColRowName(); virtual bool HandleDbData(); @@ -419,6 +420,12 @@ protected: 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) + struct LambdaFunc + { + bool bInLambdaFunction = false; + short nBracketPos = 0; + } mLambda; + public: enum InitSymbols { diff --git a/include/formula/compiler.hxx b/include/formula/compiler.hxx index 181576000c4d..c9e2e5375b01 100644 --- a/include/formula/compiler.hxx +++ b/include/formula/compiler.hxx @@ -61,6 +61,8 @@ #define SC_OPCODE_TABLE_REF_ITEM_THIS_ROW 35 #define SC_OPCODE_STOP_DIV 36 #define SC_OPCODE_SKIP 37 /* used to skip raw tokens during string compilation */ +#define SC_OPCODE_STRINGNAME 38 /* special OpCode for lambda function names */ +#define SC_OPCODE_LET 39 /*** error constants #... ***/ #define SC_OPCODE_START_ERRORS 40 diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx index 89f6f95f57dd..8681e95639eb 100644 --- a/include/formula/opcode.hxx +++ b/include/formula/opcode.hxx @@ -38,6 +38,7 @@ enum OpCode : sal_uInt16 ocIfError = SC_OPCODE_IF_ERROR, ocIfNA = SC_OPCODE_IF_NA, ocChoose = SC_OPCODE_CHOOSE, + ocLet = SC_OPCODE_LET, // Parentheses and separators ocOpen = SC_OPCODE_OPEN, ocClose = SC_OPCODE_CLOSE, @@ -61,6 +62,7 @@ enum OpCode : sal_uInt16 ocTableRefItemTotals = SC_OPCODE_TABLE_REF_ITEM_TOTALS, ocTableRefItemThisRow = SC_OPCODE_TABLE_REF_ITEM_THIS_ROW, ocSkip = SC_OPCODE_SKIP, + ocStringName = SC_OPCODE_STRINGNAME, // Access commands ocDBArea = SC_OPCODE_DB_AREA, ocTableRef = SC_OPCODE_TABLE_REF, @@ -553,6 +555,7 @@ inline std::string OpCodeEnumToString(OpCode eCode) case ocMissing: return "Missing"; case ocBad: return "Bad"; case ocStringXML: return "StringXML"; + case ocStringName: return "StringName"; case ocSpaces: return "Spaces"; case ocWhitespace: return "Whitespace"; case ocMatRef: return "MatRef"; @@ -996,6 +999,7 @@ inline std::string OpCodeEnumToString(OpCode eCode) case ocSort: return "Sort"; case ocSortBy: return "SortBy"; case ocUnique: return "Unique"; + case ocLet: return "Let"; case ocTTT: return "TTT"; case ocDebugVar: return "DebugVar"; case ocDataToken1: return "DataToken1"; diff --git a/include/formula/tokenarray.hxx b/include/formula/tokenarray.hxx index fbf2c186768a..4e59ca8e4f02 100644 --- a/include/formula/tokenarray.hxx +++ b/include/formula/tokenarray.hxx @@ -487,6 +487,7 @@ public: FormulaToken* AddExternal( const OUString& rStr, OpCode eOp = ocExternal ); FormulaToken* AddBad( const OUString& rStr ); /// ocBad with OUString FormulaToken* AddStringXML( const OUString& rStr ); /// ocStringXML with OUString, temporary during import + FormulaToken* AddStringName( const OUString& rStr ); /// ocStringName with OUString - Lambda functions virtual FormulaToken* MergeArray( ); @@ -578,6 +579,7 @@ public: private: SAL_DLLPRIVATE const FormulaToken* GetNonEndOfPathToken( short nIdx ) const; + SAL_DLLPRIVATE const FormulaToken* GetNonEndOfPathToken2( short nIdx ) const; }; // For use in SAL_INFO, SAL_WARN etc @@ -639,6 +641,7 @@ public: FormulaToken* Next(); FormulaToken* NextNoSpaces(); FormulaToken* GetNextName(); + FormulaToken* GetNextStringName(); FormulaToken* GetNextReference(); FormulaToken* GetNextReferenceRPN(); FormulaToken* GetNextReferenceOrName(); diff --git a/sc/README.md b/sc/README.md index a66cb5491ff9..84457499162f 100644 --- a/sc/README.md +++ b/sc/README.md @@ -70,6 +70,8 @@ https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocume * Information Functions * COUNTIF * COUNTIFS +* Lambda Functions + * LET * Lookup Functions * HLOOKUP * LOOKUP diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx index af379104bca7..e95e5ee78e31 100644 --- a/sc/inc/compiler.hxx +++ b/sc/inc/compiler.hxx @@ -155,6 +155,7 @@ public: // since the reference count is cleared! void SetOpCode( OpCode eCode ); void SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase ); + void SetStringName( rtl_uString* pData, rtl_uString* pDataIgnoreCase ); void SetSingleReference( const ScSingleRefData& rRef ); void SetDoubleReference( const ScComplexRefData& rRef ); void SetDouble( double fVal ); @@ -358,6 +359,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 ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange ); bool ParseDBRange( const OUString& ); bool ParseColRowName( const OUString& ); @@ -516,6 +518,7 @@ private: virtual void fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const override; virtual bool HandleExternalReference(const formula::FormulaToken& _aToken) override; + virtual bool HandleStringName() override; virtual bool HandleRange() override; virtual bool HandleColRowName() override; virtual bool HandleDbData() override; diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h index e02235b8ac8b..7e0289108c8d 100644 --- a/sc/inc/helpids.h +++ b/sc/inc/helpids.h @@ -601,5 +601,6 @@ inline constexpr OUString HID_FUNC_FILTER_MS = u"SC_HID_FUNC_FILTER_MS"_ustr; inline constexpr OUString HID_FUNC_SORT_MS = u"SC_HID_FUNC_SORT_MS"_ustr; inline constexpr OUString HID_FUNC_SORTBY_MS = u"SC_HID_FUNC_SORTBY_MS"_ustr; inline constexpr OUString HID_FUNC_UNIQUE_MS = u"SC_HID_FUNC_UNIQUE_MS"_ustr; +inline constexpr OUString HID_FUNC_LET_MS = u"SC_HID_FUNC_LET_MS"_ustr; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/scfuncs.hrc b/sc/inc/scfuncs.hrc index 7a129a046a11..9ce5e234c288 100644 --- a/sc/inc/scfuncs.hrc +++ b/sc/inc/scfuncs.hrc @@ -4263,4 +4263,16 @@ const TranslateId SC_OPCODE_UNIQUE_ARY[] = NC_("SC_OPCODE_UNIQUE", "Logical value that defines what values are considered unique: TRUE - returns values that occur only once. FALSE or omitted (default) - returns all distinct (different) values in the range or array.") }; +// -=*# Resource for function LET #*=- +const TranslateId SC_OPCODE_LET_ARY[] = +{ + NC_("SC_OPCODE_LET", "The LET function assigns names to calculation results. This allows storing intermediate calculations, values, or defining names inside a formula. These names only apply within the scope of the LET function."), + NC_("SC_OPCODE_LET", "Name 1"), + NC_("SC_OPCODE_LET", "The first name to assign. Must start with a letter. Cannot be the output of a formula or conflict with range syntax."), + NC_("SC_OPCODE_LET", "Name value "), + NC_("SC_OPCODE_LET", "Name value 1, Name value 2,... The value or calculation to assign to Name."), + NC_("SC_OPCODE_LET", "Calculation or Name "), + NC_("SC_OPCODE_LET", "A calculation that uses all names within the LET function. This must be the last argument in the LET function. Or a second Name to assign to a second Name value. If a second Name is specified, Name value 2 and Calculation or Name 3 become required.") +}; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/extras/scfunctionlistobj.cxx b/sc/qa/extras/scfunctionlistobj.cxx index 87060183685d..431e63bd7ef2 100644 --- a/sc/qa/extras/scfunctionlistobj.cxx +++ b/sc/qa/extras/scfunctionlistobj.cxx @@ -77,7 +77,7 @@ public: ScFunctionListObj::ScFunctionListObj() : UnoApiTest(u"/sc/qa/extras/testdocuments"_ustr) , XElementAccess(cppu::UnoType>::get()) - , XIndexAccess(403) + , XIndexAccess(404) , XNameAccess("IF") , XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions") { diff --git a/sc/qa/unit/data/functions/spreadsheet/fods/let.fods b/sc/qa/unit/data/functions/spreadsheet/fods/let.fods new file mode 100644 index 000000000000..97ad8c2dbcdb --- /dev/null +++ b/sc/qa/unit/data/functions/spreadsheet/fods/let.fods @@ -0,0 +1,4308 @@ + + + + 2024-01-16T18:30:06.278000000PT7H19M5S109LibreOfficeDev/24.8.0.0.alpha1$Windows_X86_64 LibreOffice_project/9daa119fe104c852a42f00e9c8349687ccd104d22024-06-02T09:11:26.793000000 + + + 0 + 0 + 50916 + 21544 + + + view1 + + + 2 + 3 + 2 + 0 + 0 + 0 + 0 + 0 + 75 + 60 + true + false + false + false + false + + + 9 + 38 + 2 + 0 + 0 + 0 + 0 + 0 + 75 + 60 + true + false + false + false + false + + + Sheet1 + 1851 + 0 + 75 + 60 + false + true + true + true + false + true + 12632256 + 1 + true + true + true + false + false + false + 1270 + 1270 + 1 + 1 + true + false + false + false + false + + + + + true + true + true + 0 + true + true + false + true + false + + + en + US + + + + + + 12632256 + true + true + 0 + false + false + true + true + false + 3 + false + Microsoft Print to PDF + false + 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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgBDT01QQVRfRFVQTEVYX01PREUTAER1cGxleE1vZGU6OlVua25vd24MAFBSSU5URVJfTkFNRRYATWljcm9zb2Z0IFByaW50IHRvIFBERgsARFJJVkVSX05BTUUWAE1pY3Jvc29mdCBQcmludCBUbyBQREY= + false + 1270 + 1270 + 1 + 1 + true + false + false + true + true + true + true + true + 7 + true + + + Sheet1 + + + Sheet2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + \ + + + + + \- + + + + + + % + + + \ + + + + + + \ + + - + + + + + \ + + - + + + + + + + + + + £ + + + + + - + £ + + + + + + + + $ + + + + + + $ + + ( + + ) + + + $ + + - + + + + + + + + + + + + + . + + . + + + + + + + + + + + + ( + + ) + + + + + + + + + + - + + + + + $ + + + + + ($ + + ) + + + + + + + + + + + + ( + + ) + + + - + + + + + + + + + + + + + + + + + + - + + + + + - € + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + + - + + + + £ + + + + - + £ + + + + + + - + + + + + + + + + + + + + + + + - + + + + + + - + + + + + + + + + + + + + + + + + + + ( + + + + ) + + + + + + + + + + $ + + + + + + + + ( + + ) + + + + + + + + + + - + + + + + + + $ + + + + + - + $ + + + + + + + + + + + - + + + + + + - + + + + + + + + + + + + + + + + - + + + + + + + Yes + + + Yes + + + No + + + + + + + + + + + + + EUR + + + + - + + + EUR + + + + + + + + - + + + + + + \ + + + + \- + + + + + \ + + + + + + \ + + - + + + + + \ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + £ + + + + - + £ + + + + + + + + + + + + + EUR + + + + - + + + EUR + + + + + + + + + - + + + + + + + - + + - + + + + $ + + + + + + $ + + ( + + ) + + + $ + + - + + + + + + + + + + $ + + + + + $( + + ) + + + $- + + + + + + + + + + + + + + + + £ + + + + + - + £ + + + + + + On + + + On + + + Off + + + + + $ + + + + + + ($ + + ) + + + + + + + + - + + + + + + + + + $ + + + + + ($ + + ) + + + + $ + + + + + + ($ + + ) + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + + + + True + + + True + + + False + + + + + + + + + - + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + / + + + + £ + + + + + - + £ + + + + + £ + + + + + - + £ + + + + + $ + + + + + $( + + ) + + + $- + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + + WAHR + + + WAHR + + + FALSCH + + + + + + . + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + + ( + + ) + + + - + + + + + + + + + + + + + : + + + + + : + + + + £ + + + + + - + £ + + + + + + \ + + + + + \- + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + : + + : + + + + \ + + + + \- + + + + + + + + + - + + + + + + + + + + / + + / + + + + + + + / + + / + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + - + + + + + + + + + - + + + + + + + + - + + + + + + + + + - + + + + + + + + + + - + + + + + + + - + + + + + + + + + + + + + + + - + + + + + + + - Kč + + + + + + + + + + + + + + + - + + + + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + - + + + + + + + + + + + + ¥€ + + + + + + + + + ( + + + + + + ) + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + - + + + + + + + + % + + + + % + + + + % + + + + % + + + + % + + + + % + + + + + + $ + + + + + ( + $ + + ) + + + + + % + + + + % + + + + - + + % + + + + + + + + - + + + + + + - + + - + + + + £ + + + + + - + £ + + + + + £ + + + + + - + £ + + + + + + % + + + + - + + % + + + + + DM + + + - + + DM + + + + + DM + + + + - + + DM + + + + + DM + + + - + + DM + + + + + DM + + + + - + + DM + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + DM + + + - + + + DM + + + + + - DM + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + DM + + + - + + + DM + + + + + - + + DM + + + + + + + + + + + Ouch! - + + - Error detected! + + + + - Result=0 - No Errordetection + + + + / + + / + + + + + / + + / + + + + + + + + + + - + + + + + + + + . + + . + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + + - € + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + $ + + + + $ + + + + + - + $ + + + + + + . + + . + + + + + . + + + + + + + . + + + + + + + + + + : + + + + + + + : + + : + + + + + + + . + + . + + + + : + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + $ + + + + $ + + + + -$ + + + + + $ + + + + + -$ + + + + + $ + + + + -$ + + + + + $ + + + + + -$ + + + + + -$ + + + + $ + + + + -$ + + + + $- + + + + + - + + + + + + + - + + + + - + + + + + -$ + + + + $ + + + + -$ + + + + $- + + + + + + - + + + + + + + - + + + + - + + + + + + + Ft + + + - + + Ft + + + + + Ft + + + + - + + Ft + + + + + Ft + + + - + + Ft + + + + + Ft + + + + - + + Ft + + + + + . + + . + + + + + . + + . + + + + + . + + + + + . + + + + + . + + . + + + + : + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + Ft + + + - + + + Ft + + + + + - Ft + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + Ft + + + - + + + Ft + + + + + - + + Ft + + + + + + + + + + + + / + + / + + + + + - + + - + + + + + - + + + + + - + + + + + : + + + + + + + : + + : + + + + + + + : + + + + + : + + : + + + + + / + + / + + + + : + + + + + + + + + ( + + ) + + + + + + + + + ( + + ) + + + + + + + + + ( + + + ) + + + + + + + + + + ( + + + ) + + + + + + + + + ( + + + ) + + + + + + + + + + ( + + + ) + + + + + + + + ( + + ) + + + + + + + + ( + + ) + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + + + + + ( + + ) + + + + + - + + + + + + + + + + + + + + + + ( + + ) + + + + - + + + + + + + + + + + + + + + + + + + + ( + + ) + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + : + + : + + + + + : + + + + + + + + % + + + + + + + - + + - + + + + + + + + + - + + + + + + + + + . + + + + + + + . + + + + + + + + + + : + + + + + + + : + + : + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ??? + + + + + Page 1 + + + + + + + + ???(???) + + + 0000.00.00, 00:00:00 + + + + + + Page 1/ 99 + + + + + + + + + + + + + + + LET Function + + + + + + + + + + + + Result + + + IGAZ + + + + + + + + + Sheet + + + Result + + + Description + + + + + 2 + + + IGAZ + + + Simple LET formulas with local references and values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Function + + + + Expected + + + + Correct + + + FunctionString + + + Comment + + + XL-test + + + + Student + + + Maths + + + English + + + Biology + + + Art + + + + + + + 25 + + + + 25 + + + + IGAZ + + + =LET(first;5;second;SUM(first;5);third;SUM(second;5);SUM(second;third)) + + + + Aiden + + + 263 + + + 215 + + + 160 + + + 153 + + + + + + + 40 + + + + 40 + + + + IGAZ + + + =LET(first;SUM(5;6);second;SUM(10;19);SUM(first;second)) + + + + Ava + + + 231 + + + 228 + + + 208 + + + 187 + + + + + + + + + + Carter + + + 154 + + + 201 + + + 213 + + + 167 + + + + + + Result + + + + Result + + + + + + Emma + + + 257 + + + 220 + + + 167 + + + 172 + + + + + + Satisfactory + + + + Satisfactory + + + + IGAZ + + + =LET(avg; AVERAGE(O2:R2); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + Isabella + + + 238 + + + 256 + + + 260 + + + 246 + + + + + + Good + + + + Good + + + + IGAZ + + + =LET(avg; AVERAGE(O3:R3); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + Jackson + + + 149 + + + 164 + + + 130 + + + 270 + + + + + + Satisfactory + + + + Satisfactory + + + + IGAZ + + + =LET(avg; AVERAGE(O4:R4); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + Liam + + + 284 + + + 250 + + + 150 + + + 158 + + + + + + Good + + + + Good + + + + IGAZ + + + =LET(avg; AVERAGE(O5:R5); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + Lucas + + + 261 + + + 227 + + + 191 + + + 221 + + + + + + Excellent + + + + Excellent + + + + IGAZ + + + =LET(avg; AVERAGE(O6:R6); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + Mia + + + 187 + + + 196 + + + 152 + + + 173 + + + + + + Satisfactory + + + + Satisfactory + + + + IGAZ + + + =LET(avg; AVERAGE(O7:R7); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + + + + + Good + + + + Good + + + + IGAZ + + + =LET(avg; AVERAGE(O8:R8); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + + + + + Good + + + + Good + + + + IGAZ + + + =LET(avg; AVERAGE(O9:R9); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + + + + + Satisfactory + + + + Satisfactory + + + + IGAZ + + + =LET(avg; AVERAGE(O10:R10); IF(avg>249; "Excellent"; IF(avg>=200; "Good"; IF(avg>150; "Satisfactory"; "Poor")))) + + + + + + + + + + + + + + + + + + + + + + + + Middle name + + + + Middle name + + + + + + Full name + + + + + + + D. + + + + D. + + + + IGAZ + + + =LET(full_name; N18; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + Aiden D. Anderson + + + + + + + Andrew + + + + Andrew + + + + IGAZ + + + =LET(full_name; N19; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + John Andrew Brown + + + + + + + Claire + + + + Claire + + + + IGAZ + + + =LET(full_name; N20; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + Ava Claire Brook + + + + + + Finn + + + + Finn + + + + IGAZ + + + =LET(full_name; N21; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + Frank Finn Hill + + + + + + M. + + + + M. + + + + IGAZ + + + =LET(full_name; N22; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + Robert M. Furlan + + + + + + Grace + + + + + Grace + + + + IGAZ + + + =LET(full_name; N23; space; SEARCH(" "; full_name); MID(full_name; space+1; SEARCH(" "; full_name; space+1) -space-1)) + + + + Mia Grace Johnson + + + + + + + + + + + + + + + + + + Student + + + Subject + + + Date + + + + Student + + + Subject + + + Date + + + + + + + + Carter + + + Biology + + + 45445 + + + + Carter + + + Biology + + + 45445 + + + + IGAZ + + + {=LET(data;$N$28:$P$45;dates;P$28:P$45;today;45444;n;R30;FILTER(data;(dates>today)*(dates<=today+n); "No results"))} + + + + Student + + + Subject + + + Date + + + + + + + + Emma + + + English + + + 45446 + + + + Emma + + + English + + + 45446 + + + + IGAZ + + + {=LET(data;$N$28:$P$45;dates;P$28:P$45;today;45444;n;R30;FILTER(data;(dates>today)*(dates<=today+n); "No results"))} + + + + Aiden + + + Maths + + + 2024-05-28 + + + + + + Emma + + + Maths + + + 45447 + + + + Emma + + + Maths + + + 45447 + + + + IGAZ + + + {=LET(data;$N$28:$P$45;dates;P$28:P$45;today;45444;n;R30;FILTER(data;(dates>today)*(dates<=today+n); "No results"))} + + + + Aiden + + + Biology + + + 2024-05-29 + + + + + + + + + + + Ava + + + Art + + + 2024-05-30 + + + + 3 + + + + + + + + + + + Ava + + + English + + + 2024-05-31 + + + + + + + + + + + Carter + + + English + + + 2024-06-01 + + + + + + + + + + Carter + + + Biology + + + 2024-06-02 + + + + + + + + + Emma + + + English + + + 2024-06-03 + + + + + + + + + + + Emma + + + Maths + + + 2024-06-04 + + + + + + + + + + Isabella + + + English + + + 2024-06-05 + + + + + + + + + + Isabella + + + Biology + + + 2024-06-06 + + + + + + + + + + Jackson + + + Biology + + + 2024-06-07 + + + + + + + + + + + Jackson + + + Art + + + 2024-06-08 + + + + + + + + + + + + + Liam + + + Art + + + 2024-06-09 + + + + + + + + + + + + Liam + + + Maths + + + 2024-06-10 + + + + + + + + + + + Lucas + + + Maths + + + 2024-06-11 + + + + + + + + + + + + Lucas + + + English + + + 2024-06-12 + + + + + + + + + + Mia + + + Art + + + 2024-06-13 + + + + + + + + + + Mia + + + Biology + + + 2024-06-14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sc/qa/unit/data/xlsx/tdf137543.xlsx b/sc/qa/unit/data/xlsx/tdf137543.xlsx new file mode 100644 index 000000000000..16801b21a2e1 Binary files /dev/null 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 3be3576b27eb..1ae4d22fc441 100644 --- a/sc/qa/unit/subsequent_export_test2.cxx +++ b/sc/qa/unit/subsequent_export_test2.cxx @@ -1311,6 +1311,20 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf91332) CPPUNIT_ASSERT_EQUAL(Color(0x90cf47), nColor); } +CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf137543XLSX) +{ + // LET function test + createScDoc("xlsx/tdf137543.xlsx"); + + save(u"Calc Office Open XML"_ustr); + xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr); + CPPUNIT_ASSERT(pSheet); + + 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); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index d68697830c23..ae0d6967def8 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -2917,6 +2917,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFunctionLists) "HYPERLINK", "INDEX", "INDIRECT", + "LET", "LOOKUP", "MATCH", "OFFSET", diff --git a/sc/source/core/data/funcdesc.cxx b/sc/source/core/data/funcdesc.cxx index 9a205aa3a279..0231e5af190e 100644 --- a/sc/source/core/data/funcdesc.cxx +++ b/sc/source/core/data/funcdesc.cxx @@ -197,12 +197,12 @@ OUString ScFuncDesc::GetParamList() const aSig.append(maDefArgNames[nVarArgsStart] + "1" + sep + maDefArgNames[nVarArgsStart+1] - + "1" + sep + + (mxFuncName != "LET" ? "1" : "2") + sep + " " + maDefArgNames[nVarArgsStart] + "2" + sep + maDefArgNames[nVarArgsStart+1] - + "2" + sep + " ... " ); + + (mxFuncName != "LET" ? "2" : "3") + sep + " ... " ); } } @@ -394,6 +394,7 @@ ScFunctionList::ScFunctionList( bool bEnglishFunctionNames ) { SC_OPCODE_IF_ERROR, ENTRY(SC_OPCODE_IF_ERROR_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_IFERROR, 2, { 0, 0 }, 0 }, { SC_OPCODE_IF_NA, ENTRY(SC_OPCODE_IF_NA_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_IFNA, 2, { 0, 0 }, 0 }, { SC_OPCODE_CHOOSE, ENTRY(SC_OPCODE_CHOOSE_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_WAHL, VAR_ARGS+1, { 0, 0 }, 31 }, + { SC_OPCODE_LET, ENTRY(SC_OPCODE_LET_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_LET_MS, PAIRED_VAR_ARGS + 1, { 0, 0, 0 }, 0 }, { SC_OPCODE_AND, ENTRY(SC_OPCODE_AND_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_UND, VAR_ARGS, { 0 }, 0 }, { SC_OPCODE_OR, ENTRY(SC_OPCODE_OR_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_ODER, VAR_ARGS, { 0 }, 0 }, { SC_OPCODE_PI, ENTRY(SC_OPCODE_PI_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_PI, 0, { }, 0 }, diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx index bde78a5662c1..e1752e086d82 100644 --- a/sc/source/core/inc/interpre.hxx +++ b/sc/source/core/inc/interpre.hxx @@ -190,6 +190,7 @@ struct FormulaTokenRef_hash { return std::hash()(static_cast(p1)); } }; typedef ::std::unordered_map< const formula::FormulaConstTokenRef, formula::FormulaConstTokenRef, FormulaTokenRef_hash> ScTokenMatrixMap; +typedef ::std::unordered_map< OUString, const formula::FormulaConstTokenRef> ScResultTokenMap; class ScInterpreter { @@ -246,6 +247,7 @@ private: formula::FormulaConstTokenRef xResult; ScJumpMatrix* pJumpMatrix; // currently active array condition, if any ScTokenMatrixMap maTokenMatrixMap; // map FormulaToken* to formula::FormulaTokenRef if in array condition + ScResultTokenMap maResultTokenMap; // Result FormulaToken* to formula::FormulaTokenRef ScFormulaCell* pMyFormulaCell; // the cell of this formula expression const formula::FormulaToken* pCur; // current token @@ -513,6 +515,11 @@ private: ScMatrixRef GetMatrix( short & rParam, size_t & rInRefList ); sc::RangeMatrix GetRangeMatrix(); + // Get tokens at specific parameters for LET (lambda) function + void getTokensAtParameter( std::unique_ptr& pTokens, short nPos ); + static void replaceNamesToResult( const std::unordered_map nResultIndexes, + std::unique_ptr& pTokens ); + void ScTableOp(); // repeated operations // common helper functions @@ -717,6 +724,7 @@ private: void ScSort(); void ScSortBy(); void ScUnique(); + void ScLet(); void ScSubTotal(); // If upon call rMissingField==true then the database field parameter may be diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx index 68422b3b83de..98a91f133674 100644 --- a/sc/source/core/tool/compiler.cxx +++ b/sc/source/core/tool/compiler.cxx @@ -74,6 +74,8 @@ #include #include #include +#include +#include using namespace formula; using namespace ::com::sun::star; @@ -3651,6 +3653,20 @@ bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck ) return false; } +bool ScCompiler::ParseLambdaFuncName( const OUString& aOrg, bool bLambdaFunction ) +{ + if (bLambdaFunction && !aOrg.isEmpty()) + { + OUString aName = aOrg; + if (aOrg.startsWithIgnoreAsciiCase(u"_xlpm.")) + aName = aName.copy(6); + svl::SharedString aSS = rDoc.GetSharedStringPool().intern(aName); + maRawToken.SetStringName(aSS.getData(), aSS.getDataIgnoreCase()); + return true; + } + return false; +} + bool ScCompiler::ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange ) { /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc) @@ -4613,6 +4629,9 @@ Label_Rewind: if (bMayBeFuncName && ParseOpCode2( aUpper )) return true; + if (ParseLambdaFuncName(aOrg, mLambda.bInLambdaFunction)) + return true; + } while (mbRewind); // Last chance: it could be a broken invalidated reference that contains @@ -4743,6 +4762,12 @@ std::unique_ptr ScCompiler::CompileString( const OUString& rFormul { case ocOpen: { + if (eLastOp == ocLet) + { + mLambda.bInLambdaFunction = true; + mLambda.nBracketPos = nBrackets; + } + ++nBrackets; if (bUseFunctionStack) { @@ -4765,7 +4790,14 @@ std::unique_ptr ScCompiler::CompileString( const OUString& rFormul } } else + { nBrackets--; + if (mLambda.bInLambdaFunction && mLambda.nBracketPos == nBrackets) + { + mLambda.bInLambdaFunction = false; + mLambda.nBracketPos = nBrackets; + } + } if (bUseFunctionStack && nFunction) --nFunction; } @@ -5033,6 +5065,14 @@ ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex()); } +bool ScCompiler::HandleStringName() +{ + ScTokenArray* pNew = new ScTokenArray(rDoc); + pNew->AddString(mpToken->GetString()); + PushTokenArray(pNew, true); + return GetToken(); +} + bool ScCompiler::HandleRange() { ScTokenArray* pNew; diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx index 3ba43d03674a..9296a8e164e1 100644 --- a/sc/source/core/tool/interpr1.cxx +++ b/sc/source/core/tool/interpr1.cxx @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -8917,6 +8918,152 @@ void ScInterpreter::ScUnique() } } +void ScInterpreter::getTokensAtParameter( std::unique_ptr& pTokens, short nPos ) +{ + sal_uInt16 nOpen = 0; + sal_uInt16 nSepCount = 0; + formula::FormulaTokenArrayPlainIterator aIter(*pArr); + formula::FormulaToken* t = aIter.First(); + for (t = aIter.NextNoSpaces(); t; t = aIter.NextNoSpaces()) + { + OpCode aOpCode = t->GetOpCode(); + formula::StackVar aIntType = t->GetType(); + if ((aOpCode == ocOpen || aOpCode == ocArrayOpen || aOpCode == ocTableRefOpen) && aIntType == formula::StackVar::svSep) + nOpen++; + else if ((aOpCode == ocClose || aOpCode == ocArrayClose || aOpCode == ocTableRefClose) && aIntType == formula::StackVar::svSep) + nOpen--; + else if (aOpCode == ocSep && aIntType == formula::StackVar::svSep && nOpen == 1) + { + nSepCount++; + continue; + } + + if (nSepCount == nPos && nOpen > 0) + { + pTokens->AddToken(*t->Clone()); + } + } +} + +void ScInterpreter::replaceNamesToResult( const std::unordered_map nResultIndexes, + std::unique_ptr& pTokens ) +{ + formula::FormulaTokenArrayPlainIterator aIterResult(*pTokens); + for (FormulaToken* t = aIterResult.GetNextStringName(); t; t = aIterResult.GetNextStringName()) + { + auto iRes = nResultIndexes.find(t->GetString().getString()); + if (iRes != nResultIndexes.end()) + pTokens->ReplaceToken(aIterResult.GetIndex() - 1, iRes->second->Clone(), + FormulaTokenArray::ReplaceMode::CODE_ONLY); + } +} + +void ScInterpreter::ScLet() +{ + const short* pJump = pCur->GetJump(); + short nJumpCount = pJump[0]; + short nOrgJumpCount = nJumpCount; + + if (nJumpCount < 3 || (nJumpCount % 2 != 1)) + { + PushError(FormulaError::ParameterExpected); + aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]); + return; + } + + OUString aStrName; + std::unordered_map nResultIndexes; + formula::FormulaTokenArrayPlainIterator aIter(*pArr); + unique_ptr pValueTokens(new ScTokenArray(mrDoc)); + + // name and function pairs parameter + while (nJumpCount > 1) + { + if (nJumpCount == nOrgJumpCount) + { + aStrName = GetString().getString(); + } + else if ((nOrgJumpCount - nJumpCount + 1) % 2 == 1) + { + aIter.Jump(pJump[static_cast(nOrgJumpCount - nJumpCount + 1)] - 1); + FormulaToken* t = aIter.NextRPN(); + aStrName = t->GetString().getString(); + } + else + { + PushError(FormulaError::ParameterExpected); + aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]); + return; + } + nJumpCount--; + + // get value tokens + getTokensAtParameter(pValueTokens, nOrgJumpCount - nJumpCount); + nJumpCount--; + + // replace names with result tokens + replaceNamesToResult(nResultIndexes, pValueTokens); + + // calculate the inner results + ScCompiler aComp(mrDoc, aPos, *pValueTokens, mrDoc.GetGrammar(), false, false, &mrContext); + aComp.CompileTokenArray(); + ScInterpreter aInt(mrDoc.GetFormulaCell(aPos), mrDoc, mrContext, aPos, *pValueTokens); + sfx2::LinkManager aNewLinkMgr(mrDoc.GetDocumentShell()); + aInt.SetLinkManager(&aNewLinkMgr); + formula::StackVar aIntType = aInt.Interpret(); + + if (aIntType == formula::svMatrixCell) + { + ScConstMatrixRef xMat(aInt.GetResultToken()->GetMatrix()); + if (!nResultIndexes.insert(std::make_pair(aStrName, new ScMatrixToken(xMat->Clone()))).second) + PushIllegalParameter(); + } + else + { + FormulaConstTokenRef xTok(aInt.GetResultToken()); + if (!nResultIndexes.insert(std::make_pair(aStrName, xTok->Clone())).second) + PushIllegalParameter(); + } + pValueTokens->Clear(); + } + + // last parameter: calculation + getTokensAtParameter(pValueTokens, nOrgJumpCount - nJumpCount); + nJumpCount--; + + // replace names with result tokens + replaceNamesToResult(nResultIndexes, pValueTokens); + + // calculate the final result + ScCompiler aComp(mrDoc, aPos, *pValueTokens, mrDoc.GetGrammar(), false, false, &mrContext); + aComp.CompileTokenArray(); + ScInterpreter aInt(mrDoc.GetFormulaCell(aPos), mrDoc, mrContext, aPos, *pValueTokens); + sfx2::LinkManager aNewLinkMgr(mrDoc.GetDocumentShell()); + aInt.SetLinkManager(&aNewLinkMgr); + formula::StackVar aIntType = aInt.Interpret(); + + if (aIntType == formula::svMatrixCell) + { + ScConstMatrixRef xMat(aInt.GetResultToken()->GetMatrix()); + PushTokenRef(new ScMatrixToken(xMat->Clone())); + } + else + { + formula::FormulaConstTokenRef xLambdaResult(aInt.GetResultToken()); + if (xLambdaResult) + { + nGlobalError = xLambdaResult->GetError(); + if (nGlobalError == FormulaError::NONE) + PushTokenRef(xLambdaResult); + else + PushError(nGlobalError); + } + } + + pValueTokens.reset(); + aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]); +} + void ScInterpreter::ScSubTotal() { sal_uInt8 nParamCount = GetByte(); diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx index 520c0b6d7acd..bea16130257f 100644 --- a/sc/source/core/tool/interpr4.cxx +++ b/sc/source/core/tool/interpr4.cxx @@ -4162,6 +4162,7 @@ StackVar ScInterpreter::Interpret() case ocSort : ScSort(); break; case ocSortBy : ScSortBy(); break; case ocUnique : ScUnique(); break; + case ocLet : ScLet(); break; case ocTrue : ScTrue(); break; case ocFalse : ScFalse(); break; case ocGetActDate : ScGetActDate(); break; diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx index ffdc1071420e..d920807c5420 100644 --- a/sc/source/core/tool/parclass.cxx +++ b/sc/source/core/tool/parclass.cxx @@ -57,6 +57,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] = { ocIfError, {{ Array, Reference }, 0, Value }}, { ocIfNA, {{ Array, Reference }, 0, Value }}, { ocChoose, {{ Array, Reference }, 1, Value }}, + { ocLet, {{ Value, ReferenceOrRefArray, ReferenceOrRefArray, }, 2, ForceArrayReturn } }, // Other specials. { ocArrayClose, {{ Bounds }, 0, Bounds }}, { ocArrayColSep, {{ Bounds }, 0, Bounds }}, @@ -76,6 +77,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] = { ocSpaces, {{ Bounds }, 0, Bounds }}, { ocStop, {{ Bounds }, 0, Bounds }}, { ocStringXML, {{ Bounds }, 0, Bounds }}, + { ocStringName, {{ Bounds }, 0, Bounds }}, { ocTableRef, {{ Bounds }, 0, Value }}, // or Reference? { ocTableRefClose, {{ Bounds }, 0, Bounds }}, { ocTableRefItemAll, {{ Bounds }, 0, Bounds }}, @@ -574,6 +576,7 @@ void ScParameterClassification::GenerateDocumentation() case ocIfError: case ocIfNA: case ocChoose: + case ocLet: aToken.SetByte(2); break; case ocPercentSign: diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 668d8910b10c..2c63eb21a02d 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -221,6 +221,10 @@ void ScRawToken::SetOpCode( OpCode e ) eType = svJump; nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1; break; + case ocLet: + eType = svJump; + nJump[0] = SAL_MAX_UINT8; + break; case ocMissing: eType = svMissing; break; @@ -256,6 +260,15 @@ void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase ) sharedstring.mpDataIgnoreCase = pDataIgnoreCase; } +void ScRawToken::SetStringName( rtl_uString* pData, rtl_uString* pDataIgnoreCase ) +{ + eOp = ocStringName; + eType = svString; + + sharedstring.mpData = pData; + sharedstring.mpDataIgnoreCase = pDataIgnoreCase; +} + void ScRawToken::SetSingleReference( const ScSingleRefData& rRef ) { eOp = ocPush; @@ -1677,6 +1690,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r ) case ocIfError: case ocIfNA: case ocChoose: + case ocLet: // Jump commands are now supported. break; } @@ -5014,7 +5028,7 @@ void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OU case svString: { OUString aStr = rToken.GetString().getString(); - if (eOp == ocBad || eOp == ocStringXML) + if (eOp == ocBad || eOp == ocStringXML || eOp == ocStringName) { rBuf.append(aStr); return; diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx index 2432c96578bf..1982c3ba2dae 100644 --- a/sc/source/filter/excel/xlformula.cxx +++ b/sc/source/filter/excel/xlformula.cxx @@ -608,7 +608,8 @@ const XclFunctionInfo saFuncTable_2021[] = EXC_FUNCENTRY_V_VR( ocSortBy, 2, 3, 0, "SORTBY" ), EXC_FUNCENTRY_V_VR( ocMatSequence,1, 4, 0, "SEQUENCE" ), EXC_FUNCENTRY_V_VR( ocRandArray, 0, 5, 0, "RANDARRAY" ), - EXC_FUNCENTRY_V_VR( ocUnique, 1, 3, 0, "UNIQUE" ) + EXC_FUNCENTRY_V_VR( ocUnique, 1, 3, 0, "UNIQUE" ), + EXC_FUNCENTRY_V_VR( ocLet, 3, 3, 0, "SORTBY") }; diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx index 86742cfed64c..739fc213fd15 100644 --- a/sc/source/filter/oox/formulabase.cxx +++ b/sc/source/filter/oox/formulabase.cxx @@ -883,6 +883,7 @@ const FunctionData saFuncTable2021[] = { "COM.MICROSOFT.SEQUENCE", "SEQUENCE", NOID, NOID, 1, 4, A, { VO }, FuncFlags::MACROCALL_NEW }, { "COM.MICROSOFT.RANDARRAY", "RANDARRAY", NOID, NOID, 0, 5, A, { VO }, FuncFlags::MACROCALL_NEW }, { "COM.MICROSOFT.UNIQUE", "UNIQUE", NOID, NOID, 1, 3, A, { VO }, FuncFlags::MACROCALL_NEW }, + { "COM.MICROSOFT.LET", "LET", NOID, NOID, 3, MX, R, { VR, VR, VA }, FuncFlags::MACROCALL_NEW | FuncFlags::PARAMPAIRS }, };