summaryrefslogtreecommitdiff
path: root/formula
diff options
context:
space:
mode:
Diffstat (limited to 'formula')
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx162
-rw-r--r--formula/source/core/api/token.cxx5
-rw-r--r--formula/source/core/resource/core_resource.src2
3 files changed, 161 insertions, 8 deletions
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index cf44e924d98c..367387faa294 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -266,6 +266,99 @@ struct OpCodeMapData
};
+bool isPotentialRangeLeftOp( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocClose:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isRangeResultFunction( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocIndirect:
+ case ocOffset:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isRangeResultOpCode( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocRange:
+ case ocUnion:
+ case ocIntersect:
+ case ocIndirect:
+ case ocOffset:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ @param pToken
+ MUST be a valid token, caller has to ensure.
+
+ @param bRight
+ If bRPN==false, bRight==false means opcodes for left side are
+ checked, bRight==true means opcodes for right side. If bRPN==true
+ it doesn't matter.
+ */
+bool isPotentialRangeType( FormulaToken* pToken, bool bRPN, bool bRight )
+{
+ switch (pToken->GetType())
+ {
+ case svByte: // could be range result, but only a few
+ if (bRPN)
+ return isRangeResultOpCode( pToken->GetOpCode());
+ else if (bRight)
+ return isRangeResultFunction( pToken->GetOpCode());
+ else
+ return isPotentialRangeLeftOp( pToken->GetOpCode());
+ case svSingleRef:
+ case svDoubleRef:
+ case svIndex: // could be range
+ //case svRefList: // um..what?
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName: // could be range
+ return true;
+ default:
+ // Separators are not part of RPN and right opcodes need to be
+ // other StackVarEnum types or functions and thus svByte.
+ return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
+ }
+}
+
+bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
+{
+ FormulaToken* pToken1 = *pCode1;
+ FormulaToken* pToken2 = *pCode2;
+ if (pToken1 && pToken2)
+ return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
+ return false;
+}
+
+bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
+ FormulaToken const * const * const pCode,
+ FormulaToken const * const * const pCode1,
+ FormulaToken const * const * const pCode2 )
+{
+ return nPC >= 2 && pCode1 && pCode2 &&
+ (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
+ (*pCode1 != nullptr) && (*pCode2 != nullptr);
+}
+
+
} // namespace
@@ -1058,6 +1151,8 @@ bool FormulaCompiler::GetToken()
bStop = true;
else
{
+ FormulaTokenRef pCurrToken = mpToken;
+ FormulaTokenRef pSpacesToken;
short nWasColRowName;
if ( pArr->nIndex
&& pArr->pCode[ pArr->nIndex-1 ]->GetOpCode() == ocColRowName )
@@ -1067,6 +1162,9 @@ bool FormulaCompiler::GetToken()
mpToken = pArr->Next();
while( mpToken && mpToken->GetOpCode() == ocSpaces )
{
+ // For significant whitespace remember last ocSpaces token. Usually
+ // there's only one even for multiple spaces.
+ pSpacesToken = mpToken;
if ( nWasColRowName )
nWasColRowName++;
if ( bAutoCorrect && !pStack )
@@ -1092,6 +1190,17 @@ bool FormulaCompiler::GetToken()
mpToken = new FormulaByteToken( ocIntersect );
pArr->nIndex--; // we advanced to the second ocColRowName, step back
}
+ else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
+ pCurrToken && mpToken &&
+ isPotentialRangeType( pCurrToken.get(), false, false) &&
+ isPotentialRangeType( mpToken.get(), false, true))
+ {
+ // Let IntersectionLine() <- Factor() decide how to treat this,
+ // once the actual arguments are determined in RPN.
+ mpToken = pSpacesToken;
+ pArr->nIndex--; // step back from next non-spaces token
+ return true;
+ }
}
}
if( bStop )
@@ -1562,12 +1671,33 @@ void FormulaCompiler::RangeLine()
void FormulaCompiler::IntersectionLine()
{
RangeLine();
- while (mpToken->GetOpCode() == ocIntersect)
+ while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
{
+ sal_uInt16 nCodeIndex = pArr->nIndex - 1;
+ FormulaToken** pCode1 = pCode - 1;
FormulaTokenRef p = mpToken;
NextToken();
RangeLine();
- PutCode(p);
+ FormulaToken** pCode2 = pCode - 1;
+ if (p->GetOpCode() == ocSpaces)
+ {
+ // Convert to intersection if both left and right are references or
+ // functions (potentially returning references, if not then a space
+ // or no space would be a syntax error anyway), not other operators
+ // or operands. Else discard.
+ if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
+ {
+ FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
+ // Replace ocSpaces with ocIntersect so that when switching
+ // formula syntax the correct operator string is created.
+ pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
+ PutCode( pIntersect);
+ }
+ }
+ else
+ {
+ PutCode(p);
+ }
}
}
@@ -1918,6 +2048,14 @@ const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuf
}
else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
+ else if (eOp == ocIntersect)
+ {
+ // Nasty, ugly, horrific, terrifying..
+ if (FormulaGrammar::isExcelSyntax( meGrammar))
+ rBuffer.append(' ');
+ else
+ rBuffer.append( mxSymbols->getSymbol( eOp));
+ }
else if( (sal_uInt16) eOp < mxSymbols->getSymbolCount()) // Keyword:
rBuffer.append( mxSymbols->getSymbol( eOp));
else
@@ -2206,7 +2344,25 @@ OpCode FormulaCompiler::NextToken()
}
}
}
- eLastOp = eOp;
+ // Nasty, ugly, horrific, terrifying.. significant whitespace..
+ if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
+ {
+ // Fake an intersection op as last op for the next round, but at
+ // least roughly check if it could make sense at all.
+ FormulaToken* pPrev = pArr->PeekPrevNoSpaces();
+ if (pPrev && isPotentialRangeType( pPrev, false, false))
+ {
+ FormulaToken* pNext = pArr->PeekNextNoSpaces();
+ if (pNext && isPotentialRangeType( pNext, false, true))
+ eLastOp = ocIntersect;
+ else
+ eLastOp = eOp;
+ }
+ else
+ eLastOp = eOp;
+ }
+ else
+ eLastOp = eOp;
}
return eOp;
}
diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index c16f3cad4332..7511c34a8720 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -868,16 +868,13 @@ FormulaToken* FormulaTokenArray::MergeArray( )
FormulaToken* FormulaTokenArray::ReplaceToken( sal_uInt16 nOffset, FormulaToken* t,
FormulaTokenArray::ReplaceMode eMode )
{
- if (eMode == BACKWARD_CODE_ONLY)
- nOffset = nLen - nOffset - 1;
-
if (nOffset < nLen)
{
CheckToken(*t);
t->IncRef();
FormulaToken* p = pCode[nOffset];
pCode[nOffset] = t;
- if (eMode == FORWARD_CODE_AND_RPN && p->GetRef() > 1)
+ if (eMode == CODE_AND_RPN && p->GetRef() > 1)
{
for (sal_uInt16 i=0; i < nRPN; ++i)
{
diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src
index d5db65ec0d7b..af22f1b6bc65 100644
--- a/formula/source/core/resource/core_resource.src
+++ b/formula/source/core/resource/core_resource.src
@@ -477,7 +477,7 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
String SC_OPCODE_AND { Text = "AND" ; };
String SC_OPCODE_OR { Text = "OR" ; };
String SC_OPCODE_XOR { Text = "_xlfn.XOR" ; };
- String SC_OPCODE_INTERSECT { Text = "!" ; };
+ String SC_OPCODE_INTERSECT { Text = " " ; };
String SC_OPCODE_UNION { Text = "~" ; };
String SC_OPCODE_RANGE { Text = ":" ; };
String SC_OPCODE_NOT { Text = "NOT" ; };