1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <compiler.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/settings.hxx>
27 #include <sfx2/app.hxx>
28 #include <sfx2/objsh.hxx>
29 #include <basic/sbmeth.hxx>
30 #include <basic/sbstar.hxx>
31 #include <svl/numformat.hxx>
32 #include <svl/zforlist.hxx>
33 #include <svl/sharedstringpool.hxx>
34 #include <sal/log.hxx>
35 #include <o3tl/safeint.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <osl/diagnose.h>
38 #include <rtl/character.hxx>
39 #include <unotools/charclass.hxx>
40 #include <unotools/configmgr.hxx>
41 #include <com/sun/star/lang/Locale.hpp>
42 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
43 #include <com/sun/star/sheet/FormulaLanguage.hpp>
44 #include <com/sun/star/i18n/KParseTokens.hpp>
45 #include <com/sun/star/i18n/KParseType.hpp>
46 #include <comphelper/processfactory.hxx>
47 #include <comphelper/string.hxx>
48 #include <unotools/transliterationwrapper.hxx>
49 #include <tools/urlobj.hxx>
50 #include <rtl/math.hxx>
51 #include <rtl/ustring.hxx>
53 #include <rangenam.hxx>
55 #include <document.hxx>
57 #include <callform.hxx>
58 #include <addincol.hxx>
59 #include <refupdat.hxx>
60 #include <globstr.hrc>
61 #include <scresid.hxx>
62 #include <formulacell.hxx>
63 #include <dociter.hxx>
64 #include <docoptio.hxx>
65 #include <formula/errorcodes.hxx>
66 #include <parclass.hxx>
67 #include <autonamecache.hxx>
68 #include <externalrefmgr.hxx>
69 #include <rangeutl.hxx>
70 #include <convuno.hxx>
71 #include <tokenuno.hxx>
72 #include <formulaparserpool.hxx>
73 #include <tokenarray.hxx>
74 #include <scmatrix.hxx>
75 #include <tokenstringcontext.hxx>
76 #include <officecfg/Office/Common.hxx>
77 #include <sfx2/linkmgr.hxx>
78 #include <interpre.hxx>
80 using namespace formula
;
81 using namespace ::com::sun::star
;
84 const CharClass
* ScCompiler::pCharClassEnglish
= nullptr;
85 const CharClass
* ScCompiler::pCharClassLocalized
= nullptr;
86 const ScCompiler::Convention
* ScCompiler::pConventions
[ ] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
108 static const char* pInternal
[2] = { "TTT", "__DEBUG_VAR" };
110 using namespace ::com::sun::star::i18n
;
112 void ScCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr
& xMap
,FormulaGrammar::Grammar _eGrammar
) const
114 size_t nSymbolOffset
;
117 // XFunctionAccess and XCell::setFormula()/getFormula() API always used
118 // PODF grammar symbols, keep it.
119 case FormulaGrammar::GRAM_API
:
120 case FormulaGrammar::GRAM_PODF
:
121 nSymbolOffset
= offsetof( AddInMap
, pUpper
);
124 case FormulaGrammar::GRAM_ODFF
:
125 nSymbolOffset
= offsetof( AddInMap
, pODFF
);
127 case FormulaGrammar::GRAM_ENGLISH
:
128 nSymbolOffset
= offsetof( AddInMap
, pEnglish
);
131 const AddInMap
* const pStop
= g_aAddInMap
+ GetAddInMapCount();
132 for (const AddInMap
* pMap
= g_aAddInMap
; pMap
< pStop
; ++pMap
)
134 char const * const * ppSymbol
=
135 reinterpret_cast< char const * const * >(
136 reinterpret_cast< char const * >(pMap
) + nSymbolOffset
);
137 xMap
->putExternal( OUString::createFromAscii( *ppSymbol
),
138 OUString::createFromAscii( pMap
->pOriginal
));
140 if (_eGrammar
== FormulaGrammar::GRAM_API
)
142 // Add English names additionally to programmatic names, so they
143 // can be used in XCell::setFormula() non-localized API calls.
144 // Note the reverse map will still deliver programmatic names for
145 // XCell::getFormula().
146 nSymbolOffset
= offsetof( AddInMap
, pEnglish
);
147 for (const AddInMap
* pMap
= g_aAddInMap
; pMap
< pStop
; ++pMap
)
149 char const * const * ppSymbol
=
150 reinterpret_cast< char const * const * >(
151 reinterpret_cast< char const * >(pMap
) + nSymbolOffset
);
152 xMap
->putExternal( OUString::createFromAscii( *ppSymbol
),
153 OUString::createFromAscii( pMap
->pOriginal
));
158 void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr
& xMap
) const
160 ScUnoAddInCollection
* pColl
= ScGlobal::GetAddInCollection();
161 tools::Long nCount
= pColl
->GetFuncCount();
162 for (tools::Long i
=0; i
< nCount
; ++i
)
164 const ScUnoAddInFuncData
* pFuncData
= pColl
->GetFuncData(i
);
166 xMap
->putExternalSoftly( pFuncData
->GetUpperName(),
167 pFuncData
->GetOriginalName());
171 void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr
& xMap
) const
173 ScUnoAddInCollection
* pColl
= ScGlobal::GetAddInCollection();
174 tools::Long nCount
= pColl
->GetFuncCount();
175 for (tools::Long i
=0; i
< nCount
; ++i
)
177 const ScUnoAddInFuncData
* pFuncData
= pColl
->GetFuncData(i
);
180 const OUString
aName( pFuncData
->GetUpperEnglish());
181 if (!aName
.isEmpty())
182 xMap
->putExternalSoftly( aName
, pFuncData
->GetOriginalName());
184 xMap
->putExternalSoftly( pFuncData
->GetUpperName(),
185 pFuncData
->GetOriginalName());
190 void ScCompiler::fillFromAddInCollectionExcelName( const NonConstOpCodeMapPtr
& xMap
) const
192 const LanguageTag
aDestLang(LANGUAGE_ENGLISH_US
);
193 ScUnoAddInCollection
* pColl
= ScGlobal::GetAddInCollection();
194 tools::Long nCount
= pColl
->GetFuncCount();
195 for (tools::Long i
=0; i
< nCount
; ++i
)
198 const ScUnoAddInFuncData
* pFuncData
= pColl
->GetFuncData(i
);
199 if (pFuncData
&& pFuncData
->GetExcelName( aDestLang
, aExcelName
, true))
201 // Note that function names not defined in OOXML but implemented by
202 // Excel should have the "_xlfn." prefix. We have no way to check
203 // though what an Add-In actually implements.
204 xMap
->putExternalSoftly( GetCharClassEnglish()->uppercase(aExcelName
), pFuncData
->GetOriginalName());
209 static std::mutex gCharClassMutex
;
211 void ScCompiler::DeInit()
213 std::scoped_lock
aGuard(gCharClassMutex
);
214 if (pCharClassEnglish
)
216 delete pCharClassEnglish
;
217 pCharClassEnglish
= nullptr;
219 if (pCharClassLocalized
)
221 delete pCharClassLocalized
;
222 pCharClassLocalized
= nullptr;
226 bool ScCompiler::IsEnglishSymbol( const OUString
& rName
)
228 // function names are always case-insensitive
229 OUString aUpper
= GetCharClassEnglish()->uppercase(rName
);
231 // 1. built-in function name
232 formula::FormulaCompiler aCompiler
;
233 OpCode eOp
= aCompiler
.GetEnglishOpCode( aUpper
);
238 // 2. old add in functions
239 if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper
))
244 // 3. new (uno) add in functions
245 OUString aIntName
= ScGlobal::GetAddInCollection()->FindFunction(aUpper
, false);
246 return !aIntName
.isEmpty(); // no valid function name
249 const CharClass
* ScCompiler::GetCharClassEnglish()
251 std::scoped_lock
aGuard(gCharClassMutex
);
252 if (!pCharClassEnglish
)
254 pCharClassEnglish
= new CharClass( ::comphelper::getProcessComponentContext(),
255 LanguageTag( LANGUAGE_ENGLISH_US
));
257 return pCharClassEnglish
;
260 const CharClass
* ScCompiler::GetCharClassLocalized()
262 // Switching UI language requires restart; if not, we would have to
263 // keep track of that.
264 std::scoped_lock
aGuard(gCharClassMutex
);
265 if (!pCharClassLocalized
)
267 pCharClassLocalized
= new CharClass( ::comphelper::getProcessComponentContext(),
268 Application::GetSettings().GetUILanguageTag());
270 return pCharClassLocalized
;
273 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar
)
275 assert( eGrammar
!= FormulaGrammar::GRAM_UNSPECIFIED
&& "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
276 if (eGrammar
== GetGrammar())
277 return; // nothing to be done
279 if( eGrammar
== FormulaGrammar::GRAM_EXTERNAL
)
281 meGrammar
= eGrammar
;
282 mxSymbols
= GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE
);
286 FormulaGrammar::Grammar eMyGrammar
= eGrammar
;
287 const sal_Int32 nFormulaLanguage
= FormulaGrammar::extractFormulaLanguage( eMyGrammar
);
288 OpCodeMapPtr xMap
= GetFinalOpCodeMap( nFormulaLanguage
);
289 OSL_ENSURE( xMap
, "ScCompiler::SetGrammar: unknown formula language");
292 xMap
= GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE
);
293 eMyGrammar
= xMap
->getGrammar();
296 // Save old grammar for call to SetGrammarAndRefConvention().
297 FormulaGrammar::Grammar eOldGrammar
= GetGrammar();
298 // This also sets the grammar associated with the map!
299 SetFormulaLanguage( xMap
);
301 // Override if necessary.
302 if (eMyGrammar
!= GetGrammar())
303 SetGrammarAndRefConvention( eMyGrammar
, eOldGrammar
);
307 // Unclear how this was intended to be refreshed when the
308 // grammar or sheet count is changed ? Ideally this would be
309 // a method on Document that would globally cache these.
310 std::vector
<OUString
> &ScCompiler::GetSetupTabNames() const
312 std::vector
<OUString
> &rTabNames
= const_cast<ScCompiler
*>(this)->maTabNames
;
314 if (rTabNames
.empty())
316 rTabNames
= rDoc
.GetAllTableNames();
317 for (auto& rTabName
: rTabNames
)
318 ScCompiler::CheckTabQuotes(rTabName
, formula::FormulaGrammar::extractRefConvention(meGrammar
));
324 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr
& xMap
)
330 if (mxSymbols
->isEnglish())
331 pCharClass
= GetCharClassEnglish();
333 pCharClass
= GetCharClassLocalized();
335 // The difference is needed for an uppercase() call that usually does not
336 // result in different strings but for a few languages like Turkish;
337 // though even de-DE and de-CH may differ in ß/SS handling..
338 // At least don't care if both are English.
339 // The current locale is more likely to not be "en" so check first.
340 const LanguageTag
& rLT1
= ScGlobal::getCharClass().getLanguageTag();
341 const LanguageTag
& rLT2
= pCharClass
->getLanguageTag();
342 mbCharClassesDiffer
= (rLT1
!= rLT2
&& (rLT1
.getLanguage() != "en" || rLT2
.getLanguage() != "en"));
344 SetGrammarAndRefConvention( mxSymbols
->getGrammar(), GetGrammar());
347 void ScCompiler::SetGrammarAndRefConvention(
348 const FormulaGrammar::Grammar eNewGrammar
, const FormulaGrammar::Grammar eOldGrammar
)
350 meGrammar
= eNewGrammar
; // SetRefConvention needs the new grammar set!
351 FormulaGrammar::AddressConvention eConv
= FormulaGrammar::extractRefConvention( meGrammar
);
352 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
&& eOldGrammar
== FormulaGrammar::GRAM_UNSPECIFIED
)
353 SetRefConvention( rDoc
.GetAddressConvention());
355 SetRefConvention( eConv
);
358 OUString
ScCompiler::FindAddInFunction( const OUString
& rUpperName
, bool bLocalFirst
) const
360 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName
, bLocalFirst
); // bLocalFirst=false for english
363 ScCompiler::Convention::~Convention()
367 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv
)
372 ScCharFlags
*t
= new ScCharFlags
[128];
374 ScCompiler::pConventions
[ meConv
] = this;
375 mpCharTable
.reset( t
);
377 for (i
= 0; i
< 128; i
++)
378 t
[i
] = ScCharFlags::Illegal
;
380 // Allow tabs/newlines.
381 // Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
382 /* tab */ t
[ 9] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
383 /* lf */ t
[10] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
384 /* cr */ t
[13] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
386 /* */ t
[32] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
387 /* ! */ t
[33] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
388 if (FormulaGrammar::CONV_ODF
== meConv
)
389 /* ! */ t
[33] |= ScCharFlags::OdfLabelOp
;
390 /* " */ t
[34] = ScCharFlags::CharString
| ScCharFlags::StringSep
;
391 /* # */ t
[35] = ScCharFlags::WordSep
| ScCharFlags::CharErrConst
;
392 /* $ */ t
[36] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
;
393 if (FormulaGrammar::CONV_ODF
== meConv
)
394 /* $ */ t
[36] |= ScCharFlags::OdfNameMarker
;
395 /* % */ t
[37] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
396 /* & */ t
[38] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
397 /* ' */ t
[39] = ScCharFlags::NameSep
;
398 /* ( */ t
[40] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
399 /* ) */ t
[41] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
400 /* * */ t
[42] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
401 /* + */ t
[43] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueExp
| ScCharFlags::ValueSign
;
402 /* , */ t
[44] = ScCharFlags::CharValue
| ScCharFlags::Value
;
403 /* - */ t
[45] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueExp
| ScCharFlags::ValueSign
;
404 /* . */ t
[46] = ScCharFlags::Word
| ScCharFlags::CharValue
| ScCharFlags::Value
| ScCharFlags::Ident
| ScCharFlags::Name
;
405 /* / */ t
[47] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
407 for (i
= 48; i
< 58; i
++)
408 /* 0-9 */ t
[i
] = ScCharFlags::CharValue
| ScCharFlags::Word
| ScCharFlags::Value
| ScCharFlags::ValueExp
| ScCharFlags::ValueValue
| ScCharFlags::Ident
| ScCharFlags::Name
;
410 /* : */ t
[58] = ScCharFlags::Char
| ScCharFlags::Word
;
411 /* ; */ t
[59] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
412 /* < */ t
[60] = ScCharFlags::CharBool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
413 /* = */ t
[61] = ScCharFlags::Char
| ScCharFlags::Bool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
414 /* > */ t
[62] = ScCharFlags::CharBool
| ScCharFlags::Bool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
415 /* ? */ t
[63] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::Name
;
418 for (i
= 65; i
< 91; i
++)
419 /* A-Z */ t
[i
] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
421 if (FormulaGrammar::CONV_ODF
== meConv
)
423 /* [ */ t
[91] = ScCharFlags::OdfLBracket
;
425 /* ] */ t
[93] = ScCharFlags::OdfRBracket
;
427 else if (FormulaGrammar::CONV_OOO
== meConv
)
429 /* [ */ t
[91] = ScCharFlags::Char
;
431 /* ] */ t
[93] = ScCharFlags::Char
;
433 else if (FormulaGrammar::CONV_XL_OOX
== meConv
)
435 /* [ */ t
[91] = ScCharFlags::Char
| ScCharFlags::CharIdent
;
437 /* ] */ t
[93] = ScCharFlags::Char
| ScCharFlags::Ident
;
439 else if (FormulaGrammar::CONV_XL_A1
== meConv
)
441 /* [ */ t
[91] = ScCharFlags::Char
;
443 /* ] */ t
[93] = ScCharFlags::Char
;
445 else if( FormulaGrammar::CONV_XL_R1C1
== meConv
)
447 /* [ */ t
[91] = ScCharFlags::Ident
;
449 /* ] */ t
[93] = ScCharFlags::Ident
;
458 /* ^ */ t
[94] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
459 /* _ */ t
[95] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
462 for (i
= 97; i
< 123; i
++)
463 /* a-z */ t
[i
] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
465 /* { */ t
[123] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array open
466 /* | */ t
[124] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array row sep (Should be OOo specific)
467 /* } */ t
[125] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array close
468 /* ~ */ t
[126] = ScCharFlags::Char
; // OOo specific
471 if( !(FormulaGrammar::CONV_XL_A1
== meConv
|| FormulaGrammar::CONV_XL_R1C1
== meConv
|| FormulaGrammar::CONV_XL_OOX
== meConv
) )
474 /* */ t
[32] |= ScCharFlags::Word
;
475 /* ! */ t
[33] |= ScCharFlags::Ident
| ScCharFlags::Word
;
476 /* " */ t
[34] |= ScCharFlags::Word
;
477 /* # */ t
[35] &= ~ScCharFlags::WordSep
;
478 /* # */ t
[35] |= ScCharFlags::Word
;
479 /* % */ t
[37] |= ScCharFlags::Word
;
480 /* & */ t
[38] |= ScCharFlags::Word
;
481 /* ' */ t
[39] |= ScCharFlags::Word
;
482 /* ( */ t
[40] |= ScCharFlags::Word
;
483 /* ) */ t
[41] |= ScCharFlags::Word
;
484 /* * */ t
[42] |= ScCharFlags::Word
;
485 /* + */ t
[43] |= ScCharFlags::Word
;
486 #if 0 /* this really needs to be locale specific. */
487 /* , */ t
[44] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
489 /* , */ t
[44] |= ScCharFlags::Word
;
491 /* - */ t
[45] |= ScCharFlags::Word
;
493 /* ; */ t
[59] |= ScCharFlags::Word
;
494 /* < */ t
[60] |= ScCharFlags::Word
;
495 /* = */ t
[61] |= ScCharFlags::Word
;
496 /* > */ t
[62] |= ScCharFlags::Word
;
497 /* ? */ // question really is not permitted in sheet name
498 /* @ */ t
[64] |= ScCharFlags::Word
;
499 /* [ */ t
[91] |= ScCharFlags::Word
;
500 /* ] */ t
[93] |= ScCharFlags::Word
;
501 /* { */ t
[123]|= ScCharFlags::Word
;
502 /* | */ t
[124]|= ScCharFlags::Word
;
503 /* } */ t
[125]|= ScCharFlags::Word
;
504 /* ~ */ t
[126]|= ScCharFlags::Word
;
507 static bool lcl_isValidQuotedText( std::u16string_view rFormula
, size_t nSrcPos
, ParseResult
& rRes
)
509 // Tokens that start at ' can have anything in them until a final '
510 // but '' marks an escaped '
511 // We've earlier guaranteed that a string containing '' will be
513 if (nSrcPos
< rFormula
.size() && rFormula
[nSrcPos
] == '\'')
515 size_t nPos
= nSrcPos
+1;
516 while (nPos
< rFormula
.size())
518 if (rFormula
[nPos
] == '\'')
520 if ( (nPos
+1 == rFormula
.size()) || (rFormula
[nPos
+1] != '\'') )
522 rRes
.TokenType
= KParseType::SINGLE_QUOTE_NAME
;
523 rRes
.EndPos
= nPos
+1;
535 static bool lcl_parseExternalName(
536 const OUString
& rSymbol
,
539 const sal_Unicode cSep
,
540 const ScDocument
& rDoc
,
541 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
543 /* TODO: future versions will have to support sheet-local names too, thus
544 * return a possible sheet name as well. */
545 const sal_Unicode
* const pStart
= rSymbol
.getStr();
546 const sal_Unicode
* p
= pStart
;
547 sal_Int32 nLen
= rSymbol
.getLength();
549 OUStringBuffer aTmpName
;
551 bool bInName
= false;
554 // For XL use existing parser that resolves bracketed and quoted and
555 // indexed external document names.
557 OUString aStartTabName
, aEndTabName
;
558 ScRefFlags nFlags
= ScRefFlags::ZERO
;
559 p
= aRange
.Parse_XL_Header( p
, rDoc
, aTmpFile
, aStartTabName
,
560 aEndTabName
, nFlags
, true, pExternalLinks
);
561 if (!p
|| p
== pStart
)
563 i
= sal_Int32(p
- pStart
);
565 for ( ; i
< nLen
; ++i
, ++p
)
570 if (c
== '.' || c
== cSep
)
575 // Move to the next char and loop until the second single
577 sal_Unicode cPrev
= c
;
579 for (sal_Int32 j
= i
; j
< nLen
; ++j
, ++p
)
586 // empty quote e.g. (=''!Name)
592 // two consecutive quotes equal a single quote in
594 aTmpFile
+= OUStringChar(c
);
603 if (cPrev
== '\'' && j
!= i
)
605 // this is not a quote but the previous one is. This
606 // ends the parsing of the quoted segment. At this
607 // point, the current char must equal the separator
612 aTmpName
.append(c
); // Keep the separator as part of the name.
615 aTmpFile
+= OUStringChar(c
);
621 // premature ending of the quoted segment.
627 // only the separator is allowed after the closing quote.
639 // A second separator ? Not a valid external name.
649 aTmpName
.append(c
); // Keep the separator as part of the name.
655 if (rtl::isAsciiAlphanumeric(c
))
660 // non-ASCII character is allowed.
669 // these special characters are allowed.
679 aTmpFile
+= OUStringChar(c
);
686 // No name found - most likely the symbol has no '!'s.
690 sal_Int32 nNameLen
= aTmpName
.getLength();
693 // Name must be at least 2-char long (separator plus name).
697 if (aTmpName
[0] != cSep
)
699 // 1st char of the name must equal the separator.
703 if (aTmpName
[nNameLen
-1] == '!')
705 // Check against #REF!.
706 if (OUString::unacquired(aTmpName
).equalsIgnoreAsciiCase("#REF!"))
711 rName
= aTmpName
.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
715 static OUString
lcl_makeExternalNameStr(const OUString
& rFile
, const OUString
& rName
,
716 const sal_Unicode cSep
, bool bODF
)
718 OUString
aEscQuote(u
"''"_ustr
);
719 OUString
aFile(rFile
.replaceAll("'", aEscQuote
));
720 OUString
aName(rName
);
722 aName
= aName
.replaceAll("'", aEscQuote
);
723 OUStringBuffer
aBuf(aFile
.getLength() + aName
.getLength() + 9);
726 aBuf
.append( "'" + aFile
+ "'" + OUStringChar(cSep
) );
728 aBuf
.append( "$$'" );
732 return aBuf
.makeStringAndClear();
735 static bool lcl_getLastTabName( OUString
& rTabName2
, const OUString
& rTabName1
,
736 const vector
<OUString
>& rTabNames
, const ScRange
& rRef
)
738 SCTAB nTabSpan
= rRef
.aEnd
.Tab() - rRef
.aStart
.Tab();
741 size_t nCount
= rTabNames
.size();
742 vector
<OUString
>::const_iterator itrBeg
= rTabNames
.begin(), itrEnd
= rTabNames
.end();
743 vector
<OUString
>::const_iterator itr
= ::std::find(itrBeg
, itrEnd
, rTabName1
);
744 if (itr
== rTabNames
.end())
746 rTabName2
= ScResId(STR_NO_REF_TABLE
);
750 size_t nDist
= ::std::distance(itrBeg
, itr
);
751 if (nDist
+ static_cast<size_t>(nTabSpan
) >= nCount
)
753 rTabName2
= ScResId(STR_NO_REF_TABLE
);
757 rTabName2
= rTabNames
[nDist
+nTabSpan
];
760 rTabName2
= rTabName1
;
767 struct Convention_A1
: public ScCompiler::Convention
769 explicit Convention_A1( FormulaGrammar::AddressConvention eConv
) : ScCompiler::Convention( eConv
) { }
770 static void MakeColStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCCOL nCol
);
771 static void MakeRowStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCROW nRow
);
773 ParseResult
parseAnyToken( const OUString
& rFormula
,
775 const CharClass
* pCharClass
,
776 bool bGroupSeparator
) const override
779 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
782 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
783 KParseTokens::ASC_UNDERSCORE
| KParseTokens::ASC_DOLLAR
;
784 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
785 // '?' allowed in range names because of Xcl :-/
786 static constexpr OUString
aAddAllowed(u
"?#"_ustr
);
787 return pCharClass
->parseAnyToken( rFormula
,
788 nSrcPos
, nStartFlags
, aAddAllowed
,
789 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
793 virtual ScCharFlags
getCharTableFlags( sal_Unicode c
, sal_Unicode
/*cLast*/ ) const override
795 return mpCharTable
[static_cast<sal_uInt8
>(c
)];
801 void Convention_A1::MakeColStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCCOL nCol
)
803 if ( !rLimits
.ValidCol(nCol
) )
804 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
806 ::ScColToAlpha( rBuffer
, nCol
);
809 void Convention_A1::MakeRowStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCROW nRow
)
811 if ( !rLimits
.ValidRow(nRow
) )
812 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
814 rBuffer
.append(sal_Int32(nRow
+ 1));
819 struct ConventionOOO_A1
: public Convention_A1
821 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO
) { }
822 explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv
) : Convention_A1 (eConv
) { }
824 static void MakeTabStr( OUStringBuffer
&rBuf
, const std::vector
<OUString
>& rTabNames
, SCTAB nTab
)
826 if (o3tl::make_unsigned(nTab
) >= rTabNames
.size())
827 rBuf
.append(ScResId(STR_NO_REF_TABLE
));
829 rBuf
.append(rTabNames
[nTab
]);
833 enum SingletonDisplay
840 static void MakeOneRefStrImpl(
841 const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
,
842 std::u16string_view rErrRef
, const std::vector
<OUString
>& rTabNames
,
843 const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
,
844 bool bForceTab
, bool bODF
, SingletonDisplay eSingletonDisplay
)
846 if( rRef
.IsFlag3D() || bForceTab
)
848 if (!ValidTab(rAbsRef
.Tab()) || rRef
.IsTabDeleted())
850 if (!rRef
.IsTabRel())
852 rBuffer
.append(rErrRef
);
857 if (!rRef
.IsTabRel())
859 MakeTabStr(rBuffer
, rTabNames
, rAbsRef
.Tab());
865 if (eSingletonDisplay
!= SINGLETON_ROW
)
867 if (!rRef
.IsColRel())
869 if (!rLimits
.ValidCol(rAbsRef
.Col()) || rRef
.IsColDeleted())
870 rBuffer
.append(rErrRef
);
872 MakeColStr(rLimits
, rBuffer
, rAbsRef
.Col());
875 if (eSingletonDisplay
!= SINGLETON_COL
)
877 if (!rRef
.IsRowRel())
879 if (!rLimits
.ValidRow(rAbsRef
.Row()) || rRef
.IsRowDeleted())
880 rBuffer
.append(rErrRef
);
882 MakeRowStr(rLimits
, rBuffer
, rAbsRef
.Row());
886 static SingletonDisplay
getSingletonDisplay( const ScSheetLimits
& rLimits
, const ScAddress
& rAbs1
, const ScAddress
& rAbs2
,
887 const ScComplexRefData
& rRef
, bool bFromRangeName
)
889 // If any part is error, display as such.
890 if (!rLimits
.ValidCol(rAbs1
.Col()) || rRef
.Ref1
.IsColDeleted() || !rLimits
.ValidRow(rAbs1
.Row()) || rRef
.Ref1
.IsRowDeleted() ||
891 !rLimits
.ValidCol(rAbs2
.Col()) || rRef
.Ref2
.IsColDeleted() || !rLimits
.ValidRow(rAbs2
.Row()) || rRef
.Ref2
.IsRowDeleted())
892 return SINGLETON_NONE
;
894 // A:A or $A:$A or A:$A or $A:A
895 if (rRef
.IsEntireCol(rLimits
))
896 return SINGLETON_COL
;
898 // Same if not in named expression and both rows of entire columns are
899 // relative references.
900 if (!bFromRangeName
&& rAbs1
.Row() == 0 && rAbs2
.Row() == rLimits
.mnMaxRow
&&
901 rRef
.Ref1
.IsRowRel() && rRef
.Ref2
.IsRowRel())
902 return SINGLETON_COL
;
904 // 1:1 or $1:$1 or 1:$1 or $1:1
905 if (rRef
.IsEntireRow(rLimits
))
906 return SINGLETON_ROW
;
908 // Same if not in named expression and both columns of entire rows are
909 // relative references.
910 if (!bFromRangeName
&& rAbs1
.Col() == 0 && rAbs2
.Col() == rLimits
.mnMaxCol
&&
911 rRef
.Ref1
.IsColRel() && rRef
.Ref2
.IsColRel())
912 return SINGLETON_ROW
;
914 return SINGLETON_NONE
;
917 virtual void makeRefStr(
918 ScSheetLimits
& rLimits
,
919 OUStringBuffer
& rBuffer
,
920 formula::FormulaGrammar::Grammar
/*eGram*/,
921 const ScAddress
& rPos
,
922 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
923 const ScComplexRefData
& rRef
,
925 bool bFromRangeName
) const override
927 // In case absolute/relative positions weren't separately available:
928 // transform relative to absolute!
929 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
931 aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
933 SingletonDisplay eSingleton
= bSingleRef
? SINGLETON_NONE
:
934 getSingletonDisplay( rLimits
, aAbs1
, aAbs2
, rRef
, bFromRangeName
);
935 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref1
, aAbs1
, false, false, eSingleton
);
939 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), false,
944 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
948 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
950 case ScCompiler::Convention::SHEET_SEPARATOR
:
957 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
958 const ScDocument
& rDoc
,
959 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
961 return lcl_parseExternalName(rSymbol
, rFile
, rName
, '#', rDoc
, pExternalLinks
);
964 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
965 const OUString
& rName
) const override
967 return lcl_makeExternalNameStr( rFile
, rName
, '#', false);
970 static bool makeExternalSingleRefStr(
971 const ScSheetLimits
& rLimits
,
972 OUStringBuffer
& rBuffer
, const OUString
& rFileName
, const OUString
& rTabName
,
973 const ScSingleRefData
& rRef
, const ScAddress
& rPos
, bool bDisplayTabName
, bool bEncodeUrl
)
975 ScAddress aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
982 aFile
= INetURLObject::decode(rFileName
, INetURLObject::DecodeMechanism::Unambiguous
);
984 rBuffer
.append("'" + aFile
.replaceAll("'", "''") + "'#");
986 if (!rRef
.IsTabRel())
988 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
993 if (!rRef
.IsColRel())
995 MakeColStr( rLimits
, rBuffer
, aAbsRef
.Col());
996 if (!rRef
.IsRowRel())
998 MakeRowStr( rLimits
, rBuffer
, aAbsRef
.Row());
1003 static void makeExternalRefStrImpl(
1004 const ScSheetLimits
& rLimits
,
1005 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, const OUString
& rFileName
,
1006 const OUString
& rTabName
, const ScSingleRefData
& rRef
, bool bODF
)
1009 rBuffer
.append( '[');
1011 bool bEncodeUrl
= bODF
;
1012 makeExternalSingleRefStr(rLimits
, rBuffer
, rFileName
, rTabName
, rRef
, rPos
, true, bEncodeUrl
);
1014 rBuffer
.append( ']');
1017 virtual void makeExternalRefStr(
1018 ScSheetLimits
& rLimits
,
1019 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1020 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1022 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabName
, rRef
, false);
1025 static void makeExternalRefStrImpl(
1026 const ScSheetLimits
& rLimits
,
1027 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, const OUString
& rFileName
,
1028 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1029 const ScComplexRefData
& rRef
, bool bODF
)
1031 ScRange aAbsRange
= rRef
.toAbs(rLimits
, rPos
);
1034 rBuffer
.append( '[');
1035 // Ensure that there's always a closing bracket, no premature returns.
1036 bool bEncodeUrl
= bODF
;
1040 if (!makeExternalSingleRefStr(rLimits
, rBuffer
, rFileName
, rTabName
, rRef
.Ref1
, rPos
, true, bEncodeUrl
))
1043 rBuffer
.append(':');
1045 OUString aLastTabName
;
1046 bool bDisplayTabName
= (aAbsRange
.aStart
.Tab() != aAbsRange
.aEnd
.Tab());
1047 if (bDisplayTabName
)
1049 // Get the name of the last table.
1050 if (!lcl_getLastTabName(aLastTabName
, rTabName
, rTabNames
, aAbsRange
))
1052 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1053 // aLastTabName contains #REF!, proceed.
1057 rBuffer
.append( '.'); // need at least the sheet separator in ODF
1058 makeExternalSingleRefStr(rLimits
,
1059 rBuffer
, rFileName
, aLastTabName
, rRef
.Ref2
, rPos
, bDisplayTabName
, bEncodeUrl
);
1063 rBuffer
.append( ']');
1066 virtual void makeExternalRefStr(
1067 ScSheetLimits
& rLimits
,
1068 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1069 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1070 const ScComplexRefData
& rRef
) const override
1072 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabNames
, rTabName
, rRef
, false);
1076 struct ConventionOOO_A1_ODF
: public ConventionOOO_A1
1078 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF
) { }
1080 virtual void makeRefStr(
1081 ScSheetLimits
& rLimits
,
1082 OUStringBuffer
& rBuffer
,
1083 formula::FormulaGrammar::Grammar eGram
,
1084 const ScAddress
& rPos
,
1085 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1086 const ScComplexRefData
& rRef
,
1088 bool bFromRangeName
) const override
1090 rBuffer
.append('[');
1091 // In case absolute/relative positions weren't separately available:
1092 // transform relative to absolute!
1093 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
1095 aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
1097 if (FormulaGrammar::isODFF(eGram
) && (rRef
.Ref1
.IsDeleted() || !rLimits
.ValidAddress(aAbs1
) ||
1098 (!bSingleRef
&& (rRef
.Ref2
.IsDeleted() || !rLimits
.ValidAddress(aAbs2
)))))
1100 rBuffer
.append(rErrRef
);
1101 // For ODFF write [#REF!], but not for PODF so apps reading ODF
1102 // 1.0/1.1 may have a better chance if they implemented the old
1107 SingletonDisplay eSingleton
= bSingleRef
? SINGLETON_NONE
:
1108 getSingletonDisplay( rLimits
, aAbs1
, aAbs2
, rRef
, bFromRangeName
);
1109 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref1
, aAbs1
, false, true, eSingleton
);
1112 rBuffer
.append(':');
1113 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), true,
1117 rBuffer
.append(']');
1120 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1121 const OUString
& rName
) const override
1123 return lcl_makeExternalNameStr( rFile
, rName
, '#', true);
1126 virtual void makeExternalRefStr(
1127 ScSheetLimits
& rLimits
,
1128 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1129 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1131 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabName
, rRef
, true);
1134 virtual void makeExternalRefStr(
1135 ScSheetLimits
& rLimits
,
1136 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1137 const std::vector
<OUString
>& rTabNames
,
1138 const OUString
& rTabName
, const ScComplexRefData
& rRef
) const override
1140 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabNames
, rTabName
, rRef
, true);
1146 virtual ~ConventionXL()
1151 const ScSheetLimits
& rLimits
,
1152 const ScAddress
& rPos
, const std::vector
<OUString
>& rTabNames
,
1153 const ScSingleRefData
& rRef
, OUString
& rTabName
)
1155 ScAddress aAbs
= rRef
.toAbs(rLimits
, rPos
);
1156 if (rRef
.IsTabDeleted() || o3tl::make_unsigned(aAbs
.Tab()) >= rTabNames
.size())
1158 rTabName
= ScResId( STR_NO_REF_TABLE
);
1161 rTabName
= rTabNames
[aAbs
.Tab()];
1164 static void MakeTabStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuf
,
1165 const ScAddress
& rPos
,
1166 const std::vector
<OUString
>& rTabNames
,
1167 const ScComplexRefData
& rRef
,
1170 if( !rRef
.Ref1
.IsFlag3D() )
1173 OUString aStartTabName
, aEndTabName
;
1175 GetTab(rLimits
, rPos
, rTabNames
, rRef
.Ref1
, aStartTabName
);
1177 if( !bSingleRef
&& rRef
.Ref2
.IsFlag3D() )
1179 GetTab(rLimits
, rPos
, rTabNames
, rRef
.Ref2
, aEndTabName
);
1182 const sal_Int32 nQuotePos
= rBuf
.getLength();
1183 rBuf
.append( aStartTabName
);
1184 if( !bSingleRef
&& rRef
.Ref2
.IsFlag3D() && aStartTabName
!= aEndTabName
)
1186 ScCompiler::FormExcelSheetRange( rBuf
, nQuotePos
, aEndTabName
);
1192 static sal_Unicode
getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType
)
1196 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
1198 case ScCompiler::Convention::SHEET_SEPARATOR
:
1204 static bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1205 const ScDocument
& rDoc
,
1206 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
1208 return lcl_parseExternalName( rSymbol
, rFile
, rName
, '!', rDoc
, pExternalLinks
);
1211 static OUString
makeExternalNameStr( const OUString
& rFile
, const OUString
& rName
)
1213 return lcl_makeExternalNameStr( rFile
, rName
, '!', false);
1216 static void makeExternalDocStr( OUStringBuffer
& rBuffer
, std::u16string_view rFullName
)
1218 // Format that is easier to deal with inside OOo, because we use file
1219 // URL, and all characters are allowed. Check if it makes sense to do
1220 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1221 // and allows relative file path.
1223 // ['file:///path/to/source/filename.xls']
1225 rBuffer
.append('[');
1226 rBuffer
.append('\'');
1227 OUString aFullName
= INetURLObject::decode(rFullName
, INetURLObject::DecodeMechanism::Unambiguous
);
1229 const sal_Unicode
* pBuf
= aFullName
.getStr();
1230 sal_Int32 nLen
= aFullName
.getLength();
1231 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
1233 const sal_Unicode c
= pBuf
[i
];
1238 rBuffer
.append('\'');
1239 rBuffer
.append(']');
1242 static void makeExternalTabNameRange( OUStringBuffer
& rBuf
, const OUString
& rTabName
,
1243 const vector
<OUString
>& rTabNames
,
1244 const ScRange
& rRef
)
1246 OUString aLastTabName
;
1247 if (!lcl_getLastTabName(aLastTabName
, rTabName
, rTabNames
, rRef
))
1249 ScRangeStringConverter::AppendTableName(rBuf
, aLastTabName
);
1253 ScRangeStringConverter::AppendTableName(rBuf
, rTabName
);
1254 if (rTabName
!= aLastTabName
)
1257 ScRangeStringConverter::AppendTableName(rBuf
, aLastTabName
);
1261 virtual void parseExternalDocName( const OUString
& rFormula
, sal_Int32
& rSrcPos
) const
1263 sal_Int32 nLen
= rFormula
.getLength();
1264 const sal_Unicode
* p
= rFormula
.getStr();
1265 sal_Unicode cPrev
= 0;
1266 for (sal_Int32 i
= rSrcPos
; i
< nLen
; ++i
)
1268 sal_Unicode c
= p
[i
];
1271 // first character must be '['.
1275 else if (i
== rSrcPos
+ 1)
1277 // second character must be a single quote.
1284 // two successive single quote is treated as a single
1292 // valid source document path found. Increment the
1293 // current position to skip the source path.
1295 if (rSrcPos
>= nLen
)
1304 // any other character
1305 if (i
> rSrcPos
+ 2 && cPrev
== '\'')
1306 // unless it's the 3rd character, a normal character
1307 // following immediately a single quote is invalid.
1315 struct ConventionXL_A1
: public Convention_A1
, public ConventionXL
1317 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1
) { }
1318 explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv
) : Convention_A1( eConv
) { }
1320 static void makeSingleCellStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbs
)
1322 if (!rRef
.IsColRel())
1324 MakeColStr(rLimits
, rBuf
, rAbs
.Col());
1325 if (!rRef
.IsRowRel())
1327 MakeRowStr(rLimits
, rBuf
, rAbs
.Row());
1330 virtual void makeRefStr(
1331 ScSheetLimits
& rLimits
,
1332 OUStringBuffer
& rBuf
,
1333 formula::FormulaGrammar::Grammar
/*eGram*/,
1334 const ScAddress
& rPos
,
1335 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1336 const ScComplexRefData
& rRef
,
1338 bool /*bFromRangeName*/ ) const override
1340 ScComplexRefData
aRef( rRef
);
1342 // Play fast and loose with invalid refs. There is not much point in producing
1343 // Foo!A1:#REF! versus #REF! at this point
1344 ScAddress aAbs1
= aRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
1346 MakeTabStr(rLimits
, rBuf
, rPos
, rTabNames
, aRef
, bSingleRef
);
1348 if (!rLimits
.ValidAddress(aAbs1
))
1350 rBuf
.append(rErrRef
);
1356 aAbs2
= aRef
.Ref2
.toAbs(rLimits
, rPos
);
1357 if (!rLimits
.ValidAddress(aAbs2
))
1359 rBuf
.append(rErrRef
);
1363 if (aAbs1
.Col() == 0 && aAbs2
.Col() >= rLimits
.mnMaxCol
)
1365 if (!aRef
.Ref1
.IsRowRel())
1367 MakeRowStr(rLimits
, rBuf
, aAbs1
.Row());
1369 if (!aRef
.Ref2
.IsRowRel())
1371 MakeRowStr(rLimits
, rBuf
, aAbs2
.Row());
1375 if (aAbs1
.Row() == 0 && aAbs2
.Row() >= rLimits
.mnMaxRow
)
1377 if (!aRef
.Ref1
.IsColRel())
1379 MakeColStr(rLimits
, rBuf
, aAbs1
.Col());
1381 if (!aRef
.Ref2
.IsColRel())
1383 MakeColStr(rLimits
, rBuf
, aAbs2
.Col());
1388 makeSingleCellStr(rLimits
, rBuf
, aRef
.Ref1
, aAbs1
);
1389 if (!bSingleRef
&& (aAbs1
.Row() != aAbs2
.Row() || aAbs1
.Col() != aAbs2
.Col()))
1392 makeSingleCellStr(rLimits
, rBuf
, aRef
.Ref2
, aAbs2
);
1396 virtual ParseResult
parseAnyToken( const OUString
& rFormula
,
1398 const CharClass
* pCharClass
,
1399 bool bGroupSeparator
) const override
1401 parseExternalDocName(rFormula
, nSrcPos
);
1404 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
1407 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
1408 KParseTokens::ASC_UNDERSCORE
| KParseTokens::ASC_DOLLAR
;
1409 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
1410 // '?' allowed in range names
1411 static constexpr OUString
aAddAllowed(u
"?!"_ustr
);
1412 return pCharClass
->parseAnyToken( rFormula
,
1413 nSrcPos
, nStartFlags
, aAddAllowed
,
1414 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
1418 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
1420 return ConventionXL::getSpecialSymbol(eSymType
);
1423 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1424 const ScDocument
& rDoc
,
1425 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
1427 return ConventionXL::parseExternalName( rSymbol
, rFile
, rName
, rDoc
, pExternalLinks
);
1430 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1431 const OUString
& rName
) const override
1433 return ConventionXL::makeExternalNameStr(rFile
, rName
);
1436 virtual void makeExternalRefStr(
1437 ScSheetLimits
& rLimits
,
1438 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1439 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1441 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1442 // This is a little different from the format Excel uses, as Excel
1443 // puts [] only around the file name. But we need to enclose the
1444 // whole file path with [] because the file name can contain any
1447 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1448 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
1449 rBuffer
.append('!');
1451 makeSingleCellStr(rLimits
, rBuffer
, rRef
, rRef
.toAbs(rLimits
, rPos
));
1454 virtual void makeExternalRefStr(
1455 ScSheetLimits
& rLimits
,
1456 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1457 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1458 const ScComplexRefData
& rRef
) const override
1460 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1462 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1463 ConventionXL::makeExternalTabNameRange(rBuffer
, rTabName
, rTabNames
, aAbsRef
);
1464 rBuffer
.append('!');
1466 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1467 if (aAbsRef
.aStart
!= aAbsRef
.aEnd
)
1469 rBuffer
.append(':');
1470 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1475 struct ConventionXL_OOX
: public ConventionXL_A1
1477 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX
) { }
1479 virtual void makeRefStr( ScSheetLimits
& rLimits
,
1480 OUStringBuffer
& rBuf
,
1481 formula::FormulaGrammar::Grammar eGram
,
1482 const ScAddress
& rPos
,
1483 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1484 const ScComplexRefData
& rRef
,
1486 bool bFromRangeName
) const override
1488 // In OOXML relative references in named expressions are relative to
1489 // column 0 and row 0. Relative sheet references don't exist.
1490 ScAddress
aPos( rPos
);
1493 // XXX NOTE: by decrementing the reference position we may end up
1494 // with resolved references with negative values. There's no proper
1495 // way to solve that or wrap them around without sheet dimensions
1496 // that are stored along. That, or blindly assume fixed dimensions
1497 // here and in import.
1498 /* TODO: maybe do that blind fixed dimensions wrap? */
1503 if (rRef
.Ref1
.IsDeleted() || (!bSingleRef
&& rRef
.Ref2
.IsDeleted()))
1505 // For OOXML write plain "#REF!" instead of detailed sheet/col/row
1507 rBuf
.append(rErrRef
);
1512 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
);
1513 if (!rLimits
.ValidAddress(aAbs1
)
1514 || o3tl::make_unsigned(aAbs1
.Tab()) >= rTabNames
.size())
1516 rBuf
.append(rErrRef
);
1523 ScAddress aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
1524 if (!rLimits
.ValidAddress(aAbs2
)
1525 || o3tl::make_unsigned(aAbs2
.Tab()) >= rTabNames
.size())
1527 rBuf
.append(rErrRef
);
1532 ConventionXL_A1::makeRefStr( rLimits
, rBuf
, eGram
, aPos
, rErrRef
, rTabNames
, rRef
, bSingleRef
, bFromRangeName
);
1535 virtual OUString
makeExternalNameStr( sal_uInt16 nFileId
, const OUString
& /*rFile*/,
1536 const OUString
& rName
) const override
1538 // [N]!DefinedName is a workbook global name.
1539 return OUString( "[" + OUString::number(nFileId
+1) + "]!" + rName
);
1541 /* TODO: add support for sheet local names, would be
1542 * [N]'Sheet Name'!DefinedName
1543 * Similar to makeExternalRefStr() but with DefinedName instead of
1547 virtual void parseExternalDocName(const OUString
& rFormula
, sal_Int32
& rSrcPos
) const override
1549 sal_Int32 nLen
= rFormula
.getLength();
1550 const sal_Unicode
* p
= rFormula
.getStr();
1551 for (sal_Int32 i
= rSrcPos
; i
< nLen
; ++i
)
1553 sal_Unicode c
= p
[i
];
1556 // first character must be '['.
1568 virtual void makeExternalRefStr(
1569 ScSheetLimits
& rLimits
,
1570 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16 nFileId
, const OUString
& /*rFileName*/,
1571 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1573 // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
1574 // Where N is a 1-based positive integer number of a file name in OOXML
1575 // xl/externalLinks/externalLinkN.xml
1577 OUString
aQuotedTab( rTabName
);
1578 ScCompiler::CheckTabQuotes( aQuotedTab
);
1579 if (!aQuotedTab
.isEmpty() && aQuotedTab
[0] == '\'')
1581 rBuffer
.append('\'');
1582 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1583 rBuffer
.append( aQuotedTab
.subView(1));
1587 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1588 rBuffer
.append( aQuotedTab
);
1590 rBuffer
.append('!');
1592 makeSingleCellStr(rLimits
, rBuffer
, rRef
, rRef
.toAbs(rLimits
, rPos
));
1595 virtual void makeExternalRefStr(
1596 ScSheetLimits
& rLimits
,
1597 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16 nFileId
, const OUString
& /*rFileName*/,
1598 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1599 const ScComplexRefData
& rRef
) const override
1601 // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
1602 // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
1603 // simpler to produce and more logical form with independently quoted
1604 // sheet names as well. The [N] having to be within the quoted sheet
1605 // name is ugly enough...
1607 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1609 OUStringBuffer aBuf
;
1610 ConventionXL::makeExternalTabNameRange( aBuf
, rTabName
, rTabNames
, aAbsRef
);
1611 if (!aBuf
.isEmpty() && aBuf
[0] == '\'')
1613 rBuffer
.append('\'');
1614 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1615 rBuffer
.append( aBuf
.subView(1));
1619 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1620 rBuffer
.append( aBuf
);
1622 rBuffer
.append('!');
1624 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1625 if (aAbsRef
.aStart
!= aAbsRef
.aEnd
)
1627 rBuffer
.append(':');
1628 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1632 static void makeExternalDocStr( OUStringBuffer
& rBuffer
, sal_uInt16 nFileId
)
1634 rBuffer
.append("[" + OUString::number( static_cast<sal_Int32
>(nFileId
+1) ) + "]");
1641 r1c1_add_col( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1644 if( rRef
.IsColRel() )
1646 SCCOL nCol
= rRef
.Col();
1648 rBuf
.append("[" + OUString::number(nCol
) + "]");
1651 rBuf
.append( static_cast<sal_Int32
>(rAbsRef
.Col() + 1) );
1654 r1c1_add_row( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1657 if( rRef
.IsRowRel() )
1659 if (rRef
.Row() != 0)
1661 rBuf
.append("[" + OUString::number(rRef
.Row()) + "]");
1665 rBuf
.append( rAbsRef
.Row() + 1 );
1670 struct ConventionXL_R1C1
: public ScCompiler::Convention
, public ConventionXL
1672 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1
) { }
1674 virtual void makeRefStr( ScSheetLimits
& rLimits
,
1675 OUStringBuffer
& rBuf
,
1676 formula::FormulaGrammar::Grammar
/*eGram*/,
1677 const ScAddress
& rPos
,
1678 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1679 const ScComplexRefData
& rRef
,
1681 bool /*bFromRangeName*/ ) const override
1683 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1684 ScComplexRefData
aRef( rRef
);
1686 MakeTabStr(rLimits
, rBuf
, rPos
, rTabNames
, aRef
, bSingleRef
);
1688 // Play fast and loose with invalid refs. There is not much point in producing
1689 // Foo!A1:#REF! versus #REF! at this point
1690 if (!rLimits
.ValidCol(aAbsRef
.aStart
.Col()) || !rLimits
.ValidRow(aAbsRef
.aStart
.Row()))
1692 rBuf
.append(rErrRef
);
1698 if (!rLimits
.ValidCol(aAbsRef
.aEnd
.Col()) || !rLimits
.ValidRow(aAbsRef
.aEnd
.Row()))
1700 rBuf
.append(rErrRef
);
1704 if (aAbsRef
.aStart
.Col() == 0 && aAbsRef
.aEnd
.Col() >= rLimits
.mnMaxCol
)
1706 r1c1_add_row(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1707 if (aAbsRef
.aStart
.Row() != aAbsRef
.aEnd
.Row() ||
1708 rRef
.Ref1
.IsRowRel() != rRef
.Ref2
.IsRowRel() )
1711 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1717 if (aAbsRef
.aStart
.Row() == 0 && aAbsRef
.aEnd
.Row() >= rLimits
.mnMaxRow
)
1719 r1c1_add_col(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1720 if (aAbsRef
.aStart
.Col() != aAbsRef
.aEnd
.Col() ||
1721 rRef
.Ref1
.IsColRel() != rRef
.Ref2
.IsColRel())
1724 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1730 r1c1_add_row(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1731 r1c1_add_col(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1732 // We can't parse a single col/row reference in the context of a R1C1
1733 // 3D reference back yet, otherwise (if Excel understands it) an
1734 // additional condition similar to ConventionXL_A1::makeRefStr() could
1737 // && (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || aAbsRef.aStart.Col() != aAbsRef.aEnd.Col())
1741 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1742 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1746 ParseResult
parseAnyToken( const OUString
& rFormula
,
1748 const CharClass
* pCharClass
,
1749 bool bGroupSeparator
) const override
1751 parseExternalDocName(rFormula
, nSrcPos
);
1754 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
1757 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
1758 KParseTokens::ASC_UNDERSCORE
;
1759 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
1760 // '?' allowed in range names
1761 static constexpr OUString
aAddAllowed(u
"?-[]!"_ustr
);
1763 return pCharClass
->parseAnyToken( rFormula
,
1764 nSrcPos
, nStartFlags
, aAddAllowed
,
1765 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
1769 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
1771 return ConventionXL::getSpecialSymbol(eSymType
);
1774 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1775 const ScDocument
& rDoc
,
1776 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
1778 return ConventionXL::parseExternalName( rSymbol
, rFile
, rName
, rDoc
, pExternalLinks
);
1781 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1782 const OUString
& rName
) const override
1784 return ConventionXL::makeExternalNameStr(rFile
, rName
);
1787 virtual void makeExternalRefStr(
1788 ScSheetLimits
& rLimits
,
1789 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1790 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1792 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1793 // This is a little different from the format Excel uses, as Excel
1794 // puts [] only around the file name. But we need to enclose the
1795 // whole file path with [] because the file name can contain any
1798 ScAddress aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1799 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1800 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
1801 rBuffer
.append('!');
1803 r1c1_add_row(rBuffer
, rRef
, aAbsRef
);
1804 r1c1_add_col(rBuffer
, rRef
, aAbsRef
);
1807 virtual void makeExternalRefStr(
1808 ScSheetLimits
& rLimits
,
1809 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1810 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1811 const ScComplexRefData
& rRef
) const override
1813 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1815 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1816 ConventionXL::makeExternalTabNameRange(rBuffer
, rTabName
, rTabNames
, aAbsRef
);
1817 rBuffer
.append('!');
1819 if (!rLimits
.ValidCol(aAbsRef
.aEnd
.Col()) || !rLimits
.ValidRow(aAbsRef
.aEnd
.Row()))
1821 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
1825 if (aAbsRef
.aStart
.Col() == 0 && aAbsRef
.aEnd
.Col() >= rLimits
.mnMaxCol
)
1827 r1c1_add_row(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1828 if (aAbsRef
.aStart
.Row() != aAbsRef
.aEnd
.Row() || rRef
.Ref1
.IsRowRel() != rRef
.Ref2
.IsRowRel())
1830 rBuffer
.append(':');
1831 r1c1_add_row(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1836 if (aAbsRef
.aStart
.Row() == 0 && aAbsRef
.aEnd
.Row() >= rLimits
.mnMaxRow
)
1838 r1c1_add_col(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1839 if (aAbsRef
.aStart
.Col() != aAbsRef
.aEnd
.Col() || rRef
.Ref1
.IsColRel() != rRef
.Ref2
.IsColRel())
1841 rBuffer
.append(':');
1842 r1c1_add_col(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1847 r1c1_add_row(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1848 r1c1_add_col(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1849 rBuffer
.append(':');
1850 r1c1_add_row(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1851 r1c1_add_col(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1854 virtual ScCharFlags
getCharTableFlags( sal_Unicode c
, sal_Unicode cLast
) const override
1856 ScCharFlags nFlags
= mpCharTable
[static_cast<sal_uInt8
>(c
)];
1857 if (c
== '-' && cLast
== '[')
1858 // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1859 nFlags
|= ScCharFlags::Ident
;
1866 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
, ScTokenArray
& rArr
,
1867 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1868 : FormulaCompiler(rArr
, bComputeII
, bMatrixFlag
),
1869 rDoc(rCxt
.getDoc()),
1871 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1872 mnCurrentSheetTab(-1),
1873 mnCurrentSheetEndPos(0),
1874 pCharClass(&ScGlobal::getCharClass()),
1875 mbCharClassesDiffer(false),
1876 mnPredetectedReference(0),
1877 mnRangeOpPosInSymbol(-1),
1878 pConv(GetRefConvention(FormulaGrammar::CONV_OOO
)),
1879 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE
),
1880 mbCloseBrackets(true),
1882 mbRefConventionChartOOXML(false),
1883 maTabNames(rCxt
.getTabNames())
1885 SetGrammar(rCxt
.getGrammar());
1886 m_oODFSavingVersion
= rCxt
.getODFSavingVersion();
1889 ScCompiler::ScCompiler( ScDocument
& rDocument
, const ScAddress
& rPos
, ScTokenArray
& rArr
,
1890 formula::FormulaGrammar::Grammar eGrammar
,
1891 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1892 : FormulaCompiler(rArr
, bComputeII
, bMatrixFlag
),
1895 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1896 mnCurrentSheetTab(-1),
1897 mnCurrentSheetEndPos(0),
1899 pCharClass( &ScGlobal::getCharClass() ),
1900 mbCharClassesDiffer(false),
1901 mnPredetectedReference(0),
1902 mnRangeOpPosInSymbol(-1),
1903 pConv( GetRefConvention( FormulaGrammar::CONV_OOO
) ),
1904 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE
),
1905 mbCloseBrackets( true ),
1907 mbRefConventionChartOOXML( false )
1909 SetGrammar( (eGrammar
== formula::FormulaGrammar::GRAM_UNSPECIFIED
) ?
1910 rDocument
.GetGrammar() :
1914 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
,
1915 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1916 : FormulaCompiler(bComputeII
, bMatrixFlag
),
1917 rDoc(rCxt
.getDoc()),
1919 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1920 mnCurrentSheetTab(-1),
1921 mnCurrentSheetEndPos(0),
1922 pCharClass(&ScGlobal::getCharClass()),
1923 mbCharClassesDiffer(false),
1924 mnPredetectedReference(0),
1925 mnRangeOpPosInSymbol(-1),
1926 pConv(GetRefConvention(FormulaGrammar::CONV_OOO
)),
1927 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE
),
1928 mbCloseBrackets(true),
1930 mbRefConventionChartOOXML(false),
1931 maTabNames(rCxt
.getTabNames())
1933 SetGrammar(rCxt
.getGrammar());
1936 ScCompiler::ScCompiler( ScDocument
& rDocument
, const ScAddress
& rPos
,
1937 formula::FormulaGrammar::Grammar eGrammar
,
1938 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1939 : FormulaCompiler(bComputeII
, bMatrixFlag
),
1942 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1943 mnCurrentSheetTab(-1),
1944 mnCurrentSheetEndPos(0),
1946 pCharClass( &ScGlobal::getCharClass() ),
1947 mbCharClassesDiffer(false),
1948 mnPredetectedReference(0),
1949 mnRangeOpPosInSymbol(-1),
1950 pConv( GetRefConvention( FormulaGrammar::CONV_OOO
) ),
1951 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE
),
1952 mbCloseBrackets( true ),
1954 mbRefConventionChartOOXML( false )
1956 SetGrammar( (eGrammar
== formula::FormulaGrammar::GRAM_UNSPECIFIED
) ?
1957 rDocument
.GetGrammar() :
1961 ScCompiler::~ScCompiler()
1965 void ScCompiler::CheckTabQuotes( OUString
& rString
,
1966 const FormulaGrammar::AddressConvention eConv
)
1968 sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
| KParseTokens::ASC_UNDERSCORE
;
1969 sal_Int32 nContFlags
= nStartFlags
;
1970 ParseResult aRes
= ScGlobal::getCharClass().parsePredefinedToken(
1971 KParseType::IDENTNAME
, rString
, 0, nStartFlags
, OUString(), nContFlags
, OUString());
1972 bool bNeedsQuote
= !((aRes
.TokenType
& KParseType::IDENTNAME
) && aRes
.EndPos
== rString
.getLength());
1977 case FormulaGrammar::CONV_UNSPECIFIED
:
1979 case FormulaGrammar::CONV_OOO
:
1980 case FormulaGrammar::CONV_XL_A1
:
1981 case FormulaGrammar::CONV_XL_R1C1
:
1982 case FormulaGrammar::CONV_XL_OOX
:
1983 case FormulaGrammar::CONV_ODF
:
1986 // escape embedded quotes
1987 rString
= rString
.replaceAll( "'", "''" );
1992 if ( !bNeedsQuote
&& CharClass::isAsciiNumeric( rString
) )
1994 // Prevent any possible confusion resulting from pure numeric sheet names.
2000 rString
= "'" + rString
+ "'";
2004 void ScCompiler::FormExcelSheetRange( OUStringBuffer
& rBuf
, sal_Int32 nQuotePos
, const OUString
& rEndTabName
)
2006 OUString
aEndTabName(rEndTabName
);
2007 if (nQuotePos
< rBuf
.getLength())
2009 const bool bQuoted2
= (!aEndTabName
.isEmpty() && aEndTabName
[0] == '\'');
2011 aEndTabName
= aEndTabName
.subView(1); // Sheet2'
2012 if (rBuf
[nQuotePos
] == '\'') // 'Sheet1'
2014 const sal_Int32 nLast
= rBuf
.getLength() - 1;
2015 if (rBuf
[nLast
] == '\'')
2016 rBuf
.remove(nLast
, 1); // 'Sheet1
2018 else if (bQuoted2
) // Sheet1
2020 rBuf
.insert(nQuotePos
, '\''); // 'Sheet1
2024 rBuf
.append( aEndTabName
);
2027 sal_Int32
ScCompiler::GetDocTabPos( const OUString
& rString
)
2029 if (rString
[0] != '\'')
2031 sal_Int32 nPos
= ScGlobal::FindUnquoted( rString
, SC_COMPILER_FILE_TAB_SEP
);
2032 // it must be 'Doc'#
2033 if (nPos
!= -1 && rString
[nPos
-1] != '\'')
2038 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv
)
2040 const Convention
* p
= GetRefConvention(eConv
);
2042 SetRefConvention(p
);
2045 const ScCompiler::Convention
* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv
)
2050 case FormulaGrammar::CONV_OOO
:
2052 static const ConventionOOO_A1 ConvOOO_A1
;
2055 case FormulaGrammar::CONV_ODF
:
2057 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF
;
2058 return &ConvOOO_A1_ODF
;
2060 case FormulaGrammar::CONV_XL_A1
:
2062 static const ConventionXL_A1 ConvXL_A1
;
2065 case FormulaGrammar::CONV_XL_R1C1
:
2067 static const ConventionXL_R1C1 ConvXL_R1C1
;
2068 return &ConvXL_R1C1
;
2070 case FormulaGrammar::CONV_XL_OOX
:
2072 static const ConventionXL_OOX ConvXL_OOX
;
2075 case FormulaGrammar::CONV_UNSPECIFIED
:
2083 void ScCompiler::SetRefConvention( const ScCompiler::Convention
*pConvP
)
2086 meGrammar
= FormulaGrammar::mergeToGrammar( meGrammar
, pConv
->meConv
);
2087 assert( FormulaGrammar::isSupported( meGrammar
));
2090 void ScCompiler::SetError(FormulaError nError
)
2092 if( pArr
->GetCodeError() == FormulaError::NONE
)
2093 pArr
->SetCodeError( nError
);
2096 static sal_Unicode
* lcl_UnicodeStrNCpy( sal_Unicode
* pDst
, const sal_Unicode
* pSrc
, sal_Int32 nMax
)
2098 const sal_Unicode
* const pStop
= pDst
+ nMax
;
2099 while ( pDst
< pStop
)
2107 // p1 MUST contain at least n characters, or terminate with NIL.
2108 // p2 MUST pass upper case letters, if any.
2109 // n MUST not be greater than length of p2
2110 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode
* p1
, const char* p2
, size_t n
)
2112 for (size_t i
=0; i
<n
; ++i
)
2118 if (p1
[i
] < 'a' || 'z' < p1
[i
])
2119 return false; // not a lower case letter
2120 if (p2
[i
] < 'A' || 'Z' < p2
[i
])
2121 return false; // not a letter to match
2122 if (p1
[i
] != p2
[i
] + 0x20)
2123 return false; // lower case doesn't match either
2130 void ScCompiler::addWhitespace( std::vector
<ScCompiler::Whitespace
> & rvSpaces
,
2131 ScCompiler::Whitespace
& rSpace
, sal_Unicode c
, sal_Int32 n
)
2133 if (rSpace
.cChar
!= c
)
2135 if (rSpace
.cChar
&& rSpace
.nCount
> 0)
2136 rvSpaces
.emplace_back(rSpace
);
2144 // Parses the formula into separate symbols for further processing.
2145 // XXX NOTE: this is a rough sketch of the original idea, there are other
2146 // states that were added and didn't make it into this table and things are
2147 // more complicated. Use the source, Luke.
2149 // initial state = GetChar
2151 // old state | read character | action | new state
2152 //---------------+-------------------+-----------------------+---------------
2153 // GetChar | ;()+-*/^=& | Symbol=char | Stop
2154 // | <> | Symbol=char | GetBool
2155 // | $ letter | Symbol=char | GetWord
2156 // | number | Symbol=char | GetValue
2157 // | " | none | GetString
2158 // | other | none | GetChar
2159 //---------------+-------------------+-----------------------+---------------
2160 // GetBool | => | Symbol=Symbol+char | Stop
2161 // | other | Dec(CharPos) | Stop
2162 //---------------+-------------------+-----------------------+---------------
2163 // GetWord | SepSymbol | Dec(CharPos) | Stop
2164 // | ()+-*/^=<>&~ | |
2165 // | space | Dec(CharPos) | Stop
2167 // | letter, number | Symbol=Symbol+char | GetWord
2168 // | other | error | Stop
2169 //---------------+-------------------+-----------------------+---------------
2170 // GetValue | ;()*/^=<>& | |
2171 // | space | Dec(CharPos) | Stop
2172 // | number E+-%,. | Symbol=Symbol+char | GetValue
2173 // | other | error | Stop
2174 //---------------+-------------------+-----------------------+---------------
2175 // GetString | " | none | Stop
2176 // | other | Symbol=Symbol+char | GetString
2177 //---------------+-------------------+-----------------------+---------------
2179 std::vector
<ScCompiler::Whitespace
> ScCompiler::NextSymbol(bool bInArray
)
2181 std::vector
<Whitespace
> vSpaces
;
2182 cSymbol
[MAXSTRLEN
] = 0; // end
2183 sal_Unicode
* pSym
= cSymbol
;
2184 const sal_Unicode
* const pStart
= aFormula
.getStr();
2185 const sal_Unicode
* pSrc
= pStart
+ nSrcPos
;
2187 sal_Unicode c
= *pSrc
;
2188 sal_Unicode cLast
= 0;
2189 bool bQuote
= false;
2190 mnRangeOpPosInSymbol
= -1;
2191 ScanState eState
= ssGetChar
;
2193 sal_Unicode cSep
= mxSymbols
->getSymbolChar( ocSep
);
2194 sal_Unicode cArrayColSep
= mxSymbols
->getSymbolChar( ocArrayColSep
);
2195 sal_Unicode cArrayRowSep
= mxSymbols
->getSymbolChar( ocArrayRowSep
);
2196 sal_Unicode cDecSep
= (mxSymbols
->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0]);
2197 sal_Unicode cDecSepAlt
= (mxSymbols
->isEnglishLocale() ? 0 : ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar());
2199 // special symbols specific to address convention used
2200 sal_Unicode cSheetPrefix
= pConv
->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX
);
2201 sal_Unicode cSheetSep
= pConv
->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR
);
2204 bool bAutoIntersection
= false;
2205 size_t nAutoIntersectionSpacesPos
= 0;
2207 bool bErrorConstantHadSlash
= false;
2208 mnPredetectedReference
= 0;
2209 // try to parse simple tokens before calling i18n parser
2210 while ((c
!= 0) && (eState
!= ssStop
) )
2213 ScCharFlags nMask
= GetCharTableFlags( c
, cLast
);
2215 // The parameter separator and the array column and row separators end
2216 // things unconditionally if not in string or reference.
2217 if (c
== cSep
|| (bInArray
&& (c
== cArrayColSep
|| c
== cArrayRowSep
)))
2221 // these are to be continued
2224 case ssGetReference
:
2225 case ssSkipReference
:
2226 case ssGetTableRefItem
:
2227 case ssGetTableRefColumn
:
2230 if (eState
== ssGetChar
)
2237 Label_MaskStateMachine
:
2242 // Order is important!
2243 if (eLastOp
== ocTableRefOpen
&& c
!= '[' && c
!= '#' && c
!= ']')
2246 eState
= ssGetTableRefColumn
;
2248 else if( nMask
& ScCharFlags::OdfLabelOp
)
2250 // '!!' automatic intersection
2251 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::OdfLabelOp
)
2253 /* TODO: For now the UI "space operator" is used, this
2254 * could be enhanced using a specialized OpCode to get
2255 * rid of the space ambiguity, which would need some
2256 * places to be adapted though. And we would still need
2257 * to support the ambiguous space operator for UI
2258 * purposes anyway. However, we then could check for
2259 * invalid usage of '!!', which currently isn't
2261 if (!bAutoIntersection
)
2264 // Add 2 because it must match the character count
2266 addWhitespace( vSpaces
, aSpace
, 0x20, 2);
2267 // Position of Whitespace where it will be added to
2269 nAutoIntersectionSpacesPos
= vSpaces
.size();
2270 bAutoIntersection
= true;
2280 nMask
&= ~ScCharFlags::OdfLabelOp
;
2281 goto Label_MaskStateMachine
;
2284 else if( nMask
& ScCharFlags::OdfNameMarker
)
2286 // '$$' defined name marker
2287 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::OdfNameMarker
)
2289 // both eaten, not added to pSym
2294 nMask
&= ~ScCharFlags::OdfNameMarker
;
2295 goto Label_MaskStateMachine
;
2298 else if( nMask
& ScCharFlags::Char
)
2300 // '[' is a special case in Excel syntax, it can start an
2301 // external reference, ID in OOXML like [1]Sheet1!A1 or
2302 // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2303 // [filename]Sheet!R1C1 that needs to be scanned
2304 // entirely, or can be ocTableRefOpen, of which the first
2305 // transforms an ocDBArea into an ocTableRef.
2306 if (c
== '[' && FormulaGrammar::isExcelSyntax( meGrammar
)
2307 && eLastOp
!= ocDBArea
&& maTableRefs
.empty())
2309 // [0]!Global_Range_Name, is a special case in OOXML
2310 // syntax, where the '0' is referencing to self and we
2311 // do not need it, so we should skip it, in order to
2312 // later it will be more recognisable for IsNamedRange.
2313 if (FormulaGrammar::isRefConventionOOXML(meGrammar
) &&
2314 pSrc
[0] == '0' && pSrc
[1] == ']' && pSrc
[2] == '!')
2321 nMask
&= ~ScCharFlags::Char
;
2322 goto Label_MaskStateMachine
;
2330 else if( nMask
& ScCharFlags::OdfLBracket
)
2332 // eaten, not added to pSym
2333 eState
= ssGetReference
;
2334 mnPredetectedReference
= 1;
2336 else if( nMask
& ScCharFlags::CharBool
)
2341 else if( nMask
& ScCharFlags::CharValue
)
2344 eState
= ssGetValue
;
2346 else if( nMask
& ScCharFlags::CharString
)
2349 eState
= ssGetString
;
2351 else if( nMask
& ScCharFlags::CharErrConst
)
2355 if (!maTableRefs
.empty() && ((nLevel
= maTableRefs
.back().mnLevel
) == 2 || nLevel
== 1))
2356 eState
= ssGetTableRefItem
;
2358 eState
= ssGetErrorConstant
;
2360 else if( nMask
& ScCharFlags::CharDontCare
)
2362 addWhitespace( vSpaces
, aSpace
, c
);
2364 else if( nMask
& ScCharFlags::CharIdent
)
2365 { // try to get a simple ASCII identifier before calling
2366 // i18n, to gain performance during import
2368 eState
= ssGetIdent
;
2379 if ( nMask
& ScCharFlags::Ident
)
2380 { // This catches also $Sheet1.A$1, for example.
2381 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2383 SetError(FormulaError::StringOverflow
);
2389 else if (c
== '#' && lcl_isUnicodeIgnoreAscii( pSrc
, "REF!", 4))
2391 // Completely ugly means to catch broken
2392 // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2393 // references that were written in ODF named ranges
2394 // (without embracing [] hence no predetected reference)
2395 // and to OOXML and handle them as one symbol.
2396 // Also catches these in UI, so we can process them
2401 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2403 SetError(FormulaError::StringOverflow
);
2414 c
= *((--pSrc
)-1); // position last/next character correctly
2416 else if (c
== ':' && mnRangeOpPosInSymbol
< 0)
2418 // One range operator may form Sheet1.A:A, which we need to
2419 // pass as one entity to IsReference().
2420 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2422 SetError(FormulaError::StringOverflow
);
2427 mnRangeOpPosInSymbol
= pSym
- &cSymbol
[0];
2431 else if ( 128 <= c
|| '\'' == c
)
2432 { // High values need reparsing with i18n,
2433 // single quoted $'sheet' names too (otherwise we'd had to
2434 // implement everything twice).
2447 if( nMask
& ScCharFlags::Bool
)
2461 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2463 SetError(FormulaError::StringOverflow
);
2466 else if (c
== cDecSep
|| (cDecSepAlt
&& c
== cDecSepAlt
))
2470 // reparse with i18n, may be numeric sheet name as well
2477 else if( nMask
& ScCharFlags::Value
)
2479 else if( nMask
& ScCharFlags::ValueSep
)
2484 else if (c
== 'E' || c
== 'e')
2486 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::ValueExp
)
2490 // reparse with i18n
2495 else if( nMask
& ScCharFlags::ValueSign
)
2497 if (((cLast
== 'E') || (cLast
== 'e')) &&
2498 (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::ValueValue
))
2510 // reparse with i18n
2518 if( nMask
& ScCharFlags::StringSep
)
2523 bQuote
= true; // "" => literal "
2532 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2534 SetError(FormulaError::StringOverflow
);
2535 eState
= ssSkipString
;
2543 if( nMask
& ScCharFlags::StringSep
)
2546 case ssGetErrorConstant
:
2548 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2549 // BUT, in UI these may have been translated! So don't
2550 // check for ASCII alnum. Note that this construct can't be
2551 // parsed with i18n.
2552 /* TODO: be strict when reading ODFF, check for ASCII alnum
2553 * and proper continuation after '/'. However, even with
2554 * the lax parsing only the error constants we have defined
2555 * as opcode symbols will be recognized and others result
2556 * in ocBad, so the result is actually conformant. */
2562 // Check if this is #REF! that starts an invalid reference.
2563 // Note we have an implicit '!' here at the end.
2564 if (pSym
- &cSymbol
[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol
, "#REF", 4) &&
2565 (GetCharTableFlags( *pSrc
, c
) & ScCharFlags::Ident
))
2566 eState
= ssGetIdent
;
2572 if (!bErrorConstantHadSlash
)
2573 bErrorConstantHadSlash
= true;
2580 else if ((nMask
& ScCharFlags::WordSep
) ||
2581 (c
< 128 && !rtl::isAsciiAlphanumeric( c
)))
2590 if (pSym
== &cSymbol
[ MAXSTRLEN
])
2592 SetError( FormulaError::StringOverflow
);
2600 case ssGetTableRefItem
:
2602 // Scan whatever up to the next ']' closer.
2605 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2607 SetError( FormulaError::StringOverflow
);
2620 case ssGetTableRefColumn
:
2622 // Scan whatever up to the next unescaped ']' closer.
2623 if (c
!= ']' || cLast
== '\'')
2625 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2627 SetError( FormulaError::StringOverflow
);
2640 case ssGetReference
:
2641 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2643 SetError( FormulaError::StringOverflow
);
2644 eState
= ssSkipReference
;
2647 case ssSkipReference
:
2648 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2649 // mandatory also if no sheet name. 'External'# is optional,
2650 // sheet name is optional, quotes around sheet name are
2651 // optional if no quote contained. [#REF!] is valid.
2652 // 2nd usage: ['Sheet'.$$'DefinedName']
2653 // 3rd usage: ['External'#$$'DefinedName']
2654 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2655 // Also for all these names quotes are optional if no quote
2659 // nRefInName: 0 := not in sheet name yet. 'External'
2660 // is parsed as if it was a sheet name and nRefInName
2661 // is reset when # is encountered immediately after closing
2662 // quote. Same with 'DefinedName', nRefInName is cleared
2663 // when : is encountered.
2665 // Encountered leading $ before sheet name.
2666 constexpr int kDollar
= (1 << 1);
2667 // Encountered ' opening quote, which may be after $ or
2669 constexpr int kOpen
= (1 << 2);
2670 // Somewhere in name.
2671 constexpr int kName
= (1 << 3);
2672 // Encountered ' in name, will be cleared if double or
2673 // transformed to kClose if not, in which case kOpen is
2675 constexpr int kQuote
= (1 << 4);
2676 // Past ' closing quote.
2677 constexpr int kClose
= (1 << 5);
2678 // Encountered # file/sheet separator.
2679 constexpr int kFileSep
= (1 << 6);
2680 // Past . sheet name separator.
2681 constexpr int kPast
= (1 << 7);
2682 // Marked name $$ follows sheet name separator, detected
2683 // while we're still on the separator. Will be cleared when
2684 // entering the name.
2685 constexpr int kMarkAhead
= (1 << 8);
2686 // In marked defined name.
2687 constexpr int kDefName
= (1 << 9);
2688 // Encountered # of #REF!
2689 constexpr int kRefErr
= (1 << 10);
2691 bool bAddToSymbol
= true;
2692 if ((nMask
& ScCharFlags::OdfRBracket
) && !(nRefInName
& kOpen
))
2694 OSL_ENSURE( nRefInName
& (kPast
| kDefName
| kRefErr
),
2695 "ScCompiler::NextSymbol: reference: "
2696 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2697 // eaten, not added to pSym
2698 bAddToSymbol
= false;
2701 else if (cSheetSep
== c
&& nRefInName
== 0)
2703 // eat it, no sheet name [.A1]
2704 bAddToSymbol
= false;
2705 nRefInName
|= kPast
;
2706 if ('$' == pSrc
[0] && '$' == pSrc
[1])
2707 nRefInName
|= kMarkAhead
;
2709 else if (!(nRefInName
& kPast
) || (nRefInName
& (kMarkAhead
| kDefName
)))
2711 // Not in col/row yet.
2713 if (SC_COMPILER_FILE_TAB_SEP
== c
&& (nRefInName
& kFileSep
))
2715 else if ('$' == c
&& '$' == pSrc
[0] && !(nRefInName
& kOpen
))
2717 nRefInName
&= ~kMarkAhead
;
2718 if (!(nRefInName
& kDefName
))
2720 // eaten, not added to pSym (2 chars)
2721 bAddToSymbol
= false;
2723 nRefInName
&= kPast
;
2724 nRefInName
|= kDefName
;
2728 // ScAddress::Parse() will recognize this as
2730 if (eState
!= ssSkipReference
)
2734 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2736 SetError( FormulaError::StringOverflow
);
2742 bAddToSymbol
= false;
2745 else if (cSheetPrefix
== c
&& nRefInName
== 0)
2746 nRefInName
|= kDollar
;
2749 // TODO: The conventions' parseExternalName()
2750 // should handle quoted names, but as long as they
2751 // don't remove non-embedded quotes here.
2752 if (!(nRefInName
& kName
))
2754 nRefInName
|= (kOpen
| kName
);
2755 bAddToSymbol
= !(nRefInName
& kDefName
);
2757 else if (!(nRefInName
& kOpen
))
2759 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2760 "a ''' without the name being enclosed in '...' violates ODF spec");
2762 else if (nRefInName
& kQuote
)
2764 // escaped embedded quote
2765 nRefInName
&= ~kQuote
;
2772 // escapes embedded quote
2773 nRefInName
|= kQuote
;
2775 case SC_COMPILER_FILE_TAB_SEP
:
2776 // sheet name should follow
2777 nRefInName
|= kFileSep
;
2780 // quote not followed by quote => close
2781 nRefInName
|= kClose
;
2782 nRefInName
&= ~kOpen
;
2784 bAddToSymbol
= !(nRefInName
& kDefName
);
2787 else if ('#' == c
&& nRefInName
== 0)
2788 nRefInName
|= kRefErr
;
2789 else if (cSheetSep
== c
&& !(nRefInName
& kOpen
))
2791 // unquoted sheet name separator
2792 nRefInName
|= kPast
;
2793 if ('$' == pSrc
[0] && '$' == pSrc
[1])
2794 nRefInName
|= kMarkAhead
;
2796 else if (':' == c
&& !(nRefInName
& kOpen
))
2798 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2799 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2801 ++mnPredetectedReference
;
2803 else if (!(nRefInName
& kName
))
2805 // start unquoted name
2806 nRefInName
|= kName
;
2813 ++mnPredetectedReference
;
2815 if (bAddToSymbol
&& eState
!= ssSkipReference
)
2816 *pSym
++ = c
; // everything is part of reference
2820 ; // nothing, prevent warning
2827 if (aSpace
.nCount
&& aSpace
.cChar
)
2828 vSpaces
.emplace_back(aSpace
);
2832 const sal_Int32 nOldSrcPos
= nSrcPos
;
2833 for (const auto& r
: vSpaces
)
2834 nSrcPos
+= r
.nCount
;
2835 // If group separator is not a possible operator and not one of any
2836 // separators then it may be parsed away in numbers. This is
2837 // specifically the case with NO-BREAK SPACE, which actually triggers
2838 // the bi18n case (which we don't want to include as yet another
2839 // special case above as it is rare enough and doesn't generally occur
2841 const sal_Unicode cGroupSep
= ScGlobal::getLocaleData().getNumThousandSep()[0];
2842 const bool bGroupSeparator
= (128 <= cGroupSep
&& cGroupSep
!= cSep
&&
2843 cGroupSep
!= cArrayColSep
&& cGroupSep
!= cArrayRowSep
&&
2844 cGroupSep
!= cDecSep
&& cGroupSep
!= cDecSepAlt
&&
2845 cGroupSep
!= cSheetPrefix
&& cGroupSep
!= cSheetSep
);
2846 // If a numeric context triggered bi18n then use the default locale's
2847 // CharClass, this may accept group separator as well.
2848 const CharClass
* pMyCharClass
= (ScGlobal::getCharClass().isDigit( OUString(pStart
[nSrcPos
]), 0) ?
2849 &ScGlobal::getCharClass() : pCharClass
);
2850 OUStringBuffer aSymbol
;
2851 mnRangeOpPosInSymbol
= -1;
2852 FormulaError nErr
= FormulaError::NONE
;
2856 // special case (e.g. $'sheetname' in OOO A1)
2857 if ( pStart
[nSrcPos
] == cSheetPrefix
&& pStart
[nSrcPos
+1] == '\'' )
2858 aSymbol
.append(pStart
[nSrcPos
++]);
2860 ParseResult aRes
= pConv
->parseAnyToken( aFormula
, nSrcPos
, pMyCharClass
, bGroupSeparator
);
2862 if ( !aRes
.TokenType
)
2864 nErr
= FormulaError::IllegalChar
;
2865 SetError( nErr
); // parsed chars as string
2867 if ( aRes
.EndPos
<= nSrcPos
)
2869 // Could not parse anything meaningful.
2870 assert(!aRes
.TokenType
);
2871 nErr
= FormulaError::IllegalChar
;
2873 // Caller has to act on an empty symbol for
2874 // nSrcPos < aFormula.getLength()
2875 nSrcPos
= nOldSrcPos
;
2876 aSymbol
.setLength(0);
2880 // When having parsed a second reference part, ensure that the
2881 // i18n parser did not mistakenly parse a number that included
2882 // a separator which happened to be meant as a parameter
2883 // separator instead.
2884 if (mnRangeOpPosInSymbol
>= 0 && (aRes
.TokenType
& KParseType::ASC_NUMBER
))
2886 for (sal_Int32 i
= nSrcPos
; i
< aRes
.EndPos
; ++i
)
2888 if (pStart
[i
] == cSep
)
2889 aRes
.EndPos
= i
; // also ends for
2892 aSymbol
.append( pStart
+ nSrcPos
, aRes
.EndPos
- nSrcPos
);
2893 nSrcPos
= aRes
.EndPos
;
2894 c
= pStart
[nSrcPos
];
2895 if ( aRes
.TokenType
& KParseType::SINGLE_QUOTE_NAME
)
2896 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2897 bi18n
= (c
== cSheetSep
|| c
== SC_COMPILER_FILE_TAB_SEP
);
2899 // One range operator restarts parsing for second reference.
2900 if (c
== ':' && mnRangeOpPosInSymbol
< 0)
2902 mnRangeOpPosInSymbol
= aSymbol
.getLength();
2906 aSymbol
.append(pStart
[nSrcPos
++]);
2908 } while ( bi18n
&& nErr
== FormulaError::NONE
);
2909 sal_Int32 nLen
= aSymbol
.getLength();
2910 if ( nLen
> MAXSTRLEN
)
2912 SetError( FormulaError::StringOverflow
);
2915 if (mnRangeOpPosInSymbol
>= nLen
)
2916 mnRangeOpPosInSymbol
= -1;
2917 lcl_UnicodeStrNCpy( cSymbol
, aSymbol
.getStr(), nLen
);
2918 pSym
= &cSymbol
[nLen
];
2922 nSrcPos
= pSrc
- pStart
;
2925 if (mnRangeOpPosInSymbol
>= 0 && mnRangeOpPosInSymbol
== (pSym
-1) - &cSymbol
[0])
2927 // This is a trailing range operator, which is nonsense. Will be caught
2929 mnRangeOpPosInSymbol
= -1;
2934 aCorrectedSymbol
= OUString(cSymbol
, pSym
- cSymbol
);
2935 if (bAutoIntersection
&& vSpaces
[nAutoIntersectionSpacesPos
].nCount
> 1)
2936 --vSpaces
[nAutoIntersectionSpacesPos
].nCount
; // replace '!!' with only one space
2940 // Convert symbol to token
2942 bool ScCompiler::ParseOpCode( const OUString
& rName
, bool bInArray
)
2944 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
2945 bool bFound
= (iLook
!= mxSymbols
->getHashMap().end());
2948 OpCode eOp
= iLook
->second
;
2951 if (rName
== mxSymbols
->getSymbol(ocArrayColSep
))
2952 eOp
= ocArrayColSep
;
2953 else if (rName
== mxSymbols
->getSymbol(ocArrayRowSep
))
2954 eOp
= ocArrayRowSep
;
2956 else if (eOp
== ocArrayColSep
|| eOp
== ocArrayRowSep
)
2958 if (rName
== mxSymbols
->getSymbol(ocSep
))
2960 else if (rName
== ";")
2962 switch (FormulaGrammar::extractFormulaLanguage( meGrammar
))
2964 // Only for languages/grammars that actually use ';'
2965 // parameter separator.
2966 case css::sheet::FormulaLanguage::NATIVE
:
2967 case css::sheet::FormulaLanguage::ENGLISH
:
2968 case css::sheet::FormulaLanguage::ODFF
:
2969 case css::sheet::FormulaLanguage::ODF_11
:
2974 else if (eOp
== ocCeil
&& mxSymbols
->isOOXML())
2976 // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2977 // unassigned for import.
2980 else if (eOp
== ocFloor
&& mxSymbols
->isOOXML())
2982 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2983 // unassigned for import.
2986 maRawToken
.SetOpCode(eOp
);
2988 else if (mxSymbols
->isODFF())
2990 // ODFF names that are not written in the current mapping but to be
2991 // recognized. New names will be written in a future release, then
2992 // exchange (!) with the names in
2993 // formula/source/core/resource/core_resource.src to be able to still
2994 // read the old names as well.
3000 static const FunctionName aOdffAliases
[] = {
3001 // Renamed old names, still accept them:
3002 { "B", ocB
}, // B -> BINOM.DIST.RANGE
3003 { "TDIST", ocTDist
}, // TDIST -> LEGACY.TDIST
3004 { "ORG.OPENOFFICE.EASTERSUNDAY", ocEasterSunday
}, // ORG.OPENOFFICE.EASTERSUNDAY -> EASTERSUNDAY
3005 { "ZGZ", ocRRI
}, // ZGZ -> RRI
3006 { "COLOR", ocColor
}, // COLOR -> ORG.LIBREOFFICE.COLOR
3007 { "GOALSEEK", ocBackSolver
}, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
3008 { "COM.MICROSOFT.F.DIST", ocFDist_LT
}, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
3009 { "COM.MICROSOFT.F.INV", ocFInv_LT
} // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
3010 // Renamed new names, prepare to read future names:
3011 //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
3013 for (const FunctionName
& rOdffAlias
: aOdffAliases
)
3015 if (rName
.equalsIgnoreAsciiCaseAscii( rOdffAlias
.pName
))
3017 maRawToken
.SetOpCode( rOdffAlias
.eOp
);
3023 else if (mxSymbols
->isOOXML())
3025 // OOXML names that are not written in the current mapping but to be
3026 // recognized as old versions wrote them.
3032 static const FunctionName aOoxmlAliases
[] = {
3033 { "EFFECTIVE", ocEffect
}, // EFFECTIVE -> EFFECT
3034 { "ERRORTYPE", ocErrorType
}, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
3035 { "MULTIRANGE", ocMultiArea
}, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
3036 { "GOALSEEK", ocBackSolver
}, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
3037 { "EASTERSUNDAY", ocEasterSunday
}, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
3038 { "CURRENT", ocCurrent
}, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
3039 { "STYLE", ocStyle
} // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
3041 for (const FunctionName
& rOoxmlAlias
: aOoxmlAliases
)
3043 if (rName
.equalsIgnoreAsciiCaseAscii( rOoxmlAlias
.pName
))
3045 maRawToken
.SetOpCode( rOoxmlAlias
.eOp
);
3051 else if (mxSymbols
->isPODF())
3053 // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
3054 // We can't rename them in
3055 // formula/source/core/resource/core_resource.src but can add
3056 // additional names to be recognized here so they match the UI names if
3057 // those are renamed.
3063 static const FunctionName aPodfAliases
[] = {
3064 { "EFFECT", ocEffect
} // EFFECTIVE -> EFFECT
3066 for (const FunctionName
& rPodfAlias
: aPodfAliases
)
3068 if (rName
.equalsIgnoreAsciiCaseAscii( rPodfAlias
.pName
))
3070 maRawToken
.SetOpCode( rPodfAlias
.eOp
);
3080 if (mxSymbols
->hasExternals())
3082 // If symbols are set by filters get mapping to exact name.
3083 ExternalHashMap::const_iterator
iExt(
3084 mxSymbols
->getExternalHashMap().find( rName
));
3085 if (iExt
!= mxSymbols
->getExternalHashMap().end())
3087 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt
).second
))
3088 aIntName
= (*iExt
).second
;
3093 // Old (deprecated) addins first for legacy.
3094 if (ScGlobal::GetLegacyFuncCollection()->findByName(OUString(cSymbol
)))
3099 // bLocalFirst=false for (English) upper full original name
3100 // (service.function)
3101 aIntName
= ScGlobal::GetAddInCollection()->FindFunction(
3102 rName
, !mxSymbols
->isEnglish());
3104 if (!aIntName
.isEmpty())
3106 maRawToken
.SetExternal( aIntName
); // international name
3112 OpCode eOp
= maRawToken
.GetOpCode();
3113 if (eOp
== ocSub
|| eOp
== ocNegSub
)
3115 bool bShouldBeNegSub
=
3116 (eLastOp
== ocOpen
|| eLastOp
== ocSep
|| eLastOp
== ocNegSub
||
3117 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_BIN_OP
) ||
3118 eLastOp
== ocArrayOpen
||
3119 eLastOp
== ocArrayColSep
|| eLastOp
== ocArrayRowSep
);
3120 if (bShouldBeNegSub
&& eOp
== ocSub
)
3121 maRawToken
.NewOpCode( ocNegSub
);
3122 //TODO: if ocNegSub had ForceArray we'd have to set it here
3123 else if (!bShouldBeNegSub
&& eOp
== ocNegSub
)
3124 maRawToken
.NewOpCode( ocSub
);
3129 bool ScCompiler::ParseOpCode2( std::u16string_view rName
)
3131 for (sal_uInt16 i
= ocInternalBegin
; i
<= ocInternalEnd
; i
++)
3133 if (o3tl::equalsAscii(rName
, pInternal
[i
- ocInternalBegin
]))
3135 maRawToken
.SetOpCode(static_cast<OpCode
>(i
));
3143 static bool lcl_ParenthesisFollows( const sal_Unicode
* p
)
3150 bool ScCompiler::ParseValue( const OUString
& rSym
)
3152 const sal_Int32 nFormulaLanguage
= FormulaGrammar::extractFormulaLanguage( GetGrammar());
3153 if (nFormulaLanguage
== css::sheet::FormulaLanguage::ODFF
|| nFormulaLanguage
== css::sheet::FormulaLanguage::OOXML
)
3155 // Speedup things for ODFF, only well-formed numbers, not locale
3156 // dependent nor user input.
3157 rtl_math_ConversionStatus eStatus
;
3158 sal_Int32 nParseEnd
;
3159 double fVal
= rtl::math::stringToDouble( rSym
, '.', 0, &eStatus
, &nParseEnd
);
3160 if (nParseEnd
!= rSym
.getLength())
3162 // Not (only) a number.
3165 return false; // partially a number => no such thing
3167 if (lcl_ParenthesisFollows( aFormula
.getStr() + nSrcPos
))
3168 return false; // some function name, not a constant
3170 // Could be TRUE or FALSE constant.
3171 OpCode eOpFunc
= ocNone
;
3172 if (rSym
.equalsIgnoreAsciiCase("TRUE"))
3174 else if (rSym
.equalsIgnoreAsciiCase("FALSE"))
3176 if (eOpFunc
!= ocNone
)
3178 maRawToken
.SetOpCode(eOpFunc
);
3179 // add missing trailing parentheses
3180 maPendingOpCodes
.push(ocOpen
);
3181 maPendingOpCodes
.push(ocClose
);
3186 if (eStatus
== rtl_math_ConversionStatus_OutOfRange
)
3188 // rtl::math::stringToDouble() recognizes XMLSchema-2 "INF" and
3189 // "NaN" (case sensitive) that could be named expressions or DB
3191 // rSym is already upper so "NaN" is not possible here.
3192 if (!std::isfinite(fVal
) && rSym
== "INF")
3195 if (GetRangeData( nSheet
, rSym
))
3197 if (rDoc
.GetDBCollection()->getNamedDBs().findByUpperName(rSym
))
3200 /* TODO: is there a specific reason why we don't accept an infinity
3201 * value that would raise an error in the interpreter, instead of
3202 * setting the hard error at the token array already? */
3203 SetError( FormulaError::IllegalArgument
);
3205 maRawToken
.SetDouble( fVal
);
3210 sal_uInt32 nIndex
= mxSymbols
->isEnglishLocale() ? mrInterpreterContext
.NFGetStandardIndex(LANGUAGE_ENGLISH_US
) : 0;
3212 if (!mrInterpreterContext
.NFIsNumberFormat(rSym
, nIndex
, fVal
))
3215 SvNumFormatType nType
= mrInterpreterContext
.NFGetType(nIndex
);
3217 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3218 // Dates should never be entered directly and automatically converted
3219 // to serial, because the serial would be wrong if null-date changed.
3220 // Usually it wouldn't be accepted anyway because the date separator
3221 // clashed with other separators or operators.
3222 if (nType
& (SvNumFormatType::TIME
| SvNumFormatType::DATE
))
3225 if (nType
== SvNumFormatType::LOGICAL
)
3227 if (lcl_ParenthesisFollows( aFormula
.getStr() + nSrcPos
))
3228 return false; // Boolean function instead.
3231 if( nType
== SvNumFormatType::TEXT
)
3232 // HACK: number too big!
3233 SetError( FormulaError::IllegalArgument
);
3234 maRawToken
.SetDouble( fVal
);
3238 bool ScCompiler::ParseString()
3240 if ( cSymbol
[0] != '"' )
3242 const sal_Unicode
* p
= cSymbol
+1;
3245 sal_Int32 nLen
= sal::static_int_cast
<sal_Int32
>( p
- cSymbol
- 1 );
3246 if (!nLen
|| cSymbol
[nLen
] != '"')
3248 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(OUString(cSymbol
+1, nLen
-1));
3249 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
3253 bool ScCompiler::ParsePredetectedErrRefReference( const OUString
& rName
, const OUString
* pErrRef
)
3255 switch (mnPredetectedReference
)
3258 return ParseSingleReference( rName
, pErrRef
);
3260 return ParseDoubleReference( rName
, pErrRef
);
3266 bool ScCompiler::ParsePredetectedReference( const OUString
& rName
)
3268 // Speedup documents with lots of broken references, e.g. sheet deleted.
3269 // It could also be a broken invalidated reference that contains #REF!
3270 // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3271 // between 2013 and 2016 until 5.1.4
3272 static constexpr OUString
aErrRef(u
"#REF!"_ustr
); // not localized in ODFF
3273 sal_Int32 nPos
= rName
.indexOf( aErrRef
);
3276 /* TODO: this may be enhanced by reusing scan information from
3277 * NextSymbol(), the positions of quotes and special characters found
3278 * there for $'sheet'.A1:... could be stored in a vector. We don't
3279 * fully rescan here whether found positions are within single quotes
3280 * for performance reasons. This code does not check for possible
3281 * occurrences of insane "valid" sheet names like
3282 * 'haha.#REF!1fooledyou' and will generate an error on such. */
3285 // Per ODFF the correct string for a reference error is just #REF!,
3287 if (rName
.getLength() == 5)
3288 return ParseErrorConstant( rName
);
3289 // #REF!.AB42 or #REF!42 or #REF!#REF!
3290 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3292 sal_Unicode c
= rName
[nPos
-1]; // before #REF!
3297 // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3298 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3300 c
= rName
[nPos
-2]; // before $#REF!
3302 sal_Unicode c2
= nPos
+5 < rName
.getLength() ? rName
[nPos
+5] : 0; // after #REF!
3306 if ('$' == c2
|| '#' == c2
|| ('0' <= c2
&& c2
<= '9'))
3308 // sheet.#REF!42 or sheet.#REF!#REF!
3309 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3313 if (mnPredetectedReference
> 1 &&
3314 ('.' == c2
|| '$' == c2
|| '#' == c2
||
3315 ('0' <= c2
&& c2
<= '9')))
3317 // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3318 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3322 if (rtl::isAsciiAlpha(c
) &&
3323 ((mnPredetectedReference
> 1 && ':' == c2
) || 0 == c2
))
3325 // AB#REF!: or AB#REF!
3326 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3330 switch (mnPredetectedReference
)
3333 return ParseSingleReference( rName
);
3335 return ParseDoubleReference( rName
);
3340 bool ScCompiler::ParseDoubleReference( const OUString
& rName
, const OUString
* pErrRef
)
3342 ScRange
aRange( aPos
, aPos
);
3343 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
3344 ScAddress::ExternalInfo aExtInfo
;
3345 ScRefFlags nFlags
= aRange
.Parse( rName
, rDoc
, aDetails
, &aExtInfo
, &maExternalLinks
, pErrRef
);
3346 if( nFlags
& ScRefFlags::VALID
)
3348 ScComplexRefData aRef
;
3349 aRef
.InitRange( aRange
);
3350 aRef
.Ref1
.SetColRel( (nFlags
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
);
3351 aRef
.Ref1
.SetRowRel( (nFlags
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
);
3352 aRef
.Ref1
.SetTabRel( (nFlags
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
);
3353 if ( !(nFlags
& ScRefFlags::TAB_VALID
) )
3354 aRef
.Ref1
.SetTabDeleted( true ); // #REF!
3355 aRef
.Ref1
.SetFlag3D( ( nFlags
& ScRefFlags::TAB_3D
) != ScRefFlags::ZERO
);
3356 aRef
.Ref2
.SetColRel( (nFlags
& ScRefFlags::COL2_ABS
) == ScRefFlags::ZERO
);
3357 aRef
.Ref2
.SetRowRel( (nFlags
& ScRefFlags::ROW2_ABS
) == ScRefFlags::ZERO
);
3358 aRef
.Ref2
.SetTabRel( (nFlags
& ScRefFlags::TAB2_ABS
) == ScRefFlags::ZERO
);
3359 if ( !(nFlags
& ScRefFlags::TAB2_VALID
) )
3360 aRef
.Ref2
.SetTabDeleted( true ); // #REF!
3361 aRef
.Ref2
.SetFlag3D( ( nFlags
& ScRefFlags::TAB2_3D
) != ScRefFlags::ZERO
);
3362 aRef
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
3363 if (aExtInfo
.mbExternal
)
3365 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3366 const OUString
* pRealTab
= pRefMgr
->getRealTableName(aExtInfo
.mnFileId
, aExtInfo
.maTabName
);
3367 maRawToken
.SetExternalDoubleRef(
3368 aExtInfo
.mnFileId
, pRealTab
? *pRealTab
: aExtInfo
.maTabName
, aRef
);
3369 maExternalFiles
.push_back(aExtInfo
.mnFileId
);
3373 maRawToken
.SetDoubleReference(aRef
);
3377 return ( nFlags
& ScRefFlags::VALID
) != ScRefFlags::ZERO
;
3380 bool ScCompiler::ParseSingleReference( const OUString
& rName
, const OUString
* pErrRef
)
3382 mnCurrentSheetEndPos
= 0;
3383 mnCurrentSheetTab
= -1;
3384 ScAddress
aAddr( aPos
);
3385 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
3386 ScAddress::ExternalInfo aExtInfo
;
3387 ScRefFlags nFlags
= aAddr
.Parse( rName
, rDoc
, aDetails
,
3388 &aExtInfo
, &maExternalLinks
, &mnCurrentSheetEndPos
, pErrRef
);
3389 // Something must be valid in order to recognize Sheet1.blah or blah.a1
3390 // as a (wrong) reference.
3391 if( nFlags
& ( ScRefFlags::COL_VALID
|ScRefFlags::ROW_VALID
|ScRefFlags::TAB_VALID
) )
3393 // Valid given tab and invalid col or row may indicate a sheet-local
3394 // named expression, bail out early and don't create a reference token.
3395 if (!(nFlags
& ScRefFlags::VALID
) && mnCurrentSheetEndPos
> 0 &&
3396 (nFlags
& ScRefFlags::TAB_VALID
) && (nFlags
& ScRefFlags::TAB_3D
))
3398 if (aExtInfo
.mbExternal
)
3400 // External names are handled separately.
3401 mnCurrentSheetEndPos
= 0;
3402 mnCurrentSheetTab
= -1;
3406 mnCurrentSheetTab
= aAddr
.Tab();
3411 if( HasPossibleNamedRangeConflict( aAddr
.Tab()))
3413 // A named range named e.g. 'num1' is valid with 1k columns, but would become a reference
3414 // when the document is opened later with 16k columns. Resolve the conflict by not
3415 // considering it a reference.
3416 OUString
aUpper( ScGlobal::getCharClass().uppercase( rName
));
3417 mnCurrentSheetTab
= aAddr
.Tab(); // temporarily set for ParseNamedRange()
3418 if(ParseNamedRange( aUpper
, true )) // only check
3420 mnCurrentSheetTab
= -1;
3423 ScSingleRefData aRef
;
3424 aRef
.InitAddress( aAddr
);
3425 aRef
.SetColRel( (nFlags
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
);
3426 aRef
.SetRowRel( (nFlags
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
);
3427 aRef
.SetTabRel( (nFlags
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
);
3428 aRef
.SetFlag3D( ( nFlags
& ScRefFlags::TAB_3D
) != ScRefFlags::ZERO
);
3429 // the reference is really invalid
3430 if( !( nFlags
& ScRefFlags::VALID
) )
3432 if( !( nFlags
& ScRefFlags::COL_VALID
) )
3433 aRef
.SetColDeleted(true);
3434 if( !( nFlags
& ScRefFlags::ROW_VALID
) )
3435 aRef
.SetRowDeleted(true);
3436 if( !( nFlags
& ScRefFlags::TAB_VALID
) )
3437 aRef
.SetTabDeleted(true);
3438 nFlags
|= ScRefFlags::VALID
;
3440 aRef
.SetAddress(rDoc
.GetSheetLimits(), aAddr
, aPos
);
3442 if (aExtInfo
.mbExternal
)
3444 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3445 const OUString
* pRealTab
= pRefMgr
->getRealTableName(aExtInfo
.mnFileId
, aExtInfo
.maTabName
);
3446 maRawToken
.SetExternalSingleRef(
3447 aExtInfo
.mnFileId
, pRealTab
? *pRealTab
: aExtInfo
.maTabName
, aRef
);
3448 maExternalFiles
.push_back(aExtInfo
.mnFileId
);
3451 maRawToken
.SetSingleReference(aRef
);
3454 return ( nFlags
& ScRefFlags::VALID
) != ScRefFlags::ZERO
;
3457 bool ScCompiler::ParseReference( const OUString
& rName
, const OUString
* pErrRef
)
3459 // Has to be called before ParseValue
3461 // A later ParseNamedRange() relies on these, being set in ParseSingleReference()
3462 // if so, reset in all cases.
3463 mnCurrentSheetEndPos
= 0;
3464 mnCurrentSheetTab
= -1;
3466 sal_Unicode ch1
= rName
[0];
3467 sal_Unicode cDecSep
= ( mxSymbols
->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0] );
3468 if ( ch1
== cDecSep
)
3470 // Code further down checks only if cDecSep=='.' so simply obtaining the
3471 // alternative decimal separator if it's not is sufficient.
3474 cDecSep
= ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar();
3475 if ( ch1
== cDecSep
)
3478 // Who was that imbecile introducing '.' as the sheet name separator!?!
3479 if ( rtl::isAsciiDigit( ch1
) && pConv
->getSpecialSymbol( Convention::SHEET_SEPARATOR
) == '.' )
3481 // Numerical sheet name is valid.
3482 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3483 // Don't create a #REF! of values. But also do not bail out on
3484 // something like 3:3, meaning entire row 3.
3487 const sal_Int32 nPos
= ScGlobal::FindUnquoted( rName
, '.');
3490 if (ScGlobal::FindUnquoted( rName
, ':') != -1)
3491 break; // may be 3:3, continue as usual
3494 sal_Unicode
const * const pTabSep
= rName
.getStr() + nPos
;
3495 sal_Unicode ch2
= pTabSep
[1]; // maybe a column identifier
3496 if ( !(ch2
== '$' || rtl::isAsciiAlpha( ch2
)) )
3498 if ( cDecSep
== '.' && (ch2
== 'E' || ch2
== 'e') // E + - digit
3499 && (GetCharTableFlags( pTabSep
[2], pTabSep
[1] ) & ScCharFlags::ValueExp
) )
3501 // If it is an 1.E2 expression check if "1" is an existent sheet
3502 // name. If so, a desired value 1.E2 would have to be entered as
3503 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3504 // require numerical sheet names always being entered quoted, which
3505 // is not desirable (too many 1999, 2000, 2001 sheets in use).
3506 // Furthermore, XML files created with versions prior to SRC640e
3507 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3508 // and would produce wrong formulas if the conditions here are met.
3509 // If you can live with these restrictions you may remove the
3510 // check and return an unconditional FALSE.
3511 OUString
aTabName( rName
.copy( 0, nPos
) );
3513 if ( !rDoc
.GetTable( aTabName
, nTab
) )
3515 // If sheet "1" exists and the expression is 1.E+2 continue as
3516 // usual, the ScRange/ScAddress parser will take care of it.
3521 if (ParseSingleReference( rName
, pErrRef
))
3524 // Though the range operator is handled explicitly, when encountering
3525 // something like Sheet1.A:A we will have to treat it as one entity if it
3526 // doesn't pass as single cell reference.
3527 if (mnRangeOpPosInSymbol
> 0) // ":foo" would be nonsense
3529 if (ParseDoubleReference( rName
, pErrRef
))
3531 // Now try with a symbol up to the range operator, rewind source
3533 assert(mnRangeOpPosInSymbol
< MAXSTRLEN
); // We should have caught the maldoers.
3534 if (mnRangeOpPosInSymbol
>= MAXSTRLEN
) // TODO: this check and return
3535 return false; // can be removed when sure.
3536 sal_Int32 nLen
= mnRangeOpPosInSymbol
;
3537 while (cSymbol
[++nLen
])
3539 cSymbol
[mnRangeOpPosInSymbol
] = 0;
3540 nSrcPos
-= (nLen
- mnRangeOpPosInSymbol
);
3541 mnRangeOpPosInSymbol
= -1;
3543 return true; // end all checks
3547 switch (pConv
->meConv
)
3549 case FormulaGrammar::CONV_XL_A1
:
3550 case FormulaGrammar::CONV_XL_OOX
:
3551 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3552 // sickness, mnRangeOpPosInSymbol did not catch the range
3553 // operator as it is within a quoted name.
3554 if (rName
[0] != '\'')
3555 return false; // Document name has to be single quoted.
3557 case FormulaGrammar::CONV_XL_R1C1
:
3558 // C2 or C[1] are valid entire column references.
3559 if (ParseDoubleReference( rName
, pErrRef
))
3569 bool ScCompiler::ParseMacro( const OUString
& rName
)
3571 #if !HAVE_FEATURE_SCRIPTING
3577 // Calling SfxObjectShell::GetBasic() may result in all sort of things
3578 // including obtaining the model and deep down in
3579 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3580 // formulas are compiled from a threaded import may result in a deadlock.
3581 // Check first if we actually could acquire it and if not bail out.
3582 /* FIXME: yes, but how ... */
3583 vcl::SolarMutexTryAndBuyGuard g
;
3584 if (!g
.isAcquired())
3586 SAL_WARN( "sc.core", "ScCompiler::ParseMacro - SolarMutex would deadlock, not obtaining Basic");
3587 return false; // bad luck
3590 OUString
aName( rName
);
3591 StarBASIC
* pObj
= nullptr;
3592 ScDocShell
* pDocSh
= rDoc
.GetDocumentShell();
3597 pObj
= pDocSh
->GetBasic();
3599 pObj
= SfxApplication::GetBasic();
3609 // ODFF recommends to store user-defined functions prefixed with "USER.",
3610 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3611 // function name so a function "USER.FOO" could not exist, and macro check
3612 // is assigned the lowest priority in function name check.
3613 if (FormulaGrammar::isODFF( GetGrammar()) && aName
.startsWithIgnoreAsciiCase("USER."))
3614 aName
= aName
.copy(5);
3616 SbxMethod
* pMeth
= static_cast<SbxMethod
*>(pObj
->Find( aName
, SbxClassType::Method
));
3621 // It really should be a BASIC function!
3622 if( pMeth
->GetType() == SbxVOID
3623 || ( pMeth
->IsFixed() && pMeth
->GetType() == SbxEMPTY
)
3624 || dynamic_cast<const SbMethod
*>( pMeth
) == nullptr )
3628 maRawToken
.SetExternal( aName
);
3629 maRawToken
.eOp
= ocMacro
;
3634 const ScRangeData
* ScCompiler::GetRangeData( SCTAB
& rSheet
, const OUString
& rUpperName
) const
3636 // try local names first
3637 rSheet
= aPos
.Tab();
3638 const ScRangeName
* pRangeName
= rDoc
.GetRangeName(rSheet
);
3639 const ScRangeData
* pData
= nullptr;
3641 pData
= pRangeName
->findByUpperName(rUpperName
);
3644 pRangeName
= rDoc
.GetRangeName();
3646 pData
= pRangeName
->findByUpperName(rUpperName
);
3653 bool ScCompiler::HasPossibleNamedRangeConflict( SCTAB nTab
) const
3655 const ScRangeName
* pRangeName
= rDoc
.GetRangeName();
3656 if (pRangeName
&& pRangeName
->hasPossibleAddressConflict())
3658 pRangeName
= rDoc
.GetRangeName(nTab
);
3659 if (pRangeName
&& pRangeName
->hasPossibleAddressConflict())
3664 bool ScCompiler::ParseNamedRange( const OUString
& rUpperName
, bool onlyCheck
)
3666 // ParseNamedRange is called only from NextNewToken, with an upper-case string
3669 const ScRangeData
* pData
= GetRangeData( nSheet
, rUpperName
);
3673 maRawToken
.SetName( nSheet
, pData
->GetIndex());
3677 // Sheet-local name with sheet specified.
3678 if (mnCurrentSheetEndPos
> 0 && mnCurrentSheetTab
>= 0)
3680 OUString
aName( rUpperName
.copy( mnCurrentSheetEndPos
));
3681 const ScRangeName
* pRangeName
= rDoc
.GetRangeName( mnCurrentSheetTab
);
3684 pData
= pRangeName
->findByUpperName(aName
);
3688 maRawToken
.SetName( mnCurrentSheetTab
, pData
->GetIndex());
3697 bool ScCompiler::ParseLambdaFuncName( const OUString
& aOrg
)
3699 if (m_aLambda
.bInLambdaFunction
&& !aOrg
.isEmpty())
3701 OUString aName
= aOrg
;
3702 if (aOrg
.startsWithIgnoreAsciiCase(u
"_xlpm."))
3703 aName
= aName
.copy(6);
3705 if (m_aLambda
.nParaPos
% 2 == 1 && m_aLambda
.nParaCount
> m_aLambda
.nParaPos
)
3706 m_aLambda
.aNameSet
.insert(aName
);
3709 // should already exist the name
3710 if (m_aLambda
.aNameSet
.find(aName
) == m_aLambda
.aNameSet
.end())
3713 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aName
);
3714 maRawToken
.SetStringName(aSS
.getData(), aSS
.getDataIgnoreCase());
3720 bool ScCompiler::ParseExternalNamedRange( const OUString
& rSymbol
, bool& rbInvalidExternalNameRange
)
3722 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3723 * correctly parses external named references in OOo, as required per RFE
3724 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3725 * spec first. Until then don't pretend to support external names that
3726 * wouldn't survive a save and reload cycle, return false instead. */
3728 rbInvalidExternalNameRange
= false;
3733 OUString aFile
, aName
;
3734 if (!pConv
->parseExternalName( rSymbol
, aFile
, aName
, rDoc
, &maExternalLinks
))
3737 if (aFile
.getLength() > MAXSTRLEN
|| aName
.getLength() > MAXSTRLEN
)
3740 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3741 OUString aTmp
= aFile
;
3742 pRefMgr
->convertToAbsName(aTmp
);
3744 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aFile
);
3745 if (!pRefMgr
->isValidRangeName(nFileId
, aName
))
3747 rbInvalidExternalNameRange
= true;
3748 // range name doesn't exist in the source document.
3752 const OUString
* pRealName
= pRefMgr
->getRealRangeName(nFileId
, aName
);
3753 maRawToken
.SetExternalName(nFileId
, pRealName
? *pRealName
: aTmp
);
3754 maExternalFiles
.push_back(nFileId
);
3758 bool ScCompiler::ParseDBRange( const OUString
& rName
)
3760 ScDBCollection::NamedDBs
& rDBs
= rDoc
.GetDBCollection()->getNamedDBs();
3761 const ScDBData
* p
= rDBs
.findByUpperName(rName
);
3765 maRawToken
.SetName( -1, p
->GetIndex()); // DB range is always global.
3766 maRawToken
.eOp
= ocDBArea
;
3770 bool ScCompiler::ParseColRowName( const OUString
& rName
)
3772 bool bInList
= false;
3773 bool bFound
= false;
3774 ScSingleRefData aRef
;
3775 OUString
aName( rName
);
3777 SCTAB nThisTab
= aPos
.Tab();
3778 for ( short jThisTab
= 1; jThisTab
>= 0 && !bInList
; jThisTab
-- )
3779 { // first check ranges on this sheet, in case of duplicated names
3780 for ( short jRow
=0; jRow
<2 && !bInList
; jRow
++ )
3782 ScRangePairList
* pRL
;
3784 pRL
= rDoc
.GetColNameRanges();
3786 pRL
= rDoc
.GetRowNameRanges();
3787 for ( size_t iPair
= 0, nPairs
= pRL
->size(); iPair
< nPairs
&& !bInList
; ++iPair
)
3789 const ScRangePair
& rR
= (*pRL
)[iPair
];
3790 const ScRange
& rNameRange
= rR
.GetRange(0);
3791 if ( jThisTab
&& (rNameRange
.aStart
.Tab() > nThisTab
||
3792 nThisTab
> rNameRange
.aEnd
.Tab()) )
3794 ScCellIterator
aIter( rDoc
, rNameRange
);
3795 for (bool bHas
= aIter
.first(); bHas
&& !bInList
; bHas
= aIter
.next())
3797 // Don't crash if cell (via CompileNameFormula) encounters
3798 // a formula cell without code and
3799 // HasStringData/Interpret/Compile is executed and all that
3801 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3802 CellType eType
= aIter
.getType();
3804 if (eType
== CELLTYPE_FORMULA
)
3806 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3807 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
3812 if (bOk
&& aIter
.hasString())
3814 OUString aStr
= aIter
.getString();
3815 if ( ScGlobal::GetTransliteration().isEqual( aStr
, aName
) )
3819 aRef
.SetColRel( true ); // ColName
3821 aRef
.SetRowRel( true ); // RowName
3822 aRef
.SetAddress(rDoc
.GetSheetLimits(), aIter
.GetPos(), aPos
);
3823 bInList
= bFound
= true;
3830 if ( !bInList
&& rDoc
.GetDocOptions().IsLookUpColRowNames() )
3831 { // search in current sheet
3832 tools::Long nDistance
= 0, nMax
= 0;
3833 tools::Long nMyCol
= static_cast<tools::Long
>(aPos
.Col());
3834 tools::Long nMyRow
= static_cast<tools::Long
>(aPos
.Row());
3836 ScAddress
aOne( 0, 0, aPos
.Tab() );
3837 ScAddress
aTwo( rDoc
.MaxCol(), rDoc
.MaxRow(), aPos
.Tab() );
3839 ScAutoNameCache
* pNameCache
= rDoc
.GetAutoNameCache();
3842 // use GetNameOccurrences to collect all positions of aName on the sheet
3843 // (only once), similar to the outer part of the loop in the "else" branch.
3845 const ScAutoNameAddresses
& rAddresses
= pNameCache
->GetNameOccurrences( aName
, aPos
.Tab() );
3847 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3848 // The order of addresses in the vector is the same as from ScCellIterator.
3850 for ( const ScAddress
& aAddress
: rAddresses
)
3853 { // stop if everything else is further away
3854 if ( nMax
< static_cast<tools::Long
>(aAddress
.Col()) )
3857 if ( aAddress
!= aPos
)
3859 // same treatment as in isEqual case below
3861 SCCOL nCol
= aAddress
.Col();
3862 SCROW nRow
= aAddress
.Row();
3863 tools::Long nC
= nMyCol
- nCol
;
3864 tools::Long nR
= nMyRow
- nRow
;
3867 tools::Long nD
= nC
* nC
+ nR
* nR
;
3868 if ( nD
< nDistance
)
3870 if ( nC
< 0 || nR
< 0 )
3873 aTwo
.Set( nCol
, nRow
, aAddress
.Tab() );
3874 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3877 else if ( nRow
>= aOne
.Row() || nMyRow
< static_cast<tools::Long
>(aOne
.Row()) )
3879 // upper left, only if not further up than the
3880 // current entry and nMyRow is below (CellIter
3881 // runs column-wise)
3883 aOne
.Set( nCol
, nRow
, aAddress
.Tab() );
3884 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
3891 aOne
.Set( nCol
, nRow
, aAddress
.Tab() );
3892 nDistance
= nC
* nC
+ nR
* nR
;
3893 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3902 ScCellIterator
aIter( rDoc
, ScRange( aOne
, aTwo
) );
3903 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
3906 { // stop if everything else is further away
3907 if ( nMax
< static_cast<tools::Long
>(aIter
.GetPos().Col()) )
3910 CellType eType
= aIter
.getType();
3912 if (eType
== CELLTYPE_FORMULA
)
3914 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3915 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
3920 if (bOk
&& aIter
.hasString())
3922 OUString aStr
= aIter
.getString();
3923 if ( ScGlobal::GetTransliteration().isEqual( aStr
, aName
) )
3925 SCCOL nCol
= aIter
.GetPos().Col();
3926 SCROW nRow
= aIter
.GetPos().Row();
3927 tools::Long nC
= nMyCol
- nCol
;
3928 tools::Long nR
= nMyRow
- nRow
;
3931 tools::Long nD
= nC
* nC
+ nR
* nR
;
3932 if ( nD
< nDistance
)
3934 if ( nC
< 0 || nR
< 0 )
3937 aTwo
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3938 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3941 else if ( nRow
>= aOne
.Row() || nMyRow
< static_cast<tools::Long
>(aOne
.Row()) )
3943 // upper left, only if not further up than the
3944 // current entry and nMyRow is below (CellIter
3945 // runs column-wise)
3947 aOne
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3948 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
3955 aOne
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3956 nDistance
= nC
* nC
+ nR
* nR
;
3957 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3970 if ( nMyCol
>= static_cast<tools::Long
>(aOne
.Col()) && nMyRow
>= static_cast<tools::Long
>(aOne
.Row()) )
3971 aAdr
= aOne
; // upper left takes precedence
3974 if ( nMyCol
< static_cast<tools::Long
>(aOne
.Col()) )
3975 { // two to the right
3976 if ( nMyRow
>= static_cast<tools::Long
>(aTwo
.Row()) )
3977 aAdr
= aTwo
; // directly right
3982 { // two below or below and right, take the nearest
3983 tools::Long nC1
= nMyCol
- aOne
.Col();
3984 tools::Long nR1
= nMyRow
- aOne
.Row();
3985 tools::Long nC2
= nMyCol
- aTwo
.Col();
3986 tools::Long nR2
= nMyRow
- aTwo
.Row();
3987 if ( nC1
* nC1
+ nR1
* nR1
<= nC2
* nC2
+ nR2
* nR2
)
3996 aRef
.InitAddress( aAdr
);
3997 // Prioritize on column label; row label only if the next cell
3998 // above/below the found label cell is text, or if both are not and
3999 // the cell below is empty and the next cell to the right is
4001 if ((aAdr
.Row() < rDoc
.MaxRow() && rDoc
.HasStringData(
4002 aAdr
.Col(), aAdr
.Row() + 1, aAdr
.Tab()))
4003 || (aAdr
.Row() > 0 && rDoc
.HasStringData(
4004 aAdr
.Col(), aAdr
.Row() - 1, aAdr
.Tab()))
4005 || (aAdr
.Row() < rDoc
.MaxRow() && rDoc
.GetRefCellValue(
4006 ScAddress( aAdr
.Col(), aAdr
.Row() + 1, aAdr
.Tab())).isEmpty()
4007 && aAdr
.Col() < rDoc
.MaxCol() && rDoc
.GetRefCellValue(
4008 ScAddress( aAdr
.Col() + 1, aAdr
.Row(), aAdr
.Tab())).hasNumeric()))
4009 aRef
.SetRowRel( true ); // RowName
4011 aRef
.SetColRel( true ); // ColName
4012 aRef
.SetAddress(rDoc
.GetSheetLimits(), aAdr
, aPos
);
4017 maRawToken
.SetSingleReference( aRef
);
4018 maRawToken
.eOp
= ocColRowName
;
4025 bool ScCompiler::ParseBoolean( const OUString
& rName
)
4027 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
) );
4028 if( iLook
!= mxSymbols
->getHashMap().end() &&
4029 ((*iLook
).second
== ocTrue
||
4030 (*iLook
).second
== ocFalse
) )
4032 maRawToken
.SetOpCode( (*iLook
).second
);
4039 bool ScCompiler::ParseErrorConstant( const OUString
& rName
)
4041 FormulaError nError
= GetErrorConstant( rName
);
4042 if (nError
!= FormulaError::NONE
)
4044 maRawToken
.SetErrorConstant( nError
);
4051 bool ScCompiler::ParseTableRefItem( const OUString
& rName
)
4054 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
4055 if (iLook
!= mxSymbols
->getHashMap().end())
4057 // Only called when there actually is a current TableRef, hence
4058 // accessing maTableRefs.back() is safe.
4059 ScTableRefToken
* p
= maTableRefs
.back().mxToken
.get();
4060 assert(p
); // not a ScTableRefToken can't be
4062 switch ((*iLook
).second
)
4064 case ocTableRefItemAll
:
4066 p
->AddItem( ScTableRefToken::ALL
);
4068 case ocTableRefItemHeaders
:
4070 p
->AddItem( ScTableRefToken::HEADERS
);
4072 case ocTableRefItemData
:
4074 p
->AddItem( ScTableRefToken::DATA
);
4076 case ocTableRefItemTotals
:
4078 p
->AddItem( ScTableRefToken::TOTALS
);
4080 case ocTableRefItemThisRow
:
4082 p
->AddItem( ScTableRefToken::THIS_ROW
);
4088 maRawToken
.SetOpCode( (*iLook
).second
);
4094 OUString
unescapeTableRefColumnSpecifier( const OUString
& rStr
)
4096 // '#', '[', ']' and '\'' are escaped with '\''
4098 if (rStr
.indexOf( '\'' ) < 0)
4101 const sal_Int32 n
= rStr
.getLength();
4102 OUStringBuffer
aBuf( n
);
4103 const sal_Unicode
* p
= rStr
.getStr();
4104 const sal_Unicode
* const pStop
= p
+ n
;
4105 bool bEscaped
= false;
4106 for ( ; p
< pStop
; ++p
)
4108 const sal_Unicode c
= *p
;
4115 bEscaped
= true; // unescaped escaping '\''
4119 return aBuf
.makeStringAndClear();
4123 bool ScCompiler::ParseTableRefColumn( const OUString
& rName
)
4125 // Only called when there actually is a current TableRef, hence
4126 // accessing maTableRefs.back() is safe.
4127 ScTableRefToken
* p
= maTableRefs
.back().mxToken
.get();
4128 assert(p
); // not a ScTableRefToken can't be
4130 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex( p
->GetIndex());
4134 OUString
aName( unescapeTableRefColumnSpecifier( rName
));
4137 pDBData
->GetArea( aRange
);
4138 aRange
.aEnd
.SetTab( aRange
.aStart
.Tab());
4139 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
4141 // Prefer the stored internal table column name, which is also needed for
4142 // named expressions during document load time when cell content isn't
4143 // available yet. Also, avoiding a possible calculation step in case the
4144 // header cell is a formula cell is "a good thing".
4145 sal_Int32 nOffset
= pDBData
->GetColumnNameOffset( aName
);
4148 // This is sneaky... we always use the top row of the database range,
4149 // regardless of whether it is a header row or not. Code evaluating
4150 // this reference must take that into account and may have to act
4151 // differently if it is a header-less table. Which are two places,
4152 // HandleTableRef() (no change necessary there) and
4153 // CreateStringFromSingleRef() (must not fallback to cell lookup).
4154 ScSingleRefData aRef
;
4155 ScAddress
aAdr( aRange
.aStart
);
4156 aAdr
.IncCol( nOffset
);
4157 aRef
.InitAddress( aAdr
);
4158 maRawToken
.SetSingleReference( aRef
);
4162 if (pDBData
->HasHeader())
4164 // Quite similar to IsColRowName() but limited to one row of headers.
4165 ScCellIterator
aIter( rDoc
, aRange
);
4166 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
4168 CellType eType
= aIter
.getType();
4170 if (eType
== CELLTYPE_FORMULA
)
4172 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
4173 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
4178 if (bOk
&& aIter
.hasString())
4180 OUString aStr
= aIter
.getString();
4181 if (ScGlobal::GetTransliteration().isEqual( aStr
, aName
))
4183 // If this is successful and the internal column name
4184 // lookup was not, it may be worth a warning.
4185 SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
4187 /* XXX NOTE: we could init the column as relative so copying a
4188 * formula across columns would point to the relative column,
4189 * but do it absolute because:
4190 * a) it makes the reference work in named expressions without
4191 * having to distinguish
4192 * b) Excel does it the same. */
4193 ScSingleRefData aRef
;
4194 aRef
.InitAddress( aIter
.GetPos());
4195 maRawToken
.SetSingleReference( aRef
);
4205 void ScCompiler::SetAutoCorrection( bool bVal
)
4207 assert(mbJumpCommandReorder
);
4208 bAutoCorrect
= bVal
;
4209 mbStopOnError
= !bVal
;
4212 void ScCompiler::AutoCorrectParsedSymbol()
4214 sal_Int32 nPos
= aCorrectedSymbol
.getLength();
4219 const sal_Unicode cQuote
= '\"';
4220 const sal_Unicode cx
= 'x';
4221 const sal_Unicode cX
= 'X';
4222 sal_Unicode c1
= aCorrectedSymbol
[0];
4223 sal_Unicode c2
= aCorrectedSymbol
[nPos
];
4224 sal_Unicode c2p
= nPos
> 0 ? aCorrectedSymbol
[nPos
-1] : 0;
4225 if ( c1
== cQuote
&& c2
!= cQuote
)
4227 // What's not a word doesn't belong to it.
4228 // Don't be pedantic: c < 128 should be sufficient here.
4229 while ( nPos
&& ((aCorrectedSymbol
[nPos
] < 128) &&
4230 ((GetCharTableFlags(aCorrectedSymbol
[nPos
], aCorrectedSymbol
[nPos
-1]) &
4231 (ScCharFlags::Word
| ScCharFlags::CharDontCare
)) == ScCharFlags::NONE
)) )
4233 if ( nPos
== MAXSTRLEN
- 1 )
4234 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
, 1, rtl::OUStringChar(cQuote
) ); // '"' the MAXSTRLENth character
4236 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
+ 1, 0, rtl::OUStringChar(cQuote
) );
4239 else if ( c1
!= cQuote
&& c2
== cQuote
)
4241 aCorrectedSymbol
= OUStringChar(cQuote
) + aCorrectedSymbol
;
4244 else if ( nPos
== 0 && (c1
== cx
|| c1
== cX
) )
4246 aCorrectedSymbol
= mxSymbols
->getSymbol(ocMul
);
4249 else if ( (GetCharTableFlags( c1
, 0 ) & ScCharFlags::CharValue
)
4250 && (GetCharTableFlags( c2
, c2p
) & ScCharFlags::CharValue
) )
4252 if ( aCorrectedSymbol
.indexOf(cx
) >= 0 ) // At least two tokens separated by cx
4254 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
4255 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUStringChar(cx
), OUStringChar(c
));
4258 if ( aCorrectedSymbol
.indexOf(cX
) >= 0 ) // At least two tokens separated by cX
4260 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
4261 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUStringChar(cX
), OUStringChar(c
));
4267 OUString
aSymbol( aCorrectedSymbol
);
4269 if ( aSymbol
[0] == '\'' )
4271 sal_Int32 nPosition
= aSymbol
.indexOf( "'#" );
4272 if (nPosition
!= -1)
4273 { // Split off 'Doc'#, may be d:\... or whatever
4274 aDoc
= aSymbol
.copy(0, nPosition
+ 2);
4275 aSymbol
= aSymbol
.copy(nPosition
+ 2);
4278 sal_Int32 nRefs
= comphelper::string::getTokenCount(aSymbol
, ':');
4281 { // duplicated or too many ':'? B:2::C10 => B2:C10
4283 sal_Int32 nIndex
= 0;
4284 OUString
aTmp1( aSymbol
.getToken( 0, ':', nIndex
) );
4285 sal_Int32 nLen1
= aTmp1
.getLength();
4286 OUStringBuffer aSym
;
4288 bool bLastAlp
= true;
4289 sal_Int32 nStrip
= 0;
4290 sal_Int32 nCount
= nRefs
;
4291 for ( sal_Int32 j
=1; j
<nCount
; j
++ )
4293 aTmp2
= aSymbol
.getToken( 0, ':', nIndex
);
4294 sal_Int32 nLen2
= aTmp2
.getLength();
4295 if ( nLen1
|| nLen2
)
4300 bLastAlp
= CharClass::isAsciiAlpha( aTmp1
);
4304 bool bNextNum
= CharClass::isAsciiNumeric( aTmp2
);
4305 if ( bLastAlp
== bNextNum
&& nStrip
< 1 )
4307 // Must be alternating number/string, only
4308 // strip within a reference.
4314 if ( !aSym
.isEmpty() && aSym
[aSym
.getLength()-1] != ':')
4318 bLastAlp
= !bNextNum
;
4324 { // B10::C10 ? append ':' on next round
4325 if ( !bLastAlp
&& !CharClass::isAsciiNumeric( aTmp1
) )
4335 aSymbol
= aSym
+ aTmp1
;
4340 if ( nRefs
&& nRefs
<= 2 )
4341 { // reference twisted? 4A => A4 etc.
4342 OUString aTab
[2], aRef
[2];
4343 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
4346 sal_Int32 nIdx
{ 0 };
4347 aRef
[0] = aSymbol
.getToken( 0, ':', nIdx
);
4348 aRef
[1] = aSymbol
.getToken( 0, ':', nIdx
);
4353 bool bChanged
= false;
4355 ScRefFlags nMask
= ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
;
4356 for ( int j
=0; j
<nRefs
; j
++ )
4359 sal_Int32 nDotPos
= -1;
4360 while ( (nTmp
= aRef
[j
].indexOf( '.', nTmp
)) != -1 )
4361 nDotPos
= nTmp
++; // the last one counts
4362 if ( nDotPos
!= -1 )
4364 aTab
[j
] = aRef
[j
].copy( 0, nDotPos
+ 1 ); // with '.'
4365 aRef
[j
] = aRef
[j
].copy( nDotPos
+ 1 );
4367 OUString
aOld( aRef
[j
] );
4368 OUStringBuffer aStr2
;
4369 const sal_Unicode
* p
= aRef
[j
].getStr();
4370 while ( *p
&& rtl::isAsciiDigit( *p
) )
4372 aRef
[j
] = OUString( p
);
4374 if ( bColons
|| aRef
[j
] != aOld
)
4378 bOk
&= ((aAdr
.Parse( aRef
[j
], rDoc
, aDetails
) & nMask
) == nMask
);
4381 if ( bChanged
&& bOk
)
4383 aCorrectedSymbol
= aDoc
;
4384 aCorrectedSymbol
+= aTab
[0];
4385 aCorrectedSymbol
+= aRef
[0];
4388 aCorrectedSymbol
+= ":";
4389 aCorrectedSymbol
+= aTab
[1];
4390 aCorrectedSymbol
+= aRef
[1];
4398 bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString
& rUpper
, const OUString
& rOrg
) const
4400 if (FormulaGrammar::isODFF( meGrammar
) || FormulaGrammar::isOOXML( meGrammar
))
4402 // ODFF and OOXML have defined sets of English function names, avoid
4404 rUpper
= rOrg
.toAsciiUpperCase();
4409 // One of localized or English.
4410 rUpper
= pCharClass
->uppercase(rOrg
);
4415 short ScCompiler::GetPossibleParaCount( std::u16string_view rLambdaFormula
) const
4417 sal_Unicode cSep
= mxSymbols
->getSymbolChar(ocSep
);
4418 sal_Unicode cOpen
= mxSymbols
->getSymbolChar(ocOpen
);
4419 sal_Unicode cClose
= mxSymbols
->getSymbolChar(ocClose
);
4420 sal_Unicode cArrayOpen
= mxSymbols
->getSymbolChar(ocArrayOpen
);
4421 sal_Unicode cArrayClose
= mxSymbols
->getSymbolChar(ocArrayClose
);
4422 short nBrackets
= 0;
4424 short nCount
= std::count_if(rLambdaFormula
.begin(), rLambdaFormula
.end(),
4425 [&](sal_Unicode c
) {
4426 if (c
== cOpen
|| c
== cArrayOpen
|| c
== '[') {
4430 else if (c
== cClose
|| c
== cArrayClose
|| c
== ']') {
4442 return static_cast<short>(nCount
+ 1);
4445 bool ScCompiler::NextNewToken( bool bInArray
)
4447 if (!maPendingOpCodes
.empty())
4449 maRawToken
.SetOpCode(maPendingOpCodes
.front());
4450 maPendingOpCodes
.pop();
4454 bool bAllowBooleans
= bInArray
;
4455 const std::vector
<Whitespace
> vSpaces
= NextSymbol(bInArray
);
4459 if (nSrcPos
< aFormula
.getLength())
4461 // Nothing could be parsed, remainder as bad string.
4462 // NextSymbol() must had set an error for this.
4463 assert( pArr
->GetCodeError() != FormulaError::NONE
);
4464 const OUString
aBad( aFormula
.copy( nSrcPos
));
4465 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern( aBad
);
4466 maRawToken
.SetString( aSS
.getData(), aSS
.getDataIgnoreCase());
4467 maRawToken
.NewOpCode( ocBad
);
4468 nSrcPos
= aFormula
.getLength();
4469 // Add bad string as last token.
4475 if (!vSpaces
.empty())
4478 for (const auto& rSpace
: vSpaces
)
4480 if (rSpace
.cChar
== 0x20)
4482 // For now keep this a FormulaByteToken for the nasty
4483 // significant whitespace intersection. This probably can be
4484 // changed to a FormulaSpaceToken but then other places may
4485 // need to be adapted.
4486 aToken
.SetOpCode( ocSpaces
);
4487 aToken
.sbyte
.cByte
= static_cast<sal_uInt8
>( std::min
<sal_Int32
>(rSpace
.nCount
, 255) );
4491 aToken
.SetOpCode( ocWhitespace
);
4492 aToken
.whitespace
.nCount
= static_cast<sal_uInt8
>( std::min
<sal_Int32
>(rSpace
.nCount
, 255) );
4493 aToken
.whitespace
.cChar
= rSpace
.cChar
;
4495 if (!static_cast<ScTokenArray
*>(pArr
)->AddRawToken( aToken
))
4497 SetError(FormulaError::CodeOverflow
);
4503 // Short cut for references when reading ODF to speedup things.
4504 if (mnPredetectedReference
)
4506 OUString
aStr( cSymbol
);
4507 bool bInvalidExternalNameRange
;
4508 if (!ParsePredetectedReference( aStr
) && !ParseExternalNamedRange( aStr
, bInvalidExternalNameRange
))
4510 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aStr
);
4511 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4512 maRawToken
.NewOpCode( ocBad
);
4517 if ( (cSymbol
[0] == '#' || cSymbol
[0] == '$') && cSymbol
[1] == 0 &&
4519 { // special case to speed up broken [$]#REF documents
4520 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4521 * be processed as usual. That would need some special treatment,
4522 * also in NextSymbol() because of possible combinations of
4523 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4524 * handled by IsPredetectedReference(), this case here remains for
4525 * manual/API input. */
4526 OUString
aBad( aFormula
.copy( nSrcPos
-1 ) );
4527 const FormulaToken
* pBadToken
= pArr
->AddBad(aBad
);
4528 eLastOp
= pBadToken
? pBadToken
->GetOpCode() : ocNone
;
4535 bool bMayBeFuncName
;
4536 bool bAsciiNonAlnum
; // operators, separators, ...
4537 if ( cSymbol
[0] < 128 )
4539 bMayBeFuncName
= rtl::isAsciiAlpha(cSymbol
[0])
4540 || (cSymbol
[0] == '_' && mxSymbols
->isOOXML() && rtl::isAsciiAlpha(cSymbol
[1]));
4541 if (!bMayBeFuncName
&& (cSymbol
[0] == '_' && cSymbol
[1] == '_') && !comphelper::IsFuzzing())
4543 bMayBeFuncName
= officecfg::Office::Common::Misc::ExperimentalMode::get();
4546 bAsciiNonAlnum
= !bMayBeFuncName
&& !rtl::isAsciiDigit( cSymbol
[0] );
4550 OUString
aTmpStr( cSymbol
[0] );
4551 bMayBeFuncName
= pCharClass
->isLetter( aTmpStr
, 0 );
4552 bAsciiNonAlnum
= false;
4555 // Within a TableRef anything except an unescaped '[' or ']' is an item
4556 // or a column specifier, do not attempt to recognize any other single
4557 // operator there so even [,] or [+] for a single character column
4558 // specifier works. Note that space between two ocTableRefOpen is not
4559 // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
4560 // without any item or column specifier is valid.
4561 if (bAsciiNonAlnum
&& cSymbol
[1] == 0 && (eLastOp
!= ocTableRefOpen
|| cSymbol
[0] == '[' || cSymbol
[0] == ']'))
4563 // Shortcut for operators and separators that need no further checks or upper.
4564 if (ParseOpCode( OUString( cSymbol
), bInArray
))
4568 if ( bMayBeFuncName
)
4570 // a function name must be followed by a parenthesis
4571 const sal_Unicode
* p
= aFormula
.getStr() + nSrcPos
;
4574 bMayBeFuncName
= ( *p
== '(' );
4577 // Italian ARCTAN.2 resulted in #REF! => ParseOpcode() before
4578 // ParseReference().
4581 bool bAsciiUpper
= false;
4587 const OUString
aOrg( cSymbol
);
4589 // Check for TableRef column specifier first, it may be anything.
4590 if (cSymbol
[0] != '#' && !maTableRefs
.empty() && maTableRefs
.back().mnLevel
)
4592 if (ParseTableRefColumn( aOrg
))
4594 // Do not attempt to resolve as any other name.
4595 aUpper
= aOrg
; // for ocBad
4596 break; // do; create ocBad token or set error.
4601 bAsciiUpper
= false;
4605 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4606 if (cSymbol
[0] == '#')
4608 // Check for TableRef item specifiers first.
4610 if (!maTableRefs
.empty() && ((nLevel
= maTableRefs
.back().mnLevel
) == 2 || nLevel
== 1))
4612 if (ParseTableRefItem( aUpper
))
4616 // This can be either an error constant ...
4617 if (ParseErrorConstant( aUpper
))
4620 // ... or some invalidated reference starting with #REF!
4621 // which is handled after the do loop.
4623 break; // do; create ocBad token or set error.
4625 if (ParseOpCode( aUpper
, bInArray
))
4631 if (aUpper
.isEmpty())
4632 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4633 if (ParseOpCode( aUpper
, bInArray
))
4637 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4638 // referred => ParseReference() before ParseValue().
4639 // Preserve case of file names in external references.
4640 if (ParseReference( aOrg
))
4642 if (mbRewind
) // Range operator, but no direct reference.
4643 continue; // do; up to range operator.
4644 // If a syntactically correct reference was recognized but invalid
4645 // e.g. because of non-existing sheet name => entire reference
4646 // ocBad to preserve input instead of #REF!.A1
4647 if (!maRawToken
.IsValidReference(rDoc
))
4649 aUpper
= aOrg
; // ensure for ocBad
4650 break; // do; create ocBad token or set error.
4655 if (aUpper
.isEmpty())
4656 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4658 // ParseBoolean() before ParseValue() to catch inline bools without the kludge
4659 // for inline arrays.
4660 if (bAllowBooleans
&& ParseBoolean( aUpper
))
4663 if (ParseValue( aUpper
))
4666 // User defined names and such do need i18n upper also in ODF.
4667 if (bAsciiUpper
|| mbCharClassesDiffer
)
4669 // Use current system locale here because user defined symbols are
4670 // more likely in that localized language than in the formula
4671 // language. This in corner cases needs to continue to work for
4672 // existing documents and environments.
4673 // Do not change bAsciiUpper from here on for the lowercase() call
4674 // below in the ocBad case to use the correct CharClass.
4675 aUpper
= ScGlobal::getCharClass().uppercase( aOrg
);
4678 if (ParseNamedRange( aUpper
))
4681 // Compiling a named expression during collecting them in import shall
4682 // not match arbitrary names that otherwise if all named expressions
4683 // were present would be recognized as named expression. Such name will
4684 // flag an error below and will be recompiled in a second step later
4685 // with ScRangeData::CompileUnresolvedXML()
4686 if (meExtendedErrorDetection
== EXTENDED_ERROR_DETECTION_NAME_NO_BREAK
&& rDoc
.IsImportingXML())
4689 // Preserve case of file names in external references.
4690 bool bInvalidExternalNameRange
;
4691 if (ParseExternalNamedRange( aOrg
, bInvalidExternalNameRange
))
4693 // Preserve case of file names in external references even when range
4694 // is not valid and previous check failed tdf#89330
4695 if (bInvalidExternalNameRange
)
4697 // add ocBad but do not lowercase
4698 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aOrg
);
4699 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4700 maRawToken
.NewOpCode( ocBad
);
4703 if (ParseDBRange( aUpper
))
4705 // If followed by '(' (with or without space inbetween) it can not be a
4706 // column/row label. Prevent arbitrary content detection.
4707 if (!bMayBeFuncName
&& ParseColRowName( aUpper
))
4709 if (bMayBeFuncName
&& ParseMacro( aUpper
))
4711 if (bMayBeFuncName
&& ParseOpCode2( aUpper
))
4714 if (ParseLambdaFuncName( aOrg
))
4719 // Last chance: it could be a broken invalidated reference that contains
4720 // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4721 // and 2016 until 5.1.4
4722 OUString
aErrRef( mxSymbols
->getSymbol( ocErrRef
));
4723 if (aUpper
.indexOf( aErrRef
) >= 0 && ParseReference( aUpper
, &aErrRef
))
4730 if ( meExtendedErrorDetection
!= EXTENDED_ERROR_DETECTION_NONE
)
4733 SetError( FormulaError::NoName
);
4734 if (meExtendedErrorDetection
== EXTENDED_ERROR_DETECTION_NAME_BREAK
)
4735 return false; // end compilation
4738 // Provide single token information and continue. Do not set an error, that
4739 // would prematurely end compilation. Simple unknown names are handled by
4741 // Use the same CharClass that was used for uppercase.
4742 aUpper
= ((bAsciiUpper
|| mbCharClassesDiffer
) ? ScGlobal::getCharClass() : *pCharClass
).lowercase( aUpper
);
4743 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aUpper
);
4744 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4745 maRawToken
.NewOpCode( ocBad
);
4747 AutoCorrectParsedSymbol();
4751 void ScCompiler::CreateStringFromXMLTokenArray( OUString
& rFormula
, OUString
& rFormulaNmsp
)
4753 bool bExternal
= GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
;
4754 sal_uInt16 nExpectedCount
= bExternal
? 2 : 1;
4755 OSL_ENSURE( pArr
->GetLen() == nExpectedCount
, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4756 if( pArr
->GetLen() == nExpectedCount
)
4758 FormulaToken
** ppTokens
= pArr
->GetArray();
4759 // string tokens expected, GetString() will assert if token type is wrong
4760 rFormula
= ppTokens
[0]->GetString().getString();
4762 rFormulaNmsp
= ppTokens
[1]->GetString().getString();
4768 class ExternalFileInserter
4771 ScExternalRefManager
& mrRefMgr
;
4773 ExternalFileInserter(const ScAddress
& rPos
, ScExternalRefManager
& rRefMgr
) :
4774 maPos(rPos
), mrRefMgr(rRefMgr
) {}
4776 void operator() (sal_uInt16 nFileId
) const
4778 mrRefMgr
.insertRefCell(nFileId
, maPos
);
4784 std::unique_ptr
<ScTokenArray
> ScCompiler::CompileString( const OUString
& rFormula
)
4786 OSL_ENSURE( meGrammar
!= FormulaGrammar::GRAM_EXTERNAL
, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4787 if( meGrammar
== FormulaGrammar::GRAM_EXTERNAL
)
4788 SetGrammar( FormulaGrammar::GRAM_PODF
);
4790 ScTokenArray
aArr(rDoc
);
4792 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
4793 aFormula
= comphelper::string::strip(rFormula
, ' ');
4799 aCorrectedFormula
.clear();
4800 aCorrectedSymbol
.clear();
4802 sal_uInt8 nForced
= 0; // ==formula forces recalc even if cell is not visible
4803 if( nSrcPos
< aFormula
.getLength() && aFormula
[nSrcPos
] == '=' )
4808 aCorrectedFormula
+= "=";
4810 if( nSrcPos
< aFormula
.getLength() && aFormula
[nSrcPos
] == '=' )
4815 aCorrectedFormula
+= "=";
4817 struct FunctionStack
4822 // FunctionStack only used if PODF or OOXML!
4823 bool bPODF
= FormulaGrammar::isPODF( meGrammar
);
4824 bool bOOXML
= FormulaGrammar::isOOXML( meGrammar
);
4825 bool bUseFunctionStack
= (bPODF
|| bOOXML
);
4826 const size_t nAlloc
= 512;
4827 FunctionStack aFuncs
[ nAlloc
];
4828 FunctionStack
* pFunctionStack
= (bUseFunctionStack
&& o3tl::make_unsigned(rFormula
.getLength()) > nAlloc
?
4829 new FunctionStack
[rFormula
.getLength()] : &aFuncs
[0]);
4830 pFunctionStack
[0].eOp
= ocNone
;
4831 pFunctionStack
[0].nSep
= 0;
4832 size_t nFunction
= 0;
4833 size_t nHighWatermark
= 0;
4834 short nBrackets
= 0;
4835 bool bInArray
= false;
4837 while( NextNewToken( bInArray
) )
4839 const OpCode eOp
= maRawToken
.GetOpCode();
4847 if (eLastOp
== ocLet
)
4849 m_aLambda
.bInLambdaFunction
= true;
4850 m_aLambda
.nBracketPos
= nBrackets
;
4851 m_aLambda
.nParaPos
++;
4852 m_aLambda
.nParaCount
= GetPossibleParaCount(rFormula
.subView(nSrcPos
- 1));
4856 if (bUseFunctionStack
)
4859 pFunctionStack
[ nFunction
].eOp
= eLastOp
;
4860 pFunctionStack
[ nFunction
].nSep
= 0;
4861 nHighWatermark
= nFunction
;
4869 SetError( FormulaError::PairExpected
);
4873 aCorrectedSymbol
.clear();
4879 if (m_aLambda
.bInLambdaFunction
&& m_aLambda
.nBracketPos
== nBrackets
)
4881 m_aLambda
.bInLambdaFunction
= false;
4882 m_aLambda
.nBracketPos
= nBrackets
;
4885 if (bUseFunctionStack
&& nFunction
)
4891 if (bUseFunctionStack
)
4892 ++pFunctionStack
[ nFunction
].nSep
;
4894 if (m_aLambda
.bInLambdaFunction
&& m_aLambda
.nBracketPos
+ 1 == nBrackets
)
4895 m_aLambda
.nParaPos
++;
4901 SetError( FormulaError::NestedArray
);
4904 // Don't count following column separator as parameter separator.
4905 if (bUseFunctionStack
)
4908 pFunctionStack
[ nFunction
].eOp
= eOp
;
4909 pFunctionStack
[ nFunction
].nSep
= 0;
4910 nHighWatermark
= nFunction
;
4922 SetError( FormulaError::PairExpected
);
4926 aCorrectedSymbol
.clear();
4929 if (bUseFunctionStack
&& nFunction
)
4933 case ocTableRefOpen
:
4935 // Don't count following item separator as parameter separator.
4936 if (bUseFunctionStack
)
4939 pFunctionStack
[ nFunction
].eOp
= eOp
;
4940 pFunctionStack
[ nFunction
].nSep
= 0;
4941 nHighWatermark
= nFunction
;
4945 case ocTableRefClose
:
4947 if (bUseFunctionStack
&& nFunction
)
4952 case ocColRowNameAuto
:
4953 // The current implementation of column / row labels doesn't
4954 // function correctly in grouped cells.
4955 aArr
.SetShareable(false);
4960 if ((eLastOp
!= ocOpen
|| eOp
!= ocClose
) &&
4961 (eLastOp
== ocOpen
||
4963 eLastOp
== ocArrayRowSep
||
4964 eLastOp
== ocArrayColSep
||
4965 eLastOp
== ocArrayOpen
) &&
4968 eOp
== ocArrayRowSep
||
4969 eOp
== ocArrayColSep
||
4970 eOp
== ocArrayClose
))
4972 // TODO: should we check for known functions with optional empty
4973 // args so the correction dialog can do better?
4974 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaMissingToken
) )
4976 SetError(FormulaError::CodeOverflow
); break;
4981 // Append a parameter for WEEKNUM, all 1.0
4982 // Function is already closed, parameter count is nSep+1
4983 size_t nFunc
= nFunction
+ 1;
4984 if (eOp
== ocClose
&& nFunc
<= nHighWatermark
&&
4985 pFunctionStack
[ nFunc
].nSep
== 0 &&
4986 pFunctionStack
[ nFunc
].eOp
== ocWeek
) // 2nd week start
4988 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaToken( svSep
, ocSep
)) ||
4989 !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaDoubleToken( 1.0)))
4991 SetError(FormulaError::CodeOverflow
); break;
4997 /* TODO: for now this is the only PODF adapter. If there were more,
4998 * factor this out. */
4999 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
5001 pFunctionStack
[ nFunction
].eOp
== ocAddress
&&
5002 pFunctionStack
[ nFunction
].nSep
== 3)
5004 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaToken( svSep
, ocSep
)) ||
5005 !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaDoubleToken( 1.0)))
5007 SetError(FormulaError::CodeOverflow
); break;
5009 ++pFunctionStack
[ nFunction
].nSep
;
5012 FormulaToken
* pNewToken
= static_cast<ScTokenArray
*>(pArr
)->Add( maRawToken
.CreateToken(rDoc
.GetSheetLimits()));
5013 if (!pNewToken
&& eOp
== ocArrayClose
&& pArr
->OpCodeBefore( pArr
->GetLen()) == ocArrayClose
)
5015 // Nested inline array or non-value/non-string in array. The
5016 // original tokens are still in the ScTokenArray and not merged
5017 // into an ScMatrixToken. Set error but keep on tokenizing.
5018 SetError( FormulaError::BadArrayContent
);
5020 else if (!pNewToken
)
5022 SetError(FormulaError::CodeOverflow
);
5025 else if (eLastOp
== ocRange
&& pNewToken
->GetOpCode() == ocPush
&& pNewToken
->GetType() == svSingleRef
)
5027 static_cast<ScTokenArray
*>(pArr
)->MergeRangeReference( aPos
);
5029 else if (eLastOp
== ocDBArea
&& pNewToken
->GetOpCode() == ocTableRefOpen
)
5031 sal_uInt16 nIdx
= pArr
->GetLen() - 1;
5032 const FormulaToken
* pPrev
= pArr
->PeekPrev( nIdx
);
5033 if (pPrev
&& pPrev
->GetOpCode() == ocDBArea
)
5035 ScTableRefToken
* pTableRefToken
= new ScTableRefToken( pPrev
->GetIndex(), ScTableRefToken::TABLE
);
5036 maTableRefs
.emplace_back( pTableRefToken
);
5037 // pPrev may be dead hereafter.
5038 static_cast<ScTokenArray
*>(pArr
)->ReplaceToken( nIdx
, pTableRefToken
,
5039 FormulaTokenArray::ReplaceMode::CODE_ONLY
);
5044 case ocTableRefOpen
:
5045 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
5046 if (maTableRefs
.empty())
5047 SetError(FormulaError::Pair
);
5049 ++maTableRefs
.back().mnLevel
;
5051 case ocTableRefClose
:
5052 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
5053 if (maTableRefs
.empty())
5054 SetError(FormulaError::Pair
);
5057 if (--maTableRefs
.back().mnLevel
== 0)
5058 maTableRefs
.pop_back();
5064 eLastOp
= maRawToken
.GetOpCode();
5066 aCorrectedFormula
+= aCorrectedSymbol
;
5068 if ( mbCloseBrackets
)
5072 FormulaByteToken
aToken( ocArrayClose
);
5073 if( !pArr
->AddToken( aToken
) )
5075 SetError(FormulaError::CodeOverflow
);
5077 else if ( bAutoCorrect
)
5078 aCorrectedFormula
+= mxSymbols
->getSymbol(ocArrayClose
);
5083 FormulaToken
aToken( svSep
, ocClose
);
5084 while( nBrackets
-- )
5086 if( !pArr
->AddToken( aToken
) )
5088 SetError(FormulaError::CodeOverflow
);
5092 aCorrectedFormula
+= mxSymbols
->getSymbol(ocClose
);
5097 pArr
->SetRecalcModeForced();
5099 if (pFunctionStack
!= &aFuncs
[0])
5100 delete [] pFunctionStack
;
5102 // remember pArr, in case a subsequent CompileTokenArray() is executed.
5103 std::unique_ptr
<ScTokenArray
> pNew(new ScTokenArray( std::move(aArr
) ));
5105 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
5107 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
5109 if (!maExternalFiles
.empty())
5111 // Remove duplicates, and register all external files found in this cell.
5112 std::sort(maExternalFiles
.begin(), maExternalFiles
.end());
5113 std::vector
<sal_uInt16
>::iterator itEnd
= std::unique(maExternalFiles
.begin(), maExternalFiles
.end());
5114 std::for_each(maExternalFiles
.begin(), itEnd
, ExternalFileInserter(aPos
, *rDoc
.GetExternalRefManager()));
5115 maExternalFiles
.erase(itEnd
, maExternalFiles
.end());
5121 std::unique_ptr
<ScTokenArray
> ScCompiler::CompileString( const OUString
& rFormula
, const OUString
& rFormulaNmsp
)
5123 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
) || rFormulaNmsp
.isEmpty(),
5124 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
5125 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
) try
5127 ScFormulaParserPool
& rParserPool
= rDoc
.GetFormulaParserPool();
5128 uno::Reference
< sheet::XFormulaParser
> xParser( rParserPool
.getFormulaParser( rFormulaNmsp
), uno::UNO_SET_THROW
);
5129 table::CellAddress aReferencePos
;
5130 ScUnoConversion::FillApiAddress( aReferencePos
, aPos
);
5131 uno::Sequence
< sheet::FormulaToken
> aTokenSeq
= xParser
->parseFormula( rFormula
, aReferencePos
);
5132 ScTokenArray
aTokenArray(rDoc
);
5133 if( ScTokenConversion::ConvertToTokenArray( rDoc
, aTokenArray
, aTokenSeq
) )
5135 // remember pArr, in case a subsequent CompileTokenArray() is executed.
5136 std::unique_ptr
<ScTokenArray
> pNew(new ScTokenArray( std::move(aTokenArray
) ));
5137 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
5139 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
5143 catch( uno::Exception
& )
5146 // no success - fallback to some internal grammar and hope the best
5147 return CompileString( rFormula
);
5150 ScRangeData
* ScCompiler::GetRangeData( const FormulaToken
& rToken
) const
5152 return rDoc
.FindRangeNameBySheetAndIndex( rToken
.GetSheet(), rToken
.GetIndex());
5155 bool ScCompiler::HandleStringName()
5157 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5158 pNew
->AddString(mpToken
->GetString());
5159 PushTokenArray(pNew
, true);
5163 bool ScCompiler::HandleRange()
5166 const ScRangeData
* pRangeData
= GetRangeData( *mpToken
);
5169 FormulaError nErr
= pRangeData
->GetErrCode();
5170 if( nErr
!= FormulaError::NONE
)
5172 else if (mbJumpCommandReorder
)
5174 // put named formula into parentheses.
5175 // But only if there aren't any yet, parenthetical
5176 // ocSep doesn't work, e.g. SUM((...;...))
5177 // or if not directly between ocSep/parenthesis,
5178 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
5179 // in short: if it isn't a self-contained expression.
5180 FormulaToken
* p1
= maArrIterator
.PeekPrevNoSpaces();
5181 FormulaToken
* p2
= maArrIterator
.PeekNextNoSpaces();
5182 OpCode eOp1
= (p1
? p1
->GetOpCode() : ocSep
);
5183 OpCode eOp2
= (p2
? p2
->GetOpCode() : ocSep
);
5184 bool bBorder1
= (eOp1
== ocSep
|| eOp1
== ocOpen
);
5185 bool bBorder2
= (eOp2
== ocSep
|| eOp2
== ocClose
);
5186 bool bAddPair
= !(bBorder1
&& bBorder2
);
5189 pNew
= new ScTokenArray(rDoc
);
5190 pNew
->AddOpCode( ocClose
);
5191 PushTokenArray( pNew
, true );
5193 pNew
= pRangeData
->GetCode()->Clone().release();
5194 pNew
->SetFromRangeName( true );
5195 PushTokenArray( pNew
, true );
5196 if( pRangeData
->HasReferences() )
5198 // Relative sheet references in sheet-local named expressions
5199 // shall still point to the same sheet as if used on the
5200 // original sheet, not shifted to the current position where
5202 SCTAB nSheetTab
= mpToken
->GetSheet();
5203 if (nSheetTab
>= 0 && nSheetTab
!= aPos
.Tab())
5204 AdjustSheetLocalNameRelReferences( nSheetTab
- aPos
.Tab());
5206 SetRelNameReference();
5209 maArrIterator
.Reset();
5212 pNew
= new ScTokenArray(rDoc
);
5213 pNew
->AddOpCode( ocOpen
);
5214 PushTokenArray( pNew
, true );
5221 // No ScRangeData for an already compiled token can happen in BIFF .xls
5222 // import if the original range is not present in the document.
5223 pNew
= new ScTokenArray(rDoc
);
5224 pNew
->Add( new FormulaErrorToken( FormulaError::NoName
));
5225 PushTokenArray( pNew
, true );
5231 bool ScCompiler::HandleExternalReference(const FormulaToken
& _aToken
)
5233 // Handle external range names.
5234 switch (_aToken
.GetType())
5236 case svExternalSingleRef
:
5237 case svExternalDoubleRef
:
5239 case svExternalName
:
5241 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
5242 const OUString
* pFile
= pRefMgr
->getExternalFileName(_aToken
.GetIndex());
5245 SetError(FormulaError::NoName
);
5249 OUString aName
= _aToken
.GetString().getString();
5250 ScExternalRefCache::TokenArrayRef xNew
= pRefMgr
->getRangeNameTokens(
5251 _aToken
.GetIndex(), aName
, &aPos
);
5255 SetError(FormulaError::NoName
);
5259 ScTokenArray
* pNew
= xNew
->Clone().release();
5260 PushTokenArray( pNew
, true);
5261 if (FormulaTokenArrayPlainIterator(*pNew
).GetNextReference() != nullptr)
5263 SetRelNameReference();
5266 maArrIterator
.Reset();
5270 OSL_FAIL("Wrong type for external reference!");
5276 void ScCompiler::AdjustSheetLocalNameRelReferences( SCTAB nDelta
)
5278 for ( auto t
: pArr
->References() )
5280 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
5281 if (rRef1
.IsTabRel())
5282 rRef1
.IncTab( nDelta
);
5283 if ( t
->GetType() == svDoubleRef
)
5285 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
5286 if (rRef2
.IsTabRel())
5287 rRef2
.IncTab( nDelta
);
5292 // reference of named range with relative references
5294 void ScCompiler::SetRelNameReference()
5296 for ( auto t
: pArr
->References() )
5298 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
5299 if ( rRef1
.IsColRel() || rRef1
.IsRowRel() || rRef1
.IsTabRel() )
5300 rRef1
.SetRelName( true );
5301 if ( t
->GetType() == svDoubleRef
)
5303 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
5304 if ( rRef2
.IsColRel() || rRef2
.IsRowRel() || rRef2
.IsTabRel() )
5305 rRef2
.SetRelName( true );
5310 // Wrap-adjust relative references of a RangeName to current position,
5311 // don't call for other token arrays!
5312 void ScCompiler::MoveRelWrap()
5314 for ( auto t
: pArr
->References() )
5316 if ( t
->GetType() == svSingleRef
|| t
->GetType() == svExternalSingleRef
)
5317 ScRefUpdate::MoveRelWrap( rDoc
, aPos
, rDoc
.MaxCol(), rDoc
.MaxRow(), SingleDoubleRefModifier( *t
->GetSingleRef() ).Ref() );
5319 ScRefUpdate::MoveRelWrap( rDoc
, aPos
, rDoc
.MaxCol(), rDoc
.MaxRow(), *t
->GetDoubleRef() );
5323 // Wrap-adjust relative references of a RangeName to current position,
5324 // don't call for other token arrays!
5325 void ScCompiler::MoveRelWrap( const ScTokenArray
& rArr
, const ScDocument
& rDoc
, const ScAddress
& rPos
,
5326 SCCOL nMaxCol
, SCROW nMaxRow
)
5328 for ( auto t
: rArr
.References() )
5330 if ( t
->GetType() == svSingleRef
|| t
->GetType() == svExternalSingleRef
)
5331 ScRefUpdate::MoveRelWrap( rDoc
, rPos
, nMaxCol
, nMaxRow
, SingleDoubleRefModifier( *t
->GetSingleRef() ).Ref() );
5333 ScRefUpdate::MoveRelWrap( rDoc
, rPos
, nMaxCol
, nMaxRow
, *t
->GetDoubleRef() );
5337 bool ScCompiler::IsCharFlagAllConventions(
5338 OUString
const & rStr
, sal_Int32 nPos
, ScCharFlags nFlags
)
5340 sal_Unicode c
= rStr
[ nPos
];
5341 sal_Unicode cLast
= nPos
> 0 ? rStr
[ nPos
-1 ] : 0;
5344 for ( int nConv
= formula::FormulaGrammar::CONV_UNSPECIFIED
;
5345 ++nConv
< formula::FormulaGrammar::CONV_LAST
; )
5347 if (pConventions
[nConv
] &&
5348 ((pConventions
[nConv
]->getCharTableFlags(c
, cLast
) & nFlags
) != nFlags
))
5350 // convention not known => assume valid
5355 return ScGlobal::getCharClass().isLetterNumeric( rStr
, nPos
);
5358 void ScCompiler::CreateStringFromExternal( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
) const
5360 const FormulaToken
* t
= pTokenP
;
5361 sal_uInt16 nFileId
= t
->GetIndex();
5362 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
5363 sal_uInt16 nUsedFileId
= pRefMgr
->convertFileIdToUsedFileId(nFileId
);
5364 const OUString
* pFileName
= pRefMgr
->getExternalFileName(nFileId
);
5368 switch (t
->GetType())
5370 case svExternalName
:
5371 rBuffer
.append(pConv
->makeExternalNameStr( nFileId
, *pFileName
, t
->GetString().getString()));
5373 case svExternalSingleRef
:
5374 pConv
->makeExternalRefStr(rDoc
.GetSheetLimits(),
5375 rBuffer
, GetPos(), nUsedFileId
, *pFileName
, t
->GetString().getString(),
5376 *t
->GetSingleRef());
5378 case svExternalDoubleRef
:
5380 vector
<OUString
> aTabNames
;
5381 pRefMgr
->getAllCachedTableNames(nFileId
, aTabNames
);
5382 // No sheet names is a valid case if external sheets were not
5383 // cached in this document and external document is not reachable,
5384 // else not and worth to be investigated.
5385 SAL_WARN_IF( aTabNames
.empty(), "sc.core", "wrecked cache of external document? '" <<
5386 *pFileName
<< "' '" << t
->GetString().getString() << "'");
5388 pConv
->makeExternalRefStr(
5389 rDoc
.GetSheetLimits(), rBuffer
, GetPos(), nUsedFileId
, *pFileName
, aTabNames
, t
->GetString().getString(),
5390 *t
->GetDoubleRef());
5394 // warning, not error, otherwise we may end up with a never
5395 // ending message box loop if this was the cursor cell to be redrawn.
5396 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
5400 void ScCompiler::CreateStringFromMatrix( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
) const
5402 const ScMatrix
* pMatrix
= pTokenP
->GetMatrix();
5403 SCSIZE nC
, nMaxC
, nR
, nMaxR
;
5405 pMatrix
->GetDimensions( nMaxC
, nMaxR
);
5407 rBuffer
.append( mxSymbols
->getSymbol(ocArrayOpen
) );
5408 for( nR
= 0 ; nR
< nMaxR
; nR
++)
5412 rBuffer
.append( mxSymbols
->getSymbol(ocArrayRowSep
) );
5415 for( nC
= 0 ; nC
< nMaxC
; nC
++)
5419 rBuffer
.append( mxSymbols
->getSymbol(ocArrayColSep
) );
5422 if( pMatrix
->IsValue( nC
, nR
) )
5424 if (pMatrix
->IsBoolean(nC
, nR
))
5425 AppendBoolean(rBuffer
, pMatrix
->GetDouble(nC
, nR
) != 0.0);
5428 FormulaError nErr
= pMatrix
->GetError(nC
, nR
);
5429 if (nErr
!= FormulaError::NONE
)
5430 rBuffer
.append(ScGlobal::GetErrorString(nErr
));
5432 AppendDouble(rBuffer
, pMatrix
->GetDouble(nC
, nR
));
5435 else if( pMatrix
->IsEmpty( nC
, nR
) )
5437 else if( pMatrix
->IsStringOrEmpty( nC
, nR
) )
5438 AppendString( rBuffer
, pMatrix
->GetString(nC
, nR
).getString() );
5441 rBuffer
.append( mxSymbols
->getSymbol(ocArrayClose
) );
5445 void escapeTableRefColumnSpecifier( OUString
& rStr
)
5447 const sal_Int32 n
= rStr
.getLength();
5448 OUStringBuffer
aBuf( n
* 2 );
5449 const sal_Unicode
* p
= rStr
.getStr();
5450 const sal_Unicode
* const pStop
= p
+ n
;
5451 for ( ; p
< pStop
; ++p
)
5453 const sal_Unicode c
= *p
;
5460 aBuf
.append( '\'' );
5467 rStr
= aBuf
.makeStringAndClear();
5471 void ScCompiler::CreateStringFromSingleRef( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5473 const FormulaToken
* p
;
5474 OUString aErrRef
= GetCurrentOpCodeMap()->getSymbol(ocErrRef
);
5475 const OpCode eOp
= _pTokenP
->GetOpCode();
5476 const ScSingleRefData
& rRef
= *_pTokenP
->GetSingleRef();
5477 ScComplexRefData aRef
;
5478 aRef
.Ref1
= aRef
.Ref2
= rRef
;
5479 if ( eOp
== ocColRowName
)
5481 ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5482 if (rDoc
.HasStringData(aAbs
.Col(), aAbs
.Row(), aAbs
.Tab()))
5484 OUString aStr
= rDoc
.GetString(aAbs
, &mrInterpreterContext
);
5485 // Enquote to SingleQuoted.
5486 aStr
= aStr
.replaceAll(u
"'", u
"''");
5487 rBuffer
.append('\'');
5488 rBuffer
.append(aStr
);
5489 rBuffer
.append('\'');
5493 rBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5494 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
,
5495 GetSetupTabNames(), aRef
, true, (pArr
&& pArr
->IsFromRangeName()));
5498 else if (pArr
&& (p
= maArrIterator
.PeekPrevNoSpaces()) && p
->GetOpCode() == ocTableRefOpen
)
5501 ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5502 const ScDBData
* pData
= rDoc
.GetDBAtCursor( aAbs
.Col(), aAbs
.Row(), aAbs
.Tab(), ScDBDataPortion::AREA
);
5503 SAL_WARN_IF( !pData
, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " <<
5504 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5506 aStr
= pData
->GetTableColumnName( aAbs
.Col());
5509 if (pData
&& pData
->HasHeader())
5511 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " <<
5512 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5513 aStr
= rDoc
.GetString(aAbs
, &mrInterpreterContext
);
5517 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " <<
5518 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5522 escapeTableRefColumnSpecifier( aStr
);
5523 rBuffer
.append(aStr
);
5526 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
,
5527 GetSetupTabNames(), aRef
, true, (pArr
&& pArr
->IsFromRangeName()));
5530 void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5532 OUString aErrRef
= GetCurrentOpCodeMap()->getSymbol(ocErrRef
);
5533 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
, GetSetupTabNames(),
5534 *_pTokenP
->GetDoubleRef(), false, (pArr
&& pArr
->IsFromRangeName()));
5537 void ScCompiler::CreateStringFromIndex( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5539 const OpCode eOp
= _pTokenP
->GetOpCode();
5540 OUStringBuffer aBuffer
;
5545 const ScRangeData
* pData
= GetRangeData( *_pTokenP
);
5548 SCTAB nTab
= _pTokenP
->GetSheet();
5549 if (nTab
>= 0 && (nTab
!= aPos
.Tab() || mbRefConventionChartOOXML
))
5551 // Sheet-local on other sheet.
5553 if (rDoc
.GetName( nTab
, aName
))
5555 ScCompiler::CheckTabQuotes( aName
, pConv
->meConv
);
5556 aBuffer
.append( aName
);
5559 aBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5560 aBuffer
.append( pConv
->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR
));
5562 else if (mbRefConventionChartOOXML
)
5564 aBuffer
.append("[0]"
5565 + OUStringChar(pConv
->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR
)));
5567 aBuffer
.append(pData
->GetName());
5573 const ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
5575 aBuffer
.append(pDBData
->GetName());
5580 if (NeedsTableRefTransformation())
5582 // Write the resulting reference if TableRef is not supported.
5583 const ScTableRefToken
* pTR
= dynamic_cast<const ScTableRefToken
*>(_pTokenP
);
5585 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5588 const FormulaToken
* pRef
= pTR
->GetAreaRefRPN();
5590 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5593 switch (pRef
->GetType())
5596 CreateStringFromSingleRef( aBuffer
, pRef
);
5599 CreateStringFromDoubleRef( aBuffer
, pRef
);
5602 AppendErrorConstant( aBuffer
, pRef
->GetError());
5605 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5612 const ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
5614 aBuffer
.append(pDBData
->GetName());
5621 if ( !aBuffer
.isEmpty() )
5622 rBuffer
.append(aBuffer
);
5624 rBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5627 void ScCompiler::LocalizeString( OUString
& rName
) const
5629 ScGlobal::GetAddInCollection()->LocalizeString( rName
);
5632 bool ScCompiler::GetExcelName( OUString
& rName
) const
5634 return ScGlobal::GetAddInCollection()->GetExcelName( rName
, LANGUAGE_ENGLISH_US
, rName
);
5637 FormulaTokenRef
ScCompiler::ExtendRangeReference( FormulaToken
& rTok1
, FormulaToken
& rTok2
)
5639 return extendRangeReference( rDoc
.GetSheetLimits(), rTok1
, rTok2
, aPos
, true/*bReuseDoubleRef*/ );
5642 void ScCompiler::fillAddInToken(::std::vector
< css::sheet::FormulaOpCodeMapEntry
>& _rVec
,bool _bIsEnglish
) const
5644 // All known AddIn functions.
5645 sheet::FormulaOpCodeMapEntry aEntry
;
5646 aEntry
.Token
.OpCode
= ocExternal
;
5648 const LanguageTag
aEnglishLanguageTag(LANGUAGE_ENGLISH_US
);
5649 ScUnoAddInCollection
* pColl
= ScGlobal::GetAddInCollection();
5650 const tools::Long nCount
= pColl
->GetFuncCount();
5651 for (tools::Long i
=0; i
< nCount
; ++i
)
5653 const ScUnoAddInFuncData
* pFuncData
= pColl
->GetFuncData(i
);
5658 // This is used with OOXML import, so GetExcelName() is really
5659 // wanted here until we'll have a parameter to differentiate
5660 // from the general css::sheet::XFormulaOpCodeMapper case and
5661 // use pFuncData->GetUpperEnglish().
5663 if (pFuncData
->GetExcelName( aEnglishLanguageTag
, aName
))
5664 aEntry
.Name
= aName
;
5666 aEntry
.Name
= pFuncData
->GetUpperName();
5669 aEntry
.Name
= pFuncData
->GetUpperLocal();
5670 aEntry
.Token
.Data
<<= pFuncData
->GetOriginalName();
5671 _rVec
.push_back( aEntry
);
5674 // FIXME: what about those old non-UNO AddIns?
5677 bool ScCompiler::HandleColRowName()
5679 ScSingleRefData
& rRef
= *mpToken
->GetSingleRef();
5680 const ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5681 if (!rDoc
.ValidAddress(aAbs
))
5683 SetError( FormulaError::NoRef
);
5686 SCCOL nCol
= aAbs
.Col();
5687 SCROW nRow
= aAbs
.Row();
5688 SCTAB nTab
= aAbs
.Tab();
5689 bool bColName
= rRef
.IsColRel();
5690 SCCOL nMyCol
= aPos
.Col();
5691 SCROW nMyRow
= aPos
.Row();
5692 bool bInList
= false;
5693 bool bValidName
= false;
5694 ScRangePairList
* pRL
= (bColName
?
5695 rDoc
.GetColNameRanges() : rDoc
.GetRowNameRanges());
5697 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5699 const ScRangePair
& rR
= (*pRL
)[i
];
5700 if ( rR
.GetRange(0).Contains( aAbs
) )
5702 bInList
= bValidName
= true;
5703 aRange
= rR
.GetRange(1);
5706 aRange
.aStart
.SetCol( nCol
);
5707 aRange
.aEnd
.SetCol( nCol
);
5711 aRange
.aStart
.SetRow( nRow
);
5712 aRange
.aEnd
.SetRow( nRow
);
5717 if ( !bInList
&& rDoc
.GetDocOptions().IsLookUpColRowNames() )
5718 { // automagically or created by copying and NamePos isn't in list
5719 ScRefCellValue
aCell(rDoc
, aAbs
);
5720 bool bString
= aCell
.hasString();
5721 if (!bString
&& aCell
.isEmpty())
5722 bString
= true; // empty cell is ok
5724 { // corresponds with ScInterpreter::ScColRowNameAuto()
5728 SCROW nStartRow
= nRow
+ 1;
5729 if ( nStartRow
> rDoc
.MaxRow() )
5730 nStartRow
= rDoc
.MaxRow();
5731 SCROW nMaxRow
= rDoc
.MaxRow();
5732 if ( nMyCol
== nCol
)
5733 { // formula cell in same column
5734 if ( nMyRow
== nStartRow
)
5735 { // take remainder under name cell
5737 if ( nStartRow
> rDoc
.MaxRow() )
5738 nStartRow
= rDoc
.MaxRow();
5740 else if ( nMyRow
> nStartRow
)
5741 { // from name cell down to formula cell
5742 nMaxRow
= nMyRow
- 1;
5745 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5746 { // next defined ColNameRange below limits row
5747 const ScRangePair
& rR
= (*pRL
)[i
];
5748 const ScRange
& rRange
= rR
.GetRange(1);
5749 if ( rRange
.aStart
.Col() <= nCol
&& nCol
<= rRange
.aEnd
.Col() )
5750 { // identical column range
5751 SCROW nTmp
= rRange
.aStart
.Row();
5752 if ( nStartRow
< nTmp
&& nTmp
<= nMaxRow
)
5756 aRange
.aStart
.Set( nCol
, nStartRow
, nTab
);
5757 aRange
.aEnd
.Set( nCol
, nMaxRow
, nTab
);
5761 SCCOL nStartCol
= nCol
+ 1;
5762 if ( nStartCol
> rDoc
.MaxCol() )
5763 nStartCol
= rDoc
.MaxCol();
5764 SCCOL nMaxCol
= rDoc
.MaxCol();
5765 if ( nMyRow
== nRow
)
5766 { // formula cell in same row
5767 if ( nMyCol
== nStartCol
)
5768 { // take remainder right from name cell
5770 if ( nStartCol
> rDoc
.MaxCol() )
5771 nStartCol
= rDoc
.MaxCol();
5773 else if ( nMyCol
> nStartCol
)
5774 { // from name cell right to formula cell
5775 nMaxCol
= nMyCol
- 1;
5778 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5779 { // next defined RowNameRange to the right limits column
5780 const ScRangePair
& rR
= (*pRL
)[i
];
5781 const ScRange
& rRange
= rR
.GetRange(1);
5782 if ( rRange
.aStart
.Row() <= nRow
&& nRow
<= rRange
.aEnd
.Row() )
5783 { // identical row range
5784 SCCOL nTmp
= rRange
.aStart
.Col();
5785 if ( nStartCol
< nTmp
&& nTmp
<= nMaxCol
)
5789 aRange
.aStart
.Set( nStartCol
, nRow
, nTab
);
5790 aRange
.aEnd
.Set( nMaxCol
, nRow
, nTab
);
5796 // And now the magic to distinguish between a range and a single
5797 // cell thereof, which is picked position-dependent of the formula
5798 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5799 // SingleRef matching the column/row of the formula cell is
5800 // generated. A ocColRowName or ocIntersect as a neighbor results
5801 // in a range. Special case: if label is valid for a single cell, a
5802 // position independent SingleRef is generated.
5803 bool bSingle
= (aRange
.aStart
== aRange
.aEnd
);
5809 FormulaToken
* p1
= maArrIterator
.PeekPrevNoSpaces();
5810 FormulaToken
* p2
= maArrIterator
.PeekNextNoSpaces();
5811 // begin/end of a formula => single
5812 OpCode eOp1
= p1
? p1
->GetOpCode() : ocAdd
;
5813 OpCode eOp2
= p2
? p2
->GetOpCode() : ocAdd
;
5814 if ( eOp1
!= ocColRowName
&& eOp1
!= ocIntersect
5815 && eOp2
!= ocColRowName
&& eOp2
!= ocIntersect
)
5817 if ( (SC_OPCODE_START_BIN_OP
<= eOp1
&& eOp1
< SC_OPCODE_STOP_BIN_OP
) ||
5818 (SC_OPCODE_START_BIN_OP
<= eOp2
&& eOp2
< SC_OPCODE_STOP_BIN_OP
))
5822 { // column and/or row must match range
5825 bFound
= (aRange
.aStart
.Row() <= nMyRow
5826 && nMyRow
<= aRange
.aEnd
.Row());
5828 aRange
.aStart
.SetRow( nMyRow
);
5832 bFound
= (aRange
.aStart
.Col() <= nMyCol
5833 && nMyCol
<= aRange
.aEnd
.Col());
5835 aRange
.aStart
.SetCol( nMyCol
);
5842 SetError(FormulaError::NoRef
);
5843 else if (mbJumpCommandReorder
)
5845 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5848 ScSingleRefData aRefData
;
5849 aRefData
.InitAddress( aRange
.aStart
);
5851 aRefData
.SetColRel( true );
5853 aRefData
.SetRowRel( true );
5854 aRefData
.SetAddress(rDoc
.GetSheetLimits(), aRange
.aStart
, aPos
);
5855 pNew
->AddSingleReference( aRefData
);
5859 ScComplexRefData aRefData
;
5860 aRefData
.InitRange( aRange
);
5863 aRefData
.Ref1
.SetColRel( true );
5864 aRefData
.Ref2
.SetColRel( true );
5868 aRefData
.Ref1
.SetRowRel( true );
5869 aRefData
.Ref2
.SetRowRel( true );
5871 aRefData
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
5873 pNew
->AddDoubleReference( aRefData
);
5876 pNew
->Add( new ScDoubleRefToken( rDoc
.GetSheetLimits(), aRefData
, ocColRowNameAuto
) );
5879 PushTokenArray( pNew
, true );
5884 SetError(FormulaError::NoName
);
5888 bool ScCompiler::HandleDbData()
5890 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(mpToken
->GetIndex());
5892 SetError(FormulaError::NoName
);
5893 else if (mbJumpCommandReorder
)
5895 ScComplexRefData aRefData
;
5896 aRefData
.InitFlags();
5898 pDBData
->GetArea(aRange
);
5899 aRange
.aEnd
.SetTab(aRange
.aStart
.Tab());
5900 aRefData
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
5901 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5902 pNew
->AddDoubleReference( aRefData
);
5903 PushTokenArray( pNew
, true );
5909 bool ScCompiler::GetTokenIfOpCode( OpCode eOp
)
5911 const formula::FormulaToken
* p
= maArrIterator
.PeekNextNoSpaces();
5912 if (p
&& p
->GetOpCode() == eOp
)
5918 /* Documentation on MS-Excel Table structured references:
5919 * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
5920 * * as of Excel 2013
5921 * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
5922 * * look for structure-reference
5925 bool ScCompiler::HandleTableRef()
5927 ScTableRefToken
* pTR
= dynamic_cast<ScTableRefToken
*>(mpToken
.get());
5930 SetError(FormulaError::UnknownToken
);
5934 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex( pTR
->GetIndex());
5936 SetError(FormulaError::NoName
);
5937 else if (mbJumpCommandReorder
)
5940 pDBData
->GetArea(aDBRange
);
5941 aDBRange
.aEnd
.SetTab(aDBRange
.aStart
.Tab());
5942 ScRange
aRange( aDBRange
);
5943 FormulaError nError
= FormulaError::NONE
;
5944 bool bForwardToClose
= false;
5945 ScTableRefToken::Item eItem
= pTR
->GetItem();
5948 case ScTableRefToken::TABLE
:
5950 // The table name without items references the table data,
5951 // without headers or totals.
5952 if (pDBData
->HasHeader())
5953 aRange
.aStart
.IncRow();
5954 if (pDBData
->HasTotals())
5955 aRange
.aEnd
.IncRow(-1);
5956 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5957 nError
= FormulaError::NoRef
;
5958 bForwardToClose
= true;
5961 case ScTableRefToken::ALL
:
5963 bForwardToClose
= true;
5966 case ScTableRefToken::HEADERS
:
5968 if (pDBData
->HasHeader())
5969 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
5971 nError
= FormulaError::NoRef
;
5972 bForwardToClose
= true;
5975 case ScTableRefToken::DATA
:
5977 if (pDBData
->HasHeader())
5978 aRange
.aStart
.IncRow();
5981 case ScTableRefToken::HEADERS_DATA
:
5983 if (pDBData
->HasTotals())
5984 aRange
.aEnd
.IncRow(-1);
5985 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5986 nError
= FormulaError::NoRef
;
5987 bForwardToClose
= true;
5990 case ScTableRefToken::TOTALS
:
5992 if (pDBData
->HasTotals())
5993 aRange
.aStart
.SetRow( aRange
.aEnd
.Row());
5995 nError
= FormulaError::NoRef
;
5996 bForwardToClose
= true;
5999 case ScTableRefToken::DATA_TOTALS
:
6001 if (pDBData
->HasHeader())
6002 aRange
.aStart
.IncRow();
6003 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
6004 nError
= FormulaError::NoRef
;
6005 bForwardToClose
= true;
6008 case ScTableRefToken::THIS_ROW
:
6010 if (aRange
.aStart
.Row() <= aPos
.Row() && aPos
.Row() <= aRange
.aEnd
.Row())
6012 aRange
.aStart
.SetRow( aPos
.Row());
6013 aRange
.aEnd
.SetRow( aPos
.Row());
6017 nError
= FormulaError::NoValue
;
6018 // For *some* relative row reference in named
6019 // expressions' thisrow special handling below.
6020 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
6022 bForwardToClose
= true;
6026 bool bColumnRange
= false;
6027 bool bCol1Rel
= false;
6028 bool bCol1RelName
= false;
6030 if (bForwardToClose
&& GetTokenIfOpCode( ocTableRefOpen
))
6044 const formula::FormulaToken
* p
= maArrIterator
.PeekNextNoSpaces();
6049 switch (p
->GetOpCode())
6051 case ocTableRefOpen
:
6052 eState
= ((eState
== sOpen
|| eState
== sSep
) ? sOpen
: sStop
);
6055 SetError( FormulaError::Pair
);
6059 case ocTableRefItemAll
:
6060 case ocTableRefItemHeaders
:
6061 case ocTableRefItemData
:
6062 case ocTableRefItemTotals
:
6063 case ocTableRefItemThisRow
:
6064 eState
= ((eState
== sOpen
) ? sItem
: sStop
);
6066 case ocTableRefClose
:
6067 eState
= ((eState
== sItem
|| eState
== sClose
) ? sClose
: sStop
);
6068 if (eState
!= sStop
&& --nLevel
== 0)
6072 eState
= ((eState
== sClose
) ? sSep
: sStop
);
6075 if (eState
== sOpen
&& p
->GetType() == svSingleRef
)
6077 bColumnRange
= true;
6078 bCol1Rel
= p
->GetSingleRef()->IsColRel();
6079 bCol1RelName
= p
->GetSingleRef()->IsRelName();
6089 if (nError
== FormulaError::NONE
)
6090 nError
= FormulaError::NoName
;
6095 if (eState
!= sStop
)
6097 if (eState
== sLast
)
6100 } while (eState
!= sStop
);
6102 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
6103 if (nError
== FormulaError::NONE
|| nError
== FormulaError::NoValue
)
6105 bool bCol2Rel
= false;
6106 bool bCol2RelName
= false;
6107 // The FormulaError::NoValue case generates a thisrow reference that can be
6108 // used to save named expressions in A1 syntax notation.
6111 // Limit range to specified columns.
6112 ScRange
aColRange( ScAddress::INITIALIZE_INVALID
);
6113 switch (mpToken
->GetType())
6117 aColRange
.aStart
= aColRange
.aEnd
= mpToken
->GetSingleRef()->toAbs(rDoc
, aPos
);
6118 if ( GetTokenIfOpCode( ocTableRefClose
) && (nLevel
--) &&
6119 GetTokenIfOpCode( ocRange
) &&
6120 GetTokenIfOpCode( ocTableRefOpen
) && (++nLevel
) &&
6121 GetTokenIfOpCode( ocPush
))
6123 if (mpToken
->GetType() != svSingleRef
)
6124 aColRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
6127 aColRange
.aEnd
= mpToken
->GetSingleRef()->toAbs(rDoc
, aPos
);
6128 aColRange
.PutInOrder();
6129 bCol2Rel
= mpToken
->GetSingleRef()->IsColRel();
6130 bCol2RelName
= mpToken
->GetSingleRef()->IsRelName();
6138 // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses
6139 if (aColRange
.aStart
.Row() != aDBRange
.aStart
.Row() || aColRange
.aEnd
.Row() != aDBRange
.aStart
.Row())
6140 aRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
6143 aColRange
.aEnd
.SetRow( aRange
.aEnd
.Row());
6144 aRange
= aRange
.Intersection( aColRange
);
6147 if (aRange
.IsValid())
6149 if (aRange
.aStart
== aRange
.aEnd
)
6151 ScSingleRefData aRefData
;
6152 aRefData
.InitFlags();
6153 aRefData
.SetColRel( bCol1Rel
);
6154 if (eItem
== ScTableRefToken::THIS_ROW
)
6156 aRefData
.SetRowRel( true);
6158 bCol1RelName
= pArr
->IsFromRangeName();
6160 aRefData
.SetRelName( bCol1RelName
);
6161 aRefData
.SetFlag3D( true);
6162 if (nError
!= FormulaError::NONE
)
6164 aRefData
.SetAddress( rDoc
.GetSheetLimits(), aRange
.aStart
, aRange
.aStart
);
6165 pTR
->SetAreaRefRPN( new ScSingleRefToken(rDoc
.GetSheetLimits(), aRefData
)); // set reference at TableRef
6166 pNew
->Add( new FormulaErrorToken( nError
)); // set error in RPN
6170 aRefData
.SetAddress( rDoc
.GetSheetLimits(), aRange
.aStart
, aPos
);
6171 pTR
->SetAreaRefRPN( pNew
->AddSingleReference( aRefData
));
6176 ScComplexRefData aRefData
;
6177 aRefData
.InitFlags();
6178 aRefData
.Ref1
.SetColRel( bCol1Rel
);
6179 aRefData
.Ref2
.SetColRel( bCol2Rel
);
6180 bool bRelName
= bCol1RelName
|| bCol2RelName
;
6181 if (eItem
== ScTableRefToken::THIS_ROW
)
6183 aRefData
.Ref1
.SetRowRel( true);
6184 aRefData
.Ref2
.SetRowRel( true);
6186 bRelName
= pArr
->IsFromRangeName();
6188 aRefData
.Ref1
.SetRelName( bRelName
);
6189 aRefData
.Ref2
.SetRelName( bRelName
);
6190 aRefData
.Ref1
.SetFlag3D( true);
6191 if (nError
!= FormulaError::NONE
)
6193 aRefData
.SetRange( rDoc
.GetSheetLimits(), aRange
, aRange
.aStart
);
6194 pTR
->SetAreaRefRPN( new ScDoubleRefToken(rDoc
.GetSheetLimits(), aRefData
)); // set reference at TableRef
6195 pNew
->Add( new FormulaErrorToken( nError
)); // set error in RPN
6199 aRefData
.SetRange( rDoc
.GetSheetLimits(), aRange
, aPos
);
6200 pTR
->SetAreaRefRPN( pNew
->AddDoubleReference( aRefData
));
6206 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( FormulaError::NoRef
)));
6211 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( nError
)));
6213 while (nLevel
-- > 0)
6215 if (!GetTokenIfOpCode( ocTableRefClose
))
6216 SetError( FormulaError::Pair
);
6218 PushTokenArray( pNew
, true );
6224 formula::ParamClass
ScCompiler::GetForceArrayParameter( const formula::FormulaToken
* pToken
, sal_uInt16 nParam
) const
6226 return ScParameterClassification::GetParameterType( pToken
, nParam
);
6229 bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken
* token
, int parameter
)
6231 formula::ParamClass param
= ScParameterClassification::GetParameterType( token
, parameter
);
6232 return param
== Value
|| param
== Array
;
6235 bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken
* token
) const
6239 formula::ParamClass paramClass
= token
->GetInForceArray();
6240 if (paramClass
== formula::ForceArray
6241 || paramClass
== formula::ReferenceOrForceArray
6242 || paramClass
== formula::SuppressedReferenceOrForceArray
6243 || paramClass
== formula::ReferenceOrRefArray
)
6247 formula::ParamClass returnType
= ScParameterClassification::GetParameterType( token
, SAL_MAX_UINT16
);
6248 return returnType
== formula::Reference
;
6251 void ScCompiler::HandleIIOpCode(FormulaToken
* token
, FormulaToken
*** pppToken
, sal_uInt8 nNumParams
)
6256 if(!HandleIIOpCodeInternal(token
, pppToken
, nNumParams
))
6257 mUnhandledPossibleImplicitIntersectionsOpCodes
.insert(token
->GetOpCode());
6259 HandleIIOpCodeInternal(token
, pppToken
, nNumParams
);
6263 // return true if opcode is handled
6264 bool ScCompiler::HandleIIOpCodeInternal(FormulaToken
* token
, FormulaToken
*** pppToken
, sal_uInt8 nNumParams
)
6266 if (nNumParams
> 0 && *pppToken
[0] == nullptr)
6267 return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray())
6269 const OpCode nOpCode
= token
->GetOpCode();
6271 if (nOpCode
== ocPush
)
6273 if(token
->GetType() == svDoubleRef
)
6274 mUnhandledPossibleImplicitIntersections
.insert( token
);
6277 else if (nOpCode
== ocSumIf
|| nOpCode
== ocAverageIf
)
6279 if (nNumParams
!= 3)
6282 if (!(pppToken
[0] && pppToken
[2] && *pppToken
[0] && *pppToken
[2]))
6285 if ((*pppToken
[0])->GetType() != svDoubleRef
)
6288 const StackVar eSumRangeType
= (*pppToken
[2])->GetType();
6290 if ( eSumRangeType
!= svSingleRef
&& eSumRangeType
!= svDoubleRef
)
6293 const ScComplexRefData
& rBaseRange
= *(*pppToken
[0])->GetDoubleRef();
6295 ScComplexRefData aSumRange
;
6296 if (eSumRangeType
== svSingleRef
)
6298 aSumRange
.Ref1
= *(*pppToken
[2])->GetSingleRef();
6299 aSumRange
.Ref2
= aSumRange
.Ref1
;
6302 aSumRange
= *(*pppToken
[2])->GetDoubleRef();
6304 CorrectSumRange(rBaseRange
, aSumRange
, pppToken
[2]);
6305 // TODO mark parameters as handled
6308 else if (nOpCode
>= SC_OPCODE_START_1_PAR
&& nOpCode
< SC_OPCODE_STOP_1_PAR
)
6310 if (nNumParams
!= 1)
6313 if( !ParameterMayBeImplicitIntersection( token
, 0 ))
6315 if (SkipImplicitIntersectionOptimization(token
))
6318 if ((*pppToken
[0])->GetType() != svDoubleRef
)
6321 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6324 else if ((nOpCode
>= SC_OPCODE_START_BIN_OP
&& nOpCode
< SC_OPCODE_STOP_BIN_OP
6325 && nOpCode
!= ocAnd
&& nOpCode
!= ocOr
)
6326 || nOpCode
== ocRound
|| nOpCode
== ocRoundUp
|| nOpCode
== ocRoundDown
)
6328 if (nNumParams
!= 2)
6331 if( !ParameterMayBeImplicitIntersection( token
, 0 ) || !ParameterMayBeImplicitIntersection( token
, 1 ))
6333 if (SkipImplicitIntersectionOptimization(token
))
6336 // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
6337 if ((*pppToken
[0])->GetType() == svDoubleRef
&& (*pppToken
[1])->GetType() != svMatrix
)
6338 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6339 if ((*pppToken
[1])->GetType() == svDoubleRef
&& (*pppToken
[0])->GetType() != svMatrix
)
6340 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[1], token
);
6343 else if ((nOpCode
>= SC_OPCODE_START_UN_OP
&& nOpCode
< SC_OPCODE_STOP_UN_OP
)
6344 || nOpCode
== ocPercentSign
)
6346 if (nNumParams
!= 1)
6349 if( !ParameterMayBeImplicitIntersection( token
, 0 ))
6351 if (SkipImplicitIntersectionOptimization(token
))
6354 if ((*pppToken
[0])->GetType() == svDoubleRef
)
6355 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6358 else if (nOpCode
== ocVLookup
)
6360 if (nNumParams
!= 3 && nNumParams
!= 4)
6363 if (SkipImplicitIntersectionOptimization(token
))
6366 assert( ParameterMayBeImplicitIntersection( token
, 0 ));
6367 assert( !ParameterMayBeImplicitIntersection( token
, 1 ));
6368 assert( ParameterMayBeImplicitIntersection( token
, 2 ));
6369 assert( ParameterMayBeImplicitIntersection( token
, 3 ));
6370 if ((*pppToken
[2])->GetType() == svDoubleRef
)
6371 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[2], token
);
6372 if ((*pppToken
[0])->GetType() == svDoubleRef
)
6373 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6374 if (nNumParams
== 4 && (*pppToken
[3])->GetType() == svDoubleRef
)
6375 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[3], token
);
6376 // a range for the second parameters is not an implicit intersection
6377 mUnhandledPossibleImplicitIntersections
.erase( *pppToken
[ 1 ] );
6382 bool possibleII
= false;
6383 for( int i
= 0; i
< nNumParams
; ++i
)
6385 if( ParameterMayBeImplicitIntersection( token
, i
)
6386 && (*pppToken
[i
])->GetType() == svDoubleRef
)
6394 // all parameters have been handled, they are not implicit intersections
6395 for( int i
= 0; i
< nNumParams
; ++i
)
6396 mUnhandledPossibleImplicitIntersections
.erase( *pppToken
[ i
] );
6404 void ScCompiler::PostProcessCode()
6406 for( const PendingImplicitIntersectionOptimization
& item
: mPendingImplicitIntersectionOptimizations
)
6408 if( *item
.parameterLocation
!= item
.parameter
) // the parameter has been changed somehow
6410 if( item
.parameterLocation
>= pCode
) // the location is not inside the code (pCode points after the end)
6412 // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
6413 if( item
.operation
->IsInForceArray())
6415 ReplaceDoubleRefII( item
.parameterLocation
);
6417 mPendingImplicitIntersectionOptimizations
.clear();
6420 void ScCompiler::AnnotateOperands()
6422 AnnotateTrimOnDoubleRefs();
6425 void ScCompiler::ReplaceDoubleRefII(FormulaToken
** ppDoubleRefTok
)
6427 const ScComplexRefData
* pRange
= (*ppDoubleRefTok
)->GetDoubleRef();
6431 const ScComplexRefData
& rRange
= *pRange
;
6433 // Can't do optimization reliably in this case (when row references are absolute).
6434 // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
6435 // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
6436 // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
6437 // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
6438 // fix this, but that is unknown at this stage, so skip such cases.
6439 if (!rRange
.Ref1
.IsRowRel() && !rRange
.Ref2
.IsRowRel())
6442 ScRange aAbsRange
= rRange
.toAbs(rDoc
, aPos
);
6443 if (aAbsRange
.aStart
== aAbsRange
.aEnd
)
6444 return; // Nothing to do (trivial case).
6448 if (!DoubleRefToPosSingleRefScalarCase(aAbsRange
, aAddr
, aPos
))
6451 ScSingleRefData aSingleRef
;
6452 aSingleRef
.InitFlags();
6453 aSingleRef
.SetColRel(rRange
.Ref1
.IsColRel());
6454 aSingleRef
.SetRowRel(true);
6455 aSingleRef
.SetTabRel(rRange
.Ref1
.IsTabRel());
6456 aSingleRef
.SetAddress(rDoc
.GetSheetLimits(), aAddr
, aPos
);
6458 // Replace the original doubleref token with computed singleref token
6459 FormulaToken
* pNewSingleRefTok
= new ScSingleRefToken(rDoc
.GetSheetLimits(), aSingleRef
);
6460 (*ppDoubleRefTok
)->DecRef();
6461 *ppDoubleRefTok
= pNewSingleRefTok
;
6462 pNewSingleRefTok
->IncRef();
6465 bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange
& rRange
, ScAddress
& rAdr
, const ScAddress
& rFormulaPos
)
6467 assert(rRange
.aStart
!= rRange
.aEnd
);
6470 SCCOL nMyCol
= rFormulaPos
.Col();
6471 SCROW nMyRow
= rFormulaPos
.Row();
6472 SCTAB nMyTab
= rFormulaPos
.Tab();
6476 nTab
= rRange
.aStart
.Tab();
6477 if ( rRange
.aStart
.Col() <= nMyCol
&& nMyCol
<= rRange
.aEnd
.Col() )
6479 nRow
= rRange
.aStart
.Row();
6480 if ( nRow
== rRange
.aEnd
.Row() )
6485 else if ( nTab
!= nMyTab
&& nTab
== rRange
.aEnd
.Tab()
6486 && rRange
.aStart
.Row() <= nMyRow
&& nMyRow
<= rRange
.aEnd
.Row() )
6493 else if ( rRange
.aStart
.Row() <= nMyRow
&& nMyRow
<= rRange
.aEnd
.Row() )
6495 nCol
= rRange
.aStart
.Col();
6496 if ( nCol
== rRange
.aEnd
.Col() )
6501 else if ( nTab
!= nMyTab
&& nTab
== rRange
.aEnd
.Tab()
6502 && rRange
.aStart
.Col() <= nMyCol
&& nMyCol
<= rRange
.aEnd
.Col() )
6511 if ( nTab
== rRange
.aEnd
.Tab() )
6513 else if ( nTab
<= nMyTab
&& nMyTab
<= rRange
.aEnd
.Tab() )
6518 rAdr
.Set( nCol
, nRow
, nTab
);
6524 static void lcl_GetColRowDeltas(const ScRange
& rRange
, SCCOL
& rXDelta
, SCROW
& rYDelta
)
6526 rXDelta
= rRange
.aEnd
.Col() - rRange
.aStart
.Col();
6527 rYDelta
= rRange
.aEnd
.Row() - rRange
.aStart
.Row();
6530 bool ScCompiler::AdjustSumRangeShape(const ScComplexRefData
& rBaseRange
, ScComplexRefData
& rSumRange
)
6532 ScRange aAbs
= rSumRange
.toAbs(rDoc
, aPos
);
6534 // Current sum-range end col/row
6535 SCCOL nEndCol
= aAbs
.aEnd
.Col();
6536 SCROW nEndRow
= aAbs
.aEnd
.Row();
6538 // Current behaviour is, we will get a #NAME? for the below case, so bail out.
6539 // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula
6540 // has a single-ref as the sum-range.
6541 if (!rDoc
.ValidCol(nEndCol
) || !rDoc
.ValidRow(nEndRow
))
6544 SCCOL nXDeltaSum
= 0;
6545 SCROW nYDeltaSum
= 0;
6547 lcl_GetColRowDeltas(aAbs
, nXDeltaSum
, nYDeltaSum
);
6549 aAbs
= rBaseRange
.toAbs(rDoc
, aPos
);
6553 lcl_GetColRowDeltas(aAbs
, nXDelta
, nYDelta
);
6555 if (nXDelta
== nXDeltaSum
&&
6556 nYDelta
== nYDeltaSum
)
6557 return false; // shapes of base-range match current sum-range
6559 // Try to make the sum-range to take the same shape as base-range,
6560 // by adjusting Ref2 member of rSumRange if the resultant sum-range don't
6561 // go out-of-bounds.
6563 SCCOL nXInc
= nXDelta
- nXDeltaSum
;
6564 SCROW nYInc
= nYDelta
- nYDeltaSum
;
6566 // Don't let a valid End[Col,Row] go beyond (rDoc.MaxCol(),rDoc.MaxRow()) to match
6567 // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks
6568 // the base-range by the (out-of-bound)amount clipped off the sum-range.
6569 // TODO: Probably we can optimize (from threading perspective) rBaseRange
6570 // by shrinking it here correspondingly (?)
6571 if (nEndCol
+ nXInc
> rDoc
.MaxCol())
6572 nXInc
= rDoc
.MaxCol() - nEndCol
;
6573 if (nEndRow
+ nYInc
> rDoc
.MaxRow())
6574 nYInc
= rDoc
.MaxRow() - nEndRow
;
6576 rSumRange
.Ref2
.IncCol(nXInc
);
6577 rSumRange
.Ref2
.IncRow(nYInc
);
6582 void ScCompiler::CorrectSumRange(const ScComplexRefData
& rBaseRange
,
6583 ScComplexRefData
& rSumRange
,
6584 FormulaToken
** ppSumRangeToken
)
6586 if (!AdjustSumRangeShape(rBaseRange
, rSumRange
))
6589 // Replace sum-range token
6590 FormulaToken
* pNewSumRangeTok
= new ScDoubleRefToken(rDoc
.GetSheetLimits(), rSumRange
);
6591 (*ppSumRangeToken
)->DecRef();
6592 *ppSumRangeToken
= pNewSumRangeTok
;
6593 pNewSumRangeTok
->IncRef();
6596 void ScCompiler::AnnotateTrimOnDoubleRefs()
6598 if (!pCode
|| !(*(pCode
- 1)))
6601 // OpCode of the "root" operator (which is already in RPN array).
6602 OpCode eOpCode
= (*(pCode
- 1))->GetOpCode();
6603 // Param number of the "root" operator (which is already in RPN array).
6604 sal_uInt8 nRootParam
= (*(pCode
- 1))->GetByte();
6605 // eOpCode can be some operator which does not change with operands with or contains zero values.
6606 if (eOpCode
== ocSum
)
6608 FormulaToken
** ppTok
= pCode
- 2; // exclude the root operator.
6609 // The following loop runs till a "pattern" is found or there is a mismatch
6610 // and marks the push DoubleRef arguments as trimmable when there is a match.
6612 // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6613 // such that one of the operands of ocEqual is a double-ref.
6614 // Examples of formula that matches this are:
6615 // SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
6616 // SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
6617 // SUM(IF(E:E=16,F:F)*$H$1*100)
6618 bool bTillClose
= true;
6619 bool bCloseTillIf
= false;
6620 sal_Int16 nToksTillIf
= 0;
6621 constexpr sal_Int16 MAXDIST_IF
= 15;
6624 FormulaToken
* pTok
= *ppTok
;
6625 OpCode eCurrOp
= pTok
->GetOpCode();
6628 // TODO : Is there a better way to handle this ?
6629 // ocIf is too far off from the sum opcode.
6630 if (nToksTillIf
> MAXDIST_IF
)
6647 bCloseTillIf
= true;
6657 if (!pTok
->IsInForceArray())
6660 const short nJumpCount
= pTok
->GetJump()[0];
6661 if (nJumpCount
!= 2) // Should have THEN but no ELSE.
6664 OpCode eCompOp
= (*(ppTok
- 1))->GetOpCode();
6665 if (eCompOp
!= ocEqual
)
6668 FormulaToken
* pLHS
= *(ppTok
- 2);
6669 FormulaToken
* pRHS
= *(ppTok
- 3);
6670 if (((pLHS
->GetType() == svSingleRef
|| pLHS
->GetType() == svDouble
) && pRHS
->GetType() == svDoubleRef
) ||
6671 ((pRHS
->GetType() == svSingleRef
|| pRHS
->GetType() == svDouble
) && pLHS
->GetType() == svDoubleRef
))
6673 if (pLHS
->GetType() == svDoubleRef
)
6674 pLHS
->GetDoubleRef()->SetTrimToData(true);
6676 pRHS
->GetDoubleRef()->SetTrimToData(true);
6687 else if (eOpCode
== ocSumProduct
)
6689 FormulaToken
** ppTok
= pCode
- 2; // exclude the root operator.
6690 // The following loop runs till a "pattern" is found or there is a mismatch
6691 // and marks the push DoubleRef arguments as trimmable when there is a match.
6693 // SUMPRODUCT(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6694 // such that one of the operands of ocEqual is a double-ref.
6695 // Examples of formula that matches this are:
6696 // SUMPRODUCT(IF($A:$A=$L12;$D:$D*G:G))
6697 // Also in case of DoubleRef arguments around other Binary operators can be trimmable inside one parameter
6698 // of the root operator:
6699 // SUMPRODUCT(($D:$D>M47:M47)*($D:$D<M48:M48)*($I:$I=N$41))
6700 bool bTillClose
= true;
6701 bool bCloseTillIf
= false;
6702 sal_Int16 nToksTillIf
= 0;
6703 constexpr sal_Int16 MAXDIST_IF
= 15;
6706 FormulaToken
* pTok
= *ppTok
;
6707 OpCode eCurrOp
= pTok
->GetOpCode();
6710 // TODO : Is there a better way to handle this ?
6711 // ocIf is too far off from the sum opcode.
6712 if (nToksTillIf
> MAXDIST_IF
)
6720 if (!pTok
->IsInForceArray())
6722 FormulaToken
* pLHS
= *(ppTok
- 1);
6723 FormulaToken
* pRHS
= *(ppTok
- 2);
6726 StackVar lhsType
= pLHS
->GetType();
6727 StackVar rhsType
= pRHS
->GetType();
6728 if (lhsType
== svDoubleRef
&& rhsType
== svDoubleRef
)
6730 pLHS
->GetDoubleRef()->SetTrimToData(true);
6731 pRHS
->GetDoubleRef()->SetTrimToData(true);
6745 case ocGreaterEqual
:
6751 // tdf#160616: Double refs with these operators only
6752 // trimmable in case of one parameter
6753 if (!pTok
->IsInForceArray() || nRootParam
> 1)
6755 FormulaToken
* pLHS
= *(ppTok
- 1);
6756 FormulaToken
* pRHS
= *(ppTok
- 2);
6759 StackVar lhsType
= pLHS
->GetType();
6760 StackVar rhsType
= pRHS
->GetType();
6761 if (lhsType
== svDoubleRef
&& (rhsType
== svSingleRef
|| rhsType
== svDoubleRef
))
6763 pLHS
->GetDoubleRef()->SetTrimToData(true);
6765 if (rhsType
== svDoubleRef
&& (lhsType
== svSingleRef
|| lhsType
== svDoubleRef
))
6767 pRHS
->GetDoubleRef()->SetTrimToData(true);
6778 bCloseTillIf
= true;
6788 if (!pTok
->IsInForceArray())
6791 const short nJumpCount
= pTok
->GetJump()[0];
6792 if (nJumpCount
!= 2) // Should have THEN but no ELSE.
6795 OpCode eCompOp
= (*(ppTok
- 1))->GetOpCode();
6796 if (eCompOp
!= ocEqual
)
6799 FormulaToken
* pLHS
= *(ppTok
- 2);
6800 FormulaToken
* pRHS
= *(ppTok
- 3);
6801 StackVar lhsType
= pLHS
->GetType();
6802 StackVar rhsType
= pRHS
->GetType();
6803 if (lhsType
== svDoubleRef
&& (rhsType
== svSingleRef
|| rhsType
== svDouble
))
6805 pLHS
->GetDoubleRef()->SetTrimToData(true);
6807 if ((lhsType
== svSingleRef
|| lhsType
== svDouble
) && rhsType
== svDoubleRef
)
6809 pRHS
->GetDoubleRef()->SetTrimToData(true);
6822 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */