sc: add @ prefix operator for opting out of array spill
The @ operator collapses an array operand to its upper-left value. It is the implicit-intersection prefix used to opt a formula out of the dynamic-array spill behaviour. =@SEQUENCE(4) returns 1 instead of the four-row spill. The new ocSingleValue opcode joins the unary-operator range so the existing compile and interpret machinery treats it like any other unary prefix. The @ character stops being part of name syntax in the OOXML grammar, so sheet names that contain @ have to be quoted from now on. In-session only for now. The operator is parsed and interpreted but not yet serialised into XLSX or ODS. Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> Change-Id: I18cf56e34f933ebb7f6e43c157618c9fe28dbe0f Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4172 Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit fbd04d7c394ad7445a667bf63820f88551b176e1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/206673 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
9537ccc6fe
commit
6f1fb25a94
@@ -68,6 +68,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] =
|
||||
{ "NOT" , ocNot },
|
||||
{ "NEG" , ocNeg },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ "PI" , ocPi },
|
||||
{ "RAND" , ocRandom },
|
||||
{ "TRUE" , ocTrue },
|
||||
@@ -540,6 +541,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] =
|
||||
{ "NOT" , ocNot },
|
||||
{ "NEG" , ocNeg },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ "PI" , ocPi },
|
||||
{ "RAND" , ocRandom },
|
||||
{ "TRUE" , ocTrue },
|
||||
@@ -1017,6 +1019,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] =
|
||||
{ "NOT" , ocNot },
|
||||
{ "NEG" , ocNeg },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ "PI" , ocPi },
|
||||
{ "RAND" , ocRandom },
|
||||
{ "TRUE" , ocTrue },
|
||||
@@ -1492,6 +1495,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] =
|
||||
{ "NOT" , ocNot },
|
||||
{ "NEG" , ocNeg },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ "PI" , ocPi },
|
||||
{ "RAND" , ocRandom },
|
||||
{ "TRUE" , ocTrue },
|
||||
@@ -1965,6 +1969,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH[] =
|
||||
{ "NOT" , ocNot },
|
||||
{ "NEG" , ocNeg },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ "PI" , ocPi },
|
||||
{ "RAND" , ocRandom },
|
||||
{ "TRUE" , ocTrue },
|
||||
@@ -2892,6 +2897,7 @@ const std::pair<const char *, int> RID_STRLIST_FUNCTION_NAMES_SYMBOLS[] =
|
||||
{ "~" , ocUnion },
|
||||
{ ":" , ocRange },
|
||||
{ "-" , ocNegSub },
|
||||
{ "@" , ocSingleValue },
|
||||
{ nullptr, -1 }
|
||||
};
|
||||
|
||||
|
||||
@@ -113,7 +113,9 @@ enum OpCode : sal_uInt16
|
||||
// Unary operators
|
||||
ocStartUnaryOperators = 70,
|
||||
ocNegSub = 70,
|
||||
ocStopUnaryOperators = 71,
|
||||
// The @ single-value prefix operator (implicit intersection).
|
||||
ocSingleValue = 71,
|
||||
ocStopUnaryOperators = 72,
|
||||
|
||||
ocStartFunction = 75,
|
||||
|
||||
@@ -646,6 +648,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
|
||||
case ocNot: return "Not";
|
||||
case ocNeg: return "Neg";
|
||||
case ocNegSub: return "NegSub";
|
||||
case ocSingleValue: return "SingleValue";
|
||||
case ocPi: return "Pi";
|
||||
case ocRandom: return "Random";
|
||||
case ocRandomNV: return "RandomNV";
|
||||
|
||||
@@ -5767,6 +5767,23 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testDynamicArrayFlagCopy)
|
||||
m_pDoc->DeleteTab(0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testSingleValueOperator)
|
||||
{
|
||||
// The @ prefix collapses an array-returning function to its upper-left
|
||||
// value, opting the formula out of auto-spill.
|
||||
|
||||
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
|
||||
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
|
||||
|
||||
m_pDoc->SetFormula(ScAddress(0, 0, 0), u"=@SEQUENCE(4)"_ustr,
|
||||
formula::FormulaGrammar::GRAM_NATIVE);
|
||||
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0, 0, 0)));
|
||||
// No spill into the row below.
|
||||
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0, 1, 0)));
|
||||
|
||||
m_pDoc->DeleteTab(0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testDynamicArrayResizeDuringCopyToClip)
|
||||
{
|
||||
// A dynamic-array resize queued during CopyToClip must run after the clipboard clone is made.
|
||||
|
||||
@@ -610,6 +610,7 @@ private:
|
||||
void ScXor();
|
||||
void ScNot();
|
||||
void ScNeg();
|
||||
void ScSingleValue();
|
||||
void ScPercentSign();
|
||||
void ScIntersect();
|
||||
void ScRangeFunc();
|
||||
|
||||
@@ -141,7 +141,8 @@ constexpr std::array<ScCharFlags, 128> makeCommonCharTable()
|
||||
a['='] = ScCharFlags::Char | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
|
||||
a['>'] = ScCharFlags::CharBool | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
|
||||
a['?'] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::Name;
|
||||
/* @ */ // FREE
|
||||
// @ is the prefix operator that opts a formula out of auto-spill.
|
||||
a['@'] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
|
||||
|
||||
for (int i = 'A'; i <= 'Z'; i++)
|
||||
a[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
|
||||
@@ -211,7 +212,8 @@ constexpr std::array<ScCharFlags, 128> makeCharTable_XL()
|
||||
a['='] |= ScCharFlags::Word;
|
||||
a['>'] |= ScCharFlags::Word;
|
||||
/* ? */ // question really is not permitted in sheet name
|
||||
a['@'] |= ScCharFlags::Word;
|
||||
// @ stays an operator in the XL grammar too. Sheet names that contain
|
||||
// @ have to be quoted.
|
||||
a['['] = ScCharFlags::Word;
|
||||
a[']'] = ScCharFlags::Word;
|
||||
a['{'] |= ScCharFlags::Word;
|
||||
|
||||
@@ -1543,6 +1543,29 @@ void ScInterpreter::ScNeg()
|
||||
}
|
||||
}
|
||||
|
||||
void ScInterpreter::ScSingleValue()
|
||||
{
|
||||
// The @ operator collapses an array operand to a single value. It is
|
||||
// the implicit-intersection prefix that opts a formula out of the
|
||||
// dynamic-array spill behaviour.
|
||||
nFuncFmtType = nCurFmtType;
|
||||
if (GetStackType() == svMatrix)
|
||||
{
|
||||
ScMatrixRef pMat = GetMatrix();
|
||||
if (!pMat)
|
||||
{
|
||||
PushIllegalParameter();
|
||||
return;
|
||||
}
|
||||
ScMatrixValue aVal = pMat->Get(0, 0);
|
||||
if (aVal.nType == ScMatValType::String || aVal.nType == ScMatValType::Empty)
|
||||
PushString(aVal.aStr);
|
||||
else
|
||||
PushDouble(aVal.fVal);
|
||||
}
|
||||
// Non-matrix operand passes through unchanged.
|
||||
}
|
||||
|
||||
void ScInterpreter::ScPercentSign()
|
||||
{
|
||||
nFuncFmtType = SvNumFormatType::PERCENT;
|
||||
|
||||
@@ -4154,6 +4154,7 @@ StackVar ScInterpreter::Interpret()
|
||||
case ocNot : ScNot(); break;
|
||||
case ocNegSub :
|
||||
case ocNeg : ScNeg(); break;
|
||||
case ocSingleValue : ScSingleValue(); break;
|
||||
case ocPercentSign : ScPercentSign(); break;
|
||||
case ocPi : ScPi(); break;
|
||||
case ocRandom : ScRandom(); break;
|
||||
|
||||
Reference in New Issue
Block a user