summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx66
-rw-r--r--formula/source/core/api/token.cxx2
2 files changed, 55 insertions, 13 deletions
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index e74284ae89f2..4b6495826d48 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -2783,19 +2783,25 @@ formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken*
void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
{
- if (rCurr->GetInForceArray() != ParamClass::Unknown)
- // Already set, unnecessary to evaluate again. This happens by calls to
- // CurrentFactor::operator=() while descending through Factor() and
- // then ascending back (and down and up, ...),
- // CheckSetForceArrayParameter() and later PutCode().
+ if (pCurrentFactorToken.get() == rCurr.get())
return;
const OpCode eOp = rCurr->GetOpCode();
const StackVar eType = rCurr->GetType();
- bool bInlineArray = false;
- if (!(eOp != ocPush && (eType == svByte || eType == svJump))
- && !(bInlineArray = (eOp == ocPush && eType == svMatrix)))
- return;
+ const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
+
+ if (!bInlineArray)
+ {
+ if (rCurr->GetInForceArray() != ParamClass::Unknown)
+ // Already set, unnecessary to evaluate again. This happens by calls to
+ // CurrentFactor::operator=() while descending through Factor() and
+ // then ascending back (and down and up, ...),
+ // CheckSetForceArrayParameter() and later PutCode().
+ return;
+
+ if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
+ return;
+ }
// Return class for inline arrays and functions returning array/matrix.
// It's somewhat unclear what Excel actually does there and in
@@ -2815,7 +2821,8 @@ void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
if (bInlineArray)
{
- // rCurr->SetInForceArray() can not be used with ocPush.
+ // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
+ // with svMatrix has an implicit ParamClass::ForceArrayReturn.
if (nCurrentFactorParam > 0 && pCurrentFactorToken
&& pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
&& GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
@@ -2828,15 +2835,50 @@ void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
return;
}
- if (!pCurrentFactorToken || (pCurrentFactorToken.get() == rCurr.get()))
+ if (!pCurrentFactorToken)
{
- if (!pCurrentFactorToken && mbMatrixFlag)
+ if (mbMatrixFlag)
{
// An array/matrix formula acts as ForceArray on all top level
// operators and function calls, so that can be inherited properly
// below.
rCurr->SetInForceArray( ParamClass::ForceArray);
}
+ else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
+ {
+ // Binary operators are not functions followed by arguments
+ // and need some peeking into RPN to inspect their operands.
+ // Note that array context is not forced if only one
+ // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
+ // if entered in column A and not input in array mode, because it
+ // involves a range reference with an implicit intersection. Check
+ // both arguments are arrays, or the other is ocPush without ranges
+ // for "={1;2}+3" or "={1;2}+A1".
+ // Note this does not catch "={1;2}+ABS(A1)" that could be forced
+ // to array, user still has to close in array mode.
+ // The IsMatrixFunction() is only necessary because not all
+ // functions returning matrix have ForceArrayReturn (yet?), see
+ // OOXML comment above.
+
+ const OpCode eOp1 = pCode[-1]->GetOpCode();
+ const OpCode eOp2 = pCode[-2]->GetOpCode();
+ const bool b1 = (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp1));
+ const bool b2 = (pCode[-2]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp2));
+ if ((b1 && b2)
+ || (b1 && eOp2 == ocPush && pCode[-2]->GetType() != svDoubleRef)
+ || (b2 && eOp1 == ocPush && pCode[-1]->GetType() != svDoubleRef))
+ {
+ rCurr->SetInForceArray( eArrayReturn);
+ }
+ }
+ else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
+ {
+ // Similar for unary operators.
+ if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
+ {
+ rCurr->SetInForceArray( eArrayReturn);
+ }
+ }
return;
}
diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index 87b0fb67ca98..2ca7af78f963 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -175,7 +175,7 @@ void FormulaToken::SetByte( sal_uInt8 )
ParamClass FormulaToken::GetInForceArray() const
{
// ok to be called for any derived class
- return ParamClass::Unknown;
+ return (eOp == ocPush && eType == svMatrix) ? ParamClass::ForceArrayReturn : ParamClass::Unknown;
}
void FormulaToken::SetInForceArray( ParamClass )