tdf#137543 - Add new LET function to Calc
Add new LET function to Calc which assigns names to calculation results. TODO: oasis proposal Change-Id: Ia0d56a30751a44a72e364a28b64fd8f617e997dc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168349 Tested-by: Gabor Kelemen <gabor.kelemen.extern@allotropia.de> Tested-by: Jenkins Reviewed-by: Balazs Varga <balazs.varga.extern@allotropia.de>
This commit is contained in:
parent
200f74b800
commit
521a56d8d1
26 changed files with 4641 additions and 10 deletions
|
@ -282,6 +282,8 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] =
|
||||||
{ "COM.MICROSOFT.SORT" , SC_OPCODE_SORT },
|
{ "COM.MICROSOFT.SORT" , SC_OPCODE_SORT },
|
||||||
{ "COM.MICROSOFT.SORTBY" , SC_OPCODE_SORTBY },
|
{ "COM.MICROSOFT.SORTBY" , SC_OPCODE_SORTBY },
|
||||||
{ "COM.MICROSOFT.UNIQUE" , SC_OPCODE_UNIQUE },
|
{ "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)
|
{ "ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||||
{ "INDEX" , SC_OPCODE_INDEX },
|
{ "INDEX" , SC_OPCODE_INDEX },
|
||||||
|
@ -737,6 +739,8 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] =
|
||||||
{ "_xlfn._xlws.SORT" , SC_OPCODE_SORT },
|
{ "_xlfn._xlws.SORT" , SC_OPCODE_SORT },
|
||||||
{ "_xlfn.SORTBY" , SC_OPCODE_SORTBY },
|
{ "_xlfn.SORTBY" , SC_OPCODE_SORTBY },
|
||||||
{ "_xlfn.UNIQUE" , SC_OPCODE_UNIQUE },
|
{ "_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)
|
{ "_xlfn.ORG.OPENOFFICE.MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||||
{ "INDEX" , SC_OPCODE_INDEX },
|
{ "INDEX" , SC_OPCODE_INDEX },
|
||||||
|
@ -1195,6 +1199,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] =
|
||||||
{ "SORT" , SC_OPCODE_SORT },
|
{ "SORT" , SC_OPCODE_SORT },
|
||||||
{ "SORTBY" , SC_OPCODE_SORTBY },
|
{ "SORTBY" , SC_OPCODE_SORTBY },
|
||||||
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
||||||
|
{ "LET" , SC_OPCODE_LET },
|
||||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||||
{ "INDEX" , SC_OPCODE_INDEX },
|
{ "INDEX" , SC_OPCODE_INDEX },
|
||||||
|
@ -1654,6 +1659,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] =
|
||||||
{ "SORT" , SC_OPCODE_SORT },
|
{ "SORT" , SC_OPCODE_SORT },
|
||||||
{ "SORTBY" , SC_OPCODE_SORTBY },
|
{ "SORTBY" , SC_OPCODE_SORTBY },
|
||||||
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
||||||
|
{ "LET" , SC_OPCODE_LET },
|
||||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||||
{ "INDEX" , SC_OPCODE_INDEX }, // ?? first character = I ??
|
{ "INDEX" , SC_OPCODE_INDEX }, // ?? first character = I ??
|
||||||
|
@ -2111,6 +2117,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH[] =
|
||||||
{ "SORT" , SC_OPCODE_SORT },
|
{ "SORT" , SC_OPCODE_SORT },
|
||||||
{ "SORTBY" , SC_OPCODE_SORTBY },
|
{ "SORTBY" , SC_OPCODE_SORTBY },
|
||||||
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
{ "UNIQUE" , SC_OPCODE_UNIQUE },
|
||||||
|
{ "LET" , SC_OPCODE_LET },
|
||||||
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA },
|
{ "MULTIRANGE" , SC_OPCODE_MULTI_AREA },
|
||||||
{ "OFFSET" , SC_OPCODE_OFFSET },
|
{ "OFFSET" , SC_OPCODE_OFFSET },
|
||||||
{ "INDEX" , SC_OPCODE_INDEX },
|
{ "INDEX" , SC_OPCODE_INDEX },
|
||||||
|
@ -2549,6 +2556,7 @@ const std::pair<TranslateId, int> RID_STRLIST_FUNCTION_NAMES[] =
|
||||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "SORT") , SC_OPCODE_SORT },
|
{ NC_("RID_STRLIST_FUNCTION_NAMES", "SORT") , SC_OPCODE_SORT },
|
||||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "SORTBY") , SC_OPCODE_SORTBY },
|
{ NC_("RID_STRLIST_FUNCTION_NAMES", "SORTBY") , SC_OPCODE_SORTBY },
|
||||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "UNIQUE") , SC_OPCODE_UNIQUE },
|
{ 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", "MULTIRANGE") , SC_OPCODE_MULTI_AREA }, // legacy for range list (union)
|
||||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "OFFSET") , SC_OPCODE_OFFSET },
|
{ NC_("RID_STRLIST_FUNCTION_NAMES", "OFFSET") , SC_OPCODE_OFFSET },
|
||||||
{ NC_("RID_STRLIST_FUNCTION_NAMES", "INDEX") , SC_OPCODE_INDEX }, // ?? first character = I ??
|
{ NC_("RID_STRLIST_FUNCTION_NAMES", "INDEX") , SC_OPCODE_INDEX }, // ?? first character = I ??
|
||||||
|
|
|
@ -626,6 +626,7 @@ uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::create
|
||||||
SC_OPCODE_IF_ERROR,
|
SC_OPCODE_IF_ERROR,
|
||||||
SC_OPCODE_IF_NA,
|
SC_OPCODE_IF_NA,
|
||||||
SC_OPCODE_CHOOSE,
|
SC_OPCODE_CHOOSE,
|
||||||
|
SC_OPCODE_LET,
|
||||||
SC_OPCODE_AND,
|
SC_OPCODE_AND,
|
||||||
SC_OPCODE_OR
|
SC_OPCODE_OR
|
||||||
};
|
};
|
||||||
|
@ -1221,6 +1222,7 @@ bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
|
case ocLet:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -1269,6 +1271,7 @@ bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
|
||||||
case ocSortBy :
|
case ocSortBy :
|
||||||
case ocRandArray :
|
case ocRandArray :
|
||||||
case ocUnique :
|
case ocUnique :
|
||||||
|
case ocLet :
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -1565,6 +1568,11 @@ bool FormulaCompiler::GetToken()
|
||||||
case ocAggregate:
|
case ocAggregate:
|
||||||
glSubTotal = true;
|
glSubTotal = true;
|
||||||
break;
|
break;
|
||||||
|
case ocStringName:
|
||||||
|
if( HandleStringName())
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
case ocName:
|
case ocName:
|
||||||
if( HandleRange())
|
if( HandleRange())
|
||||||
{
|
{
|
||||||
|
@ -1946,6 +1954,9 @@ void FormulaCompiler::Factor()
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
||||||
break;
|
break;
|
||||||
|
case ocLet:
|
||||||
|
pFacToken->GetJump()[0] = SAL_MAX_UINT8 + 1;
|
||||||
|
break;
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
pFacToken->GetJump()[ 0 ] = 2; // if, behind
|
pFacToken->GetJump()[ 0 ] = 2; // if, behind
|
||||||
|
@ -1978,6 +1989,9 @@ void FormulaCompiler::Factor()
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
nJumpMax = FORMULA_MAXJUMPCOUNT;
|
nJumpMax = FORMULA_MAXJUMPCOUNT;
|
||||||
break;
|
break;
|
||||||
|
case ocLet:
|
||||||
|
nJumpMax = SAL_MAX_UINT8;
|
||||||
|
break;
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
nJumpMax = 2;
|
nJumpMax = 2;
|
||||||
|
@ -1993,7 +2007,7 @@ void FormulaCompiler::Factor()
|
||||||
assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
|
assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
|
||||||
}
|
}
|
||||||
short nJumpCount = 0;
|
short nJumpCount = 0;
|
||||||
while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
|
while ( (nJumpCount < (SAL_MAX_UINT8 - 1)) && (eOp == ocSep)
|
||||||
&& (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
|
&& (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
|
||||||
{
|
{
|
||||||
if ( ++nJumpCount <= nJumpMax )
|
if ( ++nJumpCount <= nJumpMax )
|
||||||
|
@ -2022,6 +2036,9 @@ void FormulaCompiler::Factor()
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
|
bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
|
||||||
break;
|
break;
|
||||||
|
case ocLet:
|
||||||
|
bLimitOk = (nJumpCount < SAL_MAX_UINT8);
|
||||||
|
break;
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
bLimitOk = (nJumpCount <= 2);
|
bLimitOk = (nJumpCount <= 2);
|
||||||
|
@ -2598,7 +2615,7 @@ const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuf
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case svString:
|
case svString:
|
||||||
if( eOp == ocBad || eOp == ocStringXML )
|
if( eOp == ocBad || eOp == ocStringXML || eOp == ocStringName )
|
||||||
rBuffer.append( t->GetString().getString());
|
rBuffer.append( t->GetString().getString());
|
||||||
else
|
else
|
||||||
AppendString( rBuffer, t->GetString().getString() );
|
AppendString( rBuffer, t->GetString().getString() );
|
||||||
|
@ -2920,6 +2937,11 @@ bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FormulaCompiler::HandleStringName()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool FormulaCompiler::HandleRange()
|
bool FormulaCompiler::HandleRange()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -435,6 +435,8 @@ bool FormulaTokenArray::AddFormulaToken(
|
||||||
AddBad( aStrVal );
|
AddBad( aStrVal );
|
||||||
else if ( eOpCode == ocStringXML )
|
else if ( eOpCode == ocStringXML )
|
||||||
AddStringXML( aStrVal );
|
AddStringXML( aStrVal );
|
||||||
|
else if ( eOpCode == ocStringName )
|
||||||
|
AddStringName( aStrVal );
|
||||||
else if ( eOpCode == ocExternal || eOpCode == ocMacro )
|
else if ( eOpCode == ocExternal || eOpCode == ocMacro )
|
||||||
Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) );
|
Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) );
|
||||||
else if ( eOpCode == ocWhitespace )
|
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
|
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 )
|
void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
|
||||||
{
|
{
|
||||||
|
@ -1576,12 +1582,15 @@ FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
|
case ocLet:
|
||||||
{
|
{
|
||||||
short nJump[FORMULA_MAXJUMPCOUNT + 1];
|
short nJump[SAL_MAX_UINT8 + 1];
|
||||||
if ( eOp == ocIf )
|
if ( eOp == ocIf )
|
||||||
nJump[ 0 ] = 3;
|
nJump[ 0 ] = 3;
|
||||||
else if ( eOp == ocChoose )
|
else if ( eOp == ocChoose )
|
||||||
nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
||||||
|
else if ( eOp == ocLet )
|
||||||
|
nJump[0] = SAL_MAX_UINT8 + 1;
|
||||||
else
|
else
|
||||||
nJump[ 0 ] = 2;
|
nJump[ 0 ] = 2;
|
||||||
pRet = new FormulaJumpToken( eOp, nJump );
|
pRet = new FormulaJumpToken( eOp, nJump );
|
||||||
|
@ -1660,6 +1669,20 @@ FormulaToken* FormulaTokenArrayPlainIterator::GetNextName()
|
||||||
return nullptr;
|
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* FormulaTokenIterator::Next()
|
||||||
{
|
{
|
||||||
const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC );
|
const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC );
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <strings.hrc>
|
#include <strings.hrc>
|
||||||
#include <bitmaps.hlst>
|
#include <bitmaps.hlst>
|
||||||
#include <core_resource.hxx>
|
#include <core_resource.hxx>
|
||||||
|
#include <rtl/math.hxx>
|
||||||
|
|
||||||
namespace formula
|
namespace formula
|
||||||
{
|
{
|
||||||
|
@ -150,7 +151,10 @@ void ParaWin::UpdateArgDesc( sal_uInt16 nArg )
|
||||||
aArgName = pFuncDesc->getParameterName(nRealArg);
|
aArgName = pFuncDesc->getParameterName(nRealArg);
|
||||||
sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
|
sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
|
||||||
if ( nArg >= nVarArgsStart )
|
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) ;
|
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();
|
sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
|
||||||
if ( nArg >= nVarArgsStart )
|
if ( nArg >= nVarArgsStart )
|
||||||
{
|
{
|
||||||
|
sal_Int16 nShifted = pFuncDesc->getFunctionName().equalsIgnoreAsciiCase(u"LET") ? nPos / 2 : 0;
|
||||||
OUString aArgName = pFuncDesc->getParameterName(nRealArg) +
|
OUString aArgName = pFuncDesc->getParameterName(nRealArg) +
|
||||||
OUString::number( (nArg-nVarArgsStart)/2 + 1 );
|
OUString::number( (nArg-nVarArgsStart)/2 + 1 + nShifted);
|
||||||
SetArgName( i, aArgName );
|
SetArgName( i, aArgName );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -344,6 +344,7 @@ protected:
|
||||||
virtual void SetError(FormulaError nError);
|
virtual void SetError(FormulaError nError);
|
||||||
virtual FormulaTokenRef ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 );
|
virtual FormulaTokenRef ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 );
|
||||||
virtual bool HandleExternalReference(const FormulaToken& _aToken);
|
virtual bool HandleExternalReference(const FormulaToken& _aToken);
|
||||||
|
virtual bool HandleStringName();
|
||||||
virtual bool HandleRange();
|
virtual bool HandleRange();
|
||||||
virtual bool HandleColRowName();
|
virtual bool HandleColRowName();
|
||||||
virtual bool HandleDbData();
|
virtual bool HandleDbData();
|
||||||
|
@ -419,6 +420,12 @@ protected:
|
||||||
bool mbComputeII; // whether to attempt computing implicit intersection ranges while building the RPN array.
|
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)
|
bool mbMatrixFlag; // whether the formula is a matrix formula (needed for II computation)
|
||||||
|
|
||||||
|
struct LambdaFunc
|
||||||
|
{
|
||||||
|
bool bInLambdaFunction = false;
|
||||||
|
short nBracketPos = 0;
|
||||||
|
} mLambda;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum InitSymbols
|
enum InitSymbols
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
#define SC_OPCODE_TABLE_REF_ITEM_THIS_ROW 35
|
#define SC_OPCODE_TABLE_REF_ITEM_THIS_ROW 35
|
||||||
#define SC_OPCODE_STOP_DIV 36
|
#define SC_OPCODE_STOP_DIV 36
|
||||||
#define SC_OPCODE_SKIP 37 /* used to skip raw tokens during string compilation */
|
#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 #... ***/
|
/*** error constants #... ***/
|
||||||
#define SC_OPCODE_START_ERRORS 40
|
#define SC_OPCODE_START_ERRORS 40
|
||||||
|
|
|
@ -38,6 +38,7 @@ enum OpCode : sal_uInt16
|
||||||
ocIfError = SC_OPCODE_IF_ERROR,
|
ocIfError = SC_OPCODE_IF_ERROR,
|
||||||
ocIfNA = SC_OPCODE_IF_NA,
|
ocIfNA = SC_OPCODE_IF_NA,
|
||||||
ocChoose = SC_OPCODE_CHOOSE,
|
ocChoose = SC_OPCODE_CHOOSE,
|
||||||
|
ocLet = SC_OPCODE_LET,
|
||||||
// Parentheses and separators
|
// Parentheses and separators
|
||||||
ocOpen = SC_OPCODE_OPEN,
|
ocOpen = SC_OPCODE_OPEN,
|
||||||
ocClose = SC_OPCODE_CLOSE,
|
ocClose = SC_OPCODE_CLOSE,
|
||||||
|
@ -61,6 +62,7 @@ enum OpCode : sal_uInt16
|
||||||
ocTableRefItemTotals = SC_OPCODE_TABLE_REF_ITEM_TOTALS,
|
ocTableRefItemTotals = SC_OPCODE_TABLE_REF_ITEM_TOTALS,
|
||||||
ocTableRefItemThisRow = SC_OPCODE_TABLE_REF_ITEM_THIS_ROW,
|
ocTableRefItemThisRow = SC_OPCODE_TABLE_REF_ITEM_THIS_ROW,
|
||||||
ocSkip = SC_OPCODE_SKIP,
|
ocSkip = SC_OPCODE_SKIP,
|
||||||
|
ocStringName = SC_OPCODE_STRINGNAME,
|
||||||
// Access commands
|
// Access commands
|
||||||
ocDBArea = SC_OPCODE_DB_AREA,
|
ocDBArea = SC_OPCODE_DB_AREA,
|
||||||
ocTableRef = SC_OPCODE_TABLE_REF,
|
ocTableRef = SC_OPCODE_TABLE_REF,
|
||||||
|
@ -553,6 +555,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
|
||||||
case ocMissing: return "Missing";
|
case ocMissing: return "Missing";
|
||||||
case ocBad: return "Bad";
|
case ocBad: return "Bad";
|
||||||
case ocStringXML: return "StringXML";
|
case ocStringXML: return "StringXML";
|
||||||
|
case ocStringName: return "StringName";
|
||||||
case ocSpaces: return "Spaces";
|
case ocSpaces: return "Spaces";
|
||||||
case ocWhitespace: return "Whitespace";
|
case ocWhitespace: return "Whitespace";
|
||||||
case ocMatRef: return "MatRef";
|
case ocMatRef: return "MatRef";
|
||||||
|
@ -996,6 +999,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
|
||||||
case ocSort: return "Sort";
|
case ocSort: return "Sort";
|
||||||
case ocSortBy: return "SortBy";
|
case ocSortBy: return "SortBy";
|
||||||
case ocUnique: return "Unique";
|
case ocUnique: return "Unique";
|
||||||
|
case ocLet: return "Let";
|
||||||
case ocTTT: return "TTT";
|
case ocTTT: return "TTT";
|
||||||
case ocDebugVar: return "DebugVar";
|
case ocDebugVar: return "DebugVar";
|
||||||
case ocDataToken1: return "DataToken1";
|
case ocDataToken1: return "DataToken1";
|
||||||
|
|
|
@ -487,6 +487,7 @@ public:
|
||||||
FormulaToken* AddExternal( const OUString& rStr, OpCode eOp = ocExternal );
|
FormulaToken* AddExternal( const OUString& rStr, OpCode eOp = ocExternal );
|
||||||
FormulaToken* AddBad( const OUString& rStr ); /// ocBad with OUString
|
FormulaToken* AddBad( const OUString& rStr ); /// ocBad with OUString
|
||||||
FormulaToken* AddStringXML( const OUString& rStr ); /// ocStringXML with OUString, temporary during import
|
FormulaToken* AddStringXML( const OUString& rStr ); /// ocStringXML with OUString, temporary during import
|
||||||
|
FormulaToken* AddStringName( const OUString& rStr ); /// ocStringName with OUString - Lambda functions
|
||||||
|
|
||||||
virtual FormulaToken* MergeArray( );
|
virtual FormulaToken* MergeArray( );
|
||||||
|
|
||||||
|
@ -578,6 +579,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SAL_DLLPRIVATE const FormulaToken* GetNonEndOfPathToken( short nIdx ) const;
|
SAL_DLLPRIVATE const FormulaToken* GetNonEndOfPathToken( short nIdx ) const;
|
||||||
|
SAL_DLLPRIVATE const FormulaToken* GetNonEndOfPathToken2( short nIdx ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// For use in SAL_INFO, SAL_WARN etc
|
// For use in SAL_INFO, SAL_WARN etc
|
||||||
|
@ -639,6 +641,7 @@ public:
|
||||||
FormulaToken* Next();
|
FormulaToken* Next();
|
||||||
FormulaToken* NextNoSpaces();
|
FormulaToken* NextNoSpaces();
|
||||||
FormulaToken* GetNextName();
|
FormulaToken* GetNextName();
|
||||||
|
FormulaToken* GetNextStringName();
|
||||||
FormulaToken* GetNextReference();
|
FormulaToken* GetNextReference();
|
||||||
FormulaToken* GetNextReferenceRPN();
|
FormulaToken* GetNextReferenceRPN();
|
||||||
FormulaToken* GetNextReferenceOrName();
|
FormulaToken* GetNextReferenceOrName();
|
||||||
|
|
|
@ -70,6 +70,8 @@ https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocume
|
||||||
* Information Functions
|
* Information Functions
|
||||||
* COUNTIF
|
* COUNTIF
|
||||||
* COUNTIFS
|
* COUNTIFS
|
||||||
|
* Lambda Functions
|
||||||
|
* LET
|
||||||
* Lookup Functions
|
* Lookup Functions
|
||||||
* HLOOKUP
|
* HLOOKUP
|
||||||
* LOOKUP
|
* LOOKUP
|
||||||
|
|
|
@ -155,6 +155,7 @@ public:
|
||||||
// since the reference count is cleared!
|
// since the reference count is cleared!
|
||||||
void SetOpCode( OpCode eCode );
|
void SetOpCode( OpCode eCode );
|
||||||
void SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase );
|
void SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase );
|
||||||
|
void SetStringName( rtl_uString* pData, rtl_uString* pDataIgnoreCase );
|
||||||
void SetSingleReference( const ScSingleRefData& rRef );
|
void SetSingleReference( const ScSingleRefData& rRef );
|
||||||
void SetDoubleReference( const ScComplexRefData& rRef );
|
void SetDoubleReference( const ScComplexRefData& rRef );
|
||||||
void SetDouble( double fVal );
|
void SetDouble( double fVal );
|
||||||
|
@ -358,6 +359,7 @@ private:
|
||||||
bool ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef );
|
bool ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef );
|
||||||
bool ParseMacro( const OUString& );
|
bool ParseMacro( const OUString& );
|
||||||
bool ParseNamedRange( const OUString&, bool onlyCheck = false );
|
bool ParseNamedRange( const OUString&, bool onlyCheck = false );
|
||||||
|
bool ParseLambdaFuncName( const OUString&, bool bLambdaFunction = false );
|
||||||
bool ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange );
|
bool ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange );
|
||||||
bool ParseDBRange( const OUString& );
|
bool ParseDBRange( const OUString& );
|
||||||
bool ParseColRowName( 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 void fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const override;
|
||||||
|
|
||||||
virtual bool HandleExternalReference(const formula::FormulaToken& _aToken) override;
|
virtual bool HandleExternalReference(const formula::FormulaToken& _aToken) override;
|
||||||
|
virtual bool HandleStringName() override;
|
||||||
virtual bool HandleRange() override;
|
virtual bool HandleRange() override;
|
||||||
virtual bool HandleColRowName() override;
|
virtual bool HandleColRowName() override;
|
||||||
virtual bool HandleDbData() override;
|
virtual bool HandleDbData() override;
|
||||||
|
|
|
@ -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_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_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_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: */
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||||
|
|
|
@ -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.")
|
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: */
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||||
|
|
|
@ -77,7 +77,7 @@ public:
|
||||||
ScFunctionListObj::ScFunctionListObj()
|
ScFunctionListObj::ScFunctionListObj()
|
||||||
: UnoApiTest(u"/sc/qa/extras/testdocuments"_ustr)
|
: UnoApiTest(u"/sc/qa/extras/testdocuments"_ustr)
|
||||||
, XElementAccess(cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get())
|
, XElementAccess(cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get())
|
||||||
, XIndexAccess(403)
|
, XIndexAccess(404)
|
||||||
, XNameAccess("IF")
|
, XNameAccess("IF")
|
||||||
, XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions")
|
, XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions")
|
||||||
{
|
{
|
||||||
|
|
4308
sc/qa/unit/data/functions/spreadsheet/fods/let.fods
Normal file
4308
sc/qa/unit/data/functions/spreadsheet/fods/let.fods
Normal file
File diff suppressed because one or more lines are too long
BIN
sc/qa/unit/data/xlsx/tdf137543.xlsx
Normal file
BIN
sc/qa/unit/data/xlsx/tdf137543.xlsx
Normal file
Binary file not shown.
|
@ -1311,6 +1311,20 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf91332)
|
||||||
CPPUNIT_ASSERT_EQUAL(Color(0x90cf47), nColor);
|
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();
|
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||||
|
|
||||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||||
|
|
|
@ -2917,6 +2917,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFunctionLists)
|
||||||
"HYPERLINK",
|
"HYPERLINK",
|
||||||
"INDEX",
|
"INDEX",
|
||||||
"INDIRECT",
|
"INDIRECT",
|
||||||
|
"LET",
|
||||||
"LOOKUP",
|
"LOOKUP",
|
||||||
"MATCH",
|
"MATCH",
|
||||||
"OFFSET",
|
"OFFSET",
|
||||||
|
|
|
@ -197,12 +197,12 @@ OUString ScFuncDesc::GetParamList() const
|
||||||
aSig.append(maDefArgNames[nVarArgsStart]
|
aSig.append(maDefArgNames[nVarArgsStart]
|
||||||
+ "1" + sep
|
+ "1" + sep
|
||||||
+ maDefArgNames[nVarArgsStart+1]
|
+ maDefArgNames[nVarArgsStart+1]
|
||||||
+ "1" + sep
|
+ (mxFuncName != "LET" ? "1" : "2") + sep
|
||||||
+ " "
|
+ " "
|
||||||
+ maDefArgNames[nVarArgsStart]
|
+ maDefArgNames[nVarArgsStart]
|
||||||
+ "2" + sep
|
+ "2" + sep
|
||||||
+ maDefArgNames[nVarArgsStart+1]
|
+ 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_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_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_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_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_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 },
|
{ SC_OPCODE_PI, ENTRY(SC_OPCODE_PI_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_PI, 0, { }, 0 },
|
||||||
|
|
|
@ -190,6 +190,7 @@ struct FormulaTokenRef_hash
|
||||||
{ return std::hash<const void *>()(static_cast<const void*>(p1)); }
|
{ return std::hash<const void *>()(static_cast<const void*>(p1)); }
|
||||||
};
|
};
|
||||||
typedef ::std::unordered_map< const formula::FormulaConstTokenRef, formula::FormulaConstTokenRef, FormulaTokenRef_hash> ScTokenMatrixMap;
|
typedef ::std::unordered_map< const formula::FormulaConstTokenRef, formula::FormulaConstTokenRef, FormulaTokenRef_hash> ScTokenMatrixMap;
|
||||||
|
typedef ::std::unordered_map< OUString, const formula::FormulaConstTokenRef> ScResultTokenMap;
|
||||||
|
|
||||||
class ScInterpreter
|
class ScInterpreter
|
||||||
{
|
{
|
||||||
|
@ -246,6 +247,7 @@ private:
|
||||||
formula::FormulaConstTokenRef xResult;
|
formula::FormulaConstTokenRef xResult;
|
||||||
ScJumpMatrix* pJumpMatrix; // currently active array condition, if any
|
ScJumpMatrix* pJumpMatrix; // currently active array condition, if any
|
||||||
ScTokenMatrixMap maTokenMatrixMap; // map FormulaToken* to formula::FormulaTokenRef if in array condition
|
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
|
ScFormulaCell* pMyFormulaCell; // the cell of this formula expression
|
||||||
|
|
||||||
const formula::FormulaToken* pCur; // current token
|
const formula::FormulaToken* pCur; // current token
|
||||||
|
@ -513,6 +515,11 @@ private:
|
||||||
ScMatrixRef GetMatrix( short & rParam, size_t & rInRefList );
|
ScMatrixRef GetMatrix( short & rParam, size_t & rInRefList );
|
||||||
sc::RangeMatrix GetRangeMatrix();
|
sc::RangeMatrix GetRangeMatrix();
|
||||||
|
|
||||||
|
// Get tokens at specific parameters for LET (lambda) function
|
||||||
|
void getTokensAtParameter( std::unique_ptr<ScTokenArray>& pTokens, short nPos );
|
||||||
|
static void replaceNamesToResult( const std::unordered_map<OUString, formula::FormulaToken*> nResultIndexes,
|
||||||
|
std::unique_ptr<ScTokenArray>& pTokens );
|
||||||
|
|
||||||
void ScTableOp(); // repeated operations
|
void ScTableOp(); // repeated operations
|
||||||
|
|
||||||
// common helper functions
|
// common helper functions
|
||||||
|
@ -717,6 +724,7 @@ private:
|
||||||
void ScSort();
|
void ScSort();
|
||||||
void ScSortBy();
|
void ScSortBy();
|
||||||
void ScUnique();
|
void ScUnique();
|
||||||
|
void ScLet();
|
||||||
void ScSubTotal();
|
void ScSubTotal();
|
||||||
|
|
||||||
// If upon call rMissingField==true then the database field parameter may be
|
// If upon call rMissingField==true then the database field parameter may be
|
||||||
|
|
|
@ -74,6 +74,8 @@
|
||||||
#include <scmatrix.hxx>
|
#include <scmatrix.hxx>
|
||||||
#include <tokenstringcontext.hxx>
|
#include <tokenstringcontext.hxx>
|
||||||
#include <officecfg/Office/Common.hxx>
|
#include <officecfg/Office/Common.hxx>
|
||||||
|
#include <sfx2/linkmgr.hxx>
|
||||||
|
#include <interpre.hxx>
|
||||||
|
|
||||||
using namespace formula;
|
using namespace formula;
|
||||||
using namespace ::com::sun::star;
|
using namespace ::com::sun::star;
|
||||||
|
@ -3651,6 +3653,20 @@ bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck )
|
||||||
return false;
|
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 )
|
bool ScCompiler::ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
|
||||||
{
|
{
|
||||||
/* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
|
/* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
|
||||||
|
@ -4613,6 +4629,9 @@ Label_Rewind:
|
||||||
if (bMayBeFuncName && ParseOpCode2( aUpper ))
|
if (bMayBeFuncName && ParseOpCode2( aUpper ))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (ParseLambdaFuncName(aOrg, mLambda.bInLambdaFunction))
|
||||||
|
return true;
|
||||||
|
|
||||||
} while (mbRewind);
|
} while (mbRewind);
|
||||||
|
|
||||||
// Last chance: it could be a broken invalidated reference that contains
|
// Last chance: it could be a broken invalidated reference that contains
|
||||||
|
@ -4743,6 +4762,12 @@ std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormul
|
||||||
{
|
{
|
||||||
case ocOpen:
|
case ocOpen:
|
||||||
{
|
{
|
||||||
|
if (eLastOp == ocLet)
|
||||||
|
{
|
||||||
|
mLambda.bInLambdaFunction = true;
|
||||||
|
mLambda.nBracketPos = nBrackets;
|
||||||
|
}
|
||||||
|
|
||||||
++nBrackets;
|
++nBrackets;
|
||||||
if (bUseFunctionStack)
|
if (bUseFunctionStack)
|
||||||
{
|
{
|
||||||
|
@ -4765,7 +4790,14 @@ std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
nBrackets--;
|
nBrackets--;
|
||||||
|
if (mLambda.bInLambdaFunction && mLambda.nBracketPos == nBrackets)
|
||||||
|
{
|
||||||
|
mLambda.bInLambdaFunction = false;
|
||||||
|
mLambda.nBracketPos = nBrackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (bUseFunctionStack && nFunction)
|
if (bUseFunctionStack && nFunction)
|
||||||
--nFunction;
|
--nFunction;
|
||||||
}
|
}
|
||||||
|
@ -5033,6 +5065,14 @@ ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const
|
||||||
return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
|
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()
|
bool ScCompiler::HandleRange()
|
||||||
{
|
{
|
||||||
ScTokenArray* pNew;
|
ScTokenArray* pNew;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include <document.hxx>
|
#include <document.hxx>
|
||||||
#include <dociter.hxx>
|
#include <dociter.hxx>
|
||||||
#include <docsh.hxx>
|
#include <docsh.hxx>
|
||||||
|
#include <sfx2/linkmgr.hxx>
|
||||||
#include <formulacell.hxx>
|
#include <formulacell.hxx>
|
||||||
#include <scmatrix.hxx>
|
#include <scmatrix.hxx>
|
||||||
#include <docoptio.hxx>
|
#include <docoptio.hxx>
|
||||||
|
@ -8917,6 +8918,152 @@ void ScInterpreter::ScUnique()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScInterpreter::getTokensAtParameter( std::unique_ptr<ScTokenArray>& 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<OUString, formula::FormulaToken*> nResultIndexes,
|
||||||
|
std::unique_ptr<ScTokenArray>& 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<OUString, formula::FormulaToken*> nResultIndexes;
|
||||||
|
formula::FormulaTokenArrayPlainIterator aIter(*pArr);
|
||||||
|
unique_ptr<ScTokenArray> 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<short>(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()
|
void ScInterpreter::ScSubTotal()
|
||||||
{
|
{
|
||||||
sal_uInt8 nParamCount = GetByte();
|
sal_uInt8 nParamCount = GetByte();
|
||||||
|
|
|
@ -4162,6 +4162,7 @@ StackVar ScInterpreter::Interpret()
|
||||||
case ocSort : ScSort(); break;
|
case ocSort : ScSort(); break;
|
||||||
case ocSortBy : ScSortBy(); break;
|
case ocSortBy : ScSortBy(); break;
|
||||||
case ocUnique : ScUnique(); break;
|
case ocUnique : ScUnique(); break;
|
||||||
|
case ocLet : ScLet(); break;
|
||||||
case ocTrue : ScTrue(); break;
|
case ocTrue : ScTrue(); break;
|
||||||
case ocFalse : ScFalse(); break;
|
case ocFalse : ScFalse(); break;
|
||||||
case ocGetActDate : ScGetActDate(); break;
|
case ocGetActDate : ScGetActDate(); break;
|
||||||
|
|
|
@ -57,6 +57,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
|
||||||
{ ocIfError, {{ Array, Reference }, 0, Value }},
|
{ ocIfError, {{ Array, Reference }, 0, Value }},
|
||||||
{ ocIfNA, {{ Array, Reference }, 0, Value }},
|
{ ocIfNA, {{ Array, Reference }, 0, Value }},
|
||||||
{ ocChoose, {{ Array, Reference }, 1, Value }},
|
{ ocChoose, {{ Array, Reference }, 1, Value }},
|
||||||
|
{ ocLet, {{ Value, ReferenceOrRefArray, ReferenceOrRefArray, }, 2, ForceArrayReturn } },
|
||||||
// Other specials.
|
// Other specials.
|
||||||
{ ocArrayClose, {{ Bounds }, 0, Bounds }},
|
{ ocArrayClose, {{ Bounds }, 0, Bounds }},
|
||||||
{ ocArrayColSep, {{ Bounds }, 0, Bounds }},
|
{ ocArrayColSep, {{ Bounds }, 0, Bounds }},
|
||||||
|
@ -76,6 +77,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
|
||||||
{ ocSpaces, {{ Bounds }, 0, Bounds }},
|
{ ocSpaces, {{ Bounds }, 0, Bounds }},
|
||||||
{ ocStop, {{ Bounds }, 0, Bounds }},
|
{ ocStop, {{ Bounds }, 0, Bounds }},
|
||||||
{ ocStringXML, {{ Bounds }, 0, Bounds }},
|
{ ocStringXML, {{ Bounds }, 0, Bounds }},
|
||||||
|
{ ocStringName, {{ Bounds }, 0, Bounds }},
|
||||||
{ ocTableRef, {{ Bounds }, 0, Value }}, // or Reference?
|
{ ocTableRef, {{ Bounds }, 0, Value }}, // or Reference?
|
||||||
{ ocTableRefClose, {{ Bounds }, 0, Bounds }},
|
{ ocTableRefClose, {{ Bounds }, 0, Bounds }},
|
||||||
{ ocTableRefItemAll, {{ Bounds }, 0, Bounds }},
|
{ ocTableRefItemAll, {{ Bounds }, 0, Bounds }},
|
||||||
|
@ -574,6 +576,7 @@ void ScParameterClassification::GenerateDocumentation()
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
|
case ocLet:
|
||||||
aToken.SetByte(2);
|
aToken.SetByte(2);
|
||||||
break;
|
break;
|
||||||
case ocPercentSign:
|
case ocPercentSign:
|
||||||
|
|
|
@ -221,6 +221,10 @@ void ScRawToken::SetOpCode( OpCode e )
|
||||||
eType = svJump;
|
eType = svJump;
|
||||||
nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
||||||
break;
|
break;
|
||||||
|
case ocLet:
|
||||||
|
eType = svJump;
|
||||||
|
nJump[0] = SAL_MAX_UINT8;
|
||||||
|
break;
|
||||||
case ocMissing:
|
case ocMissing:
|
||||||
eType = svMissing;
|
eType = svMissing;
|
||||||
break;
|
break;
|
||||||
|
@ -256,6 +260,15 @@ void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
|
||||||
sharedstring.mpDataIgnoreCase = 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 )
|
void ScRawToken::SetSingleReference( const ScSingleRefData& rRef )
|
||||||
{
|
{
|
||||||
eOp = ocPush;
|
eOp = ocPush;
|
||||||
|
@ -1677,6 +1690,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
|
||||||
case ocIfError:
|
case ocIfError:
|
||||||
case ocIfNA:
|
case ocIfNA:
|
||||||
case ocChoose:
|
case ocChoose:
|
||||||
|
case ocLet:
|
||||||
// Jump commands are now supported.
|
// Jump commands are now supported.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -5014,7 +5028,7 @@ void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OU
|
||||||
case svString:
|
case svString:
|
||||||
{
|
{
|
||||||
OUString aStr = rToken.GetString().getString();
|
OUString aStr = rToken.GetString().getString();
|
||||||
if (eOp == ocBad || eOp == ocStringXML)
|
if (eOp == ocBad || eOp == ocStringXML || eOp == ocStringName)
|
||||||
{
|
{
|
||||||
rBuf.append(aStr);
|
rBuf.append(aStr);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -608,7 +608,8 @@ const XclFunctionInfo saFuncTable_2021[] =
|
||||||
EXC_FUNCENTRY_V_VR( ocSortBy, 2, 3, 0, "SORTBY" ),
|
EXC_FUNCENTRY_V_VR( ocSortBy, 2, 3, 0, "SORTBY" ),
|
||||||
EXC_FUNCENTRY_V_VR( ocMatSequence,1, 4, 0, "SEQUENCE" ),
|
EXC_FUNCENTRY_V_VR( ocMatSequence,1, 4, 0, "SEQUENCE" ),
|
||||||
EXC_FUNCENTRY_V_VR( ocRandArray, 0, 5, 0, "RANDARRAY" ),
|
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")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -883,6 +883,7 @@ const FunctionData saFuncTable2021[] =
|
||||||
{ "COM.MICROSOFT.SEQUENCE", "SEQUENCE", NOID, NOID, 1, 4, A, { VO }, FuncFlags::MACROCALL_NEW },
|
{ "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.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.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 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue