diff options
Diffstat (limited to 'formula/source')
-rw-r--r-- | formula/source/core/api/FormulaCompiler.cxx | 162 | ||||
-rw-r--r-- | formula/source/core/api/token.cxx | 5 | ||||
-rw-r--r-- | formula/source/core/resource/core_resource.src | 2 |
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" ; }; |