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::DeInit()
192 if (pCharClassEnglish
)
194 delete pCharClassEnglish
;
195 pCharClassEnglish
= nullptr;
197 if (pCharClassLocalized
)
199 delete pCharClassLocalized
;
200 pCharClassLocalized
= nullptr;
204 bool ScCompiler::IsEnglishSymbol( const OUString
& rName
)
206 // function names are always case-insensitive
207 OUString aUpper
= GetCharClassEnglish()->uppercase(rName
);
209 // 1. built-in function name
210 formula::FormulaCompiler aCompiler
;
211 OpCode eOp
= aCompiler
.GetEnglishOpCode( aUpper
);
216 // 2. old add in functions
217 if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper
))
222 // 3. new (uno) add in functions
223 OUString aIntName
= ScGlobal::GetAddInCollection()->FindFunction(aUpper
, false);
224 return !aIntName
.isEmpty(); // no valid function name
227 static std::mutex
& getCharClassMutex()
229 static std::mutex aMutex
;
233 const CharClass
* ScCompiler::GetCharClassEnglish()
235 if (!pCharClassEnglish
)
237 std::scoped_lock
aGuard(getCharClassMutex());
238 if (!pCharClassEnglish
)
240 pCharClassEnglish
= new CharClass( ::comphelper::getProcessComponentContext(),
241 LanguageTag( LANGUAGE_ENGLISH_US
));
244 return pCharClassEnglish
;
247 const CharClass
* ScCompiler::GetCharClassLocalized()
249 if (!pCharClassLocalized
)
251 // Switching UI language requires restart; if not, we would have to
252 // keep track of that.
253 std::scoped_lock
aGuard(getCharClassMutex());
254 if (!pCharClassLocalized
)
256 pCharClassLocalized
= new CharClass( ::comphelper::getProcessComponentContext(),
257 Application::GetSettings().GetUILanguageTag());
260 return pCharClassLocalized
;
263 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar
)
265 assert( eGrammar
!= FormulaGrammar::GRAM_UNSPECIFIED
&& "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
266 if (eGrammar
== GetGrammar())
267 return; // nothing to be done
269 if( eGrammar
== FormulaGrammar::GRAM_EXTERNAL
)
271 meGrammar
= eGrammar
;
272 mxSymbols
= GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE
);
276 FormulaGrammar::Grammar eMyGrammar
= eGrammar
;
277 const sal_Int32 nFormulaLanguage
= FormulaGrammar::extractFormulaLanguage( eMyGrammar
);
278 OpCodeMapPtr xMap
= GetFinalOpCodeMap( nFormulaLanguage
);
279 OSL_ENSURE( xMap
, "ScCompiler::SetGrammar: unknown formula language");
282 xMap
= GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE
);
283 eMyGrammar
= xMap
->getGrammar();
286 // Save old grammar for call to SetGrammarAndRefConvention().
287 FormulaGrammar::Grammar eOldGrammar
= GetGrammar();
288 // This also sets the grammar associated with the map!
289 SetFormulaLanguage( xMap
);
291 // Override if necessary.
292 if (eMyGrammar
!= GetGrammar())
293 SetGrammarAndRefConvention( eMyGrammar
, eOldGrammar
);
297 // Unclear how this was intended to be refreshed when the
298 // grammar or sheet count is changed ? Ideally this would be
299 // a method on Document that would globally cache these.
300 std::vector
<OUString
> &ScCompiler::GetSetupTabNames() const
302 std::vector
<OUString
> &rTabNames
= const_cast<ScCompiler
*>(this)->maTabNames
;
304 if (rTabNames
.empty())
306 rTabNames
= rDoc
.GetAllTableNames();
307 for (auto& rTabName
: rTabNames
)
308 ScCompiler::CheckTabQuotes(rTabName
, formula::FormulaGrammar::extractRefConvention(meGrammar
));
314 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr
& xMap
)
320 if (mxSymbols
->isEnglish())
321 pCharClass
= GetCharClassEnglish();
323 pCharClass
= GetCharClassLocalized();
325 // The difference is needed for an uppercase() call that usually does not
326 // result in different strings but for a few languages like Turkish;
327 // though even de-DE and de-CH may differ in ß/SS handling..
328 // At least don't care if both are English.
329 // The current locale is more likely to not be "en" so check first.
330 const LanguageTag
& rLT1
= ScGlobal::getCharClass().getLanguageTag();
331 const LanguageTag
& rLT2
= pCharClass
->getLanguageTag();
332 mbCharClassesDiffer
= (rLT1
!= rLT2
&& (rLT1
.getLanguage() != "en" || rLT2
.getLanguage() != "en"));
334 SetGrammarAndRefConvention( mxSymbols
->getGrammar(), GetGrammar());
337 void ScCompiler::SetGrammarAndRefConvention(
338 const FormulaGrammar::Grammar eNewGrammar
, const FormulaGrammar::Grammar eOldGrammar
)
340 meGrammar
= eNewGrammar
; // SetRefConvention needs the new grammar set!
341 FormulaGrammar::AddressConvention eConv
= FormulaGrammar::extractRefConvention( meGrammar
);
342 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
&& eOldGrammar
== FormulaGrammar::GRAM_UNSPECIFIED
)
343 SetRefConvention( rDoc
.GetAddressConvention());
345 SetRefConvention( eConv
);
348 OUString
ScCompiler::FindAddInFunction( const OUString
& rUpperName
, bool bLocalFirst
) const
350 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName
, bLocalFirst
); // bLocalFirst=false for english
353 ScCompiler::Convention::~Convention()
357 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv
)
362 ScCharFlags
*t
= new ScCharFlags
[128];
364 ScCompiler::pConventions
[ meConv
] = this;
365 mpCharTable
.reset( t
);
367 for (i
= 0; i
< 128; i
++)
368 t
[i
] = ScCharFlags::Illegal
;
370 // Allow tabs/newlines.
371 // Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
372 /* tab */ t
[ 9] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
373 /* lf */ t
[10] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
374 /* cr */ t
[13] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
376 /* */ t
[32] = ScCharFlags::CharDontCare
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
377 /* ! */ t
[33] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
378 if (FormulaGrammar::CONV_ODF
== meConv
)
379 /* ! */ t
[33] |= ScCharFlags::OdfLabelOp
;
380 /* " */ t
[34] = ScCharFlags::CharString
| ScCharFlags::StringSep
;
381 /* # */ t
[35] = ScCharFlags::WordSep
| ScCharFlags::CharErrConst
;
382 /* $ */ t
[36] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
;
383 if (FormulaGrammar::CONV_ODF
== meConv
)
384 /* $ */ t
[36] |= ScCharFlags::OdfNameMarker
;
385 /* % */ t
[37] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
386 /* & */ t
[38] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
387 /* ' */ t
[39] = ScCharFlags::NameSep
;
388 /* ( */ t
[40] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
389 /* ) */ t
[41] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
390 /* * */ t
[42] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
391 /* + */ t
[43] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueExp
| ScCharFlags::ValueSign
;
392 /* , */ t
[44] = ScCharFlags::CharValue
| ScCharFlags::Value
;
393 /* - */ t
[45] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueExp
| ScCharFlags::ValueSign
;
394 /* . */ t
[46] = ScCharFlags::Word
| ScCharFlags::CharValue
| ScCharFlags::Value
| ScCharFlags::Ident
| ScCharFlags::Name
;
395 /* / */ t
[47] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
397 for (i
= 48; i
< 58; i
++)
398 /* 0-9 */ t
[i
] = ScCharFlags::CharValue
| ScCharFlags::Word
| ScCharFlags::Value
| ScCharFlags::ValueExp
| ScCharFlags::ValueValue
| ScCharFlags::Ident
| ScCharFlags::Name
;
400 /* : */ t
[58] = ScCharFlags::Char
| ScCharFlags::Word
;
401 /* ; */ t
[59] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
402 /* < */ t
[60] = ScCharFlags::CharBool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
403 /* = */ t
[61] = ScCharFlags::Char
| ScCharFlags::Bool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
404 /* > */ t
[62] = ScCharFlags::CharBool
| ScCharFlags::Bool
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
405 /* ? */ t
[63] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::Name
;
408 for (i
= 65; i
< 91; i
++)
409 /* A-Z */ t
[i
] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
411 if (FormulaGrammar::CONV_ODF
== meConv
)
413 /* [ */ t
[91] = ScCharFlags::OdfLBracket
;
415 /* ] */ t
[93] = ScCharFlags::OdfRBracket
;
417 else if (FormulaGrammar::CONV_OOO
== meConv
)
419 /* [ */ t
[91] = ScCharFlags::Char
;
421 /* ] */ t
[93] = ScCharFlags::Char
;
423 else if (FormulaGrammar::CONV_XL_OOX
== meConv
)
425 /* [ */ t
[91] = ScCharFlags::Char
| ScCharFlags::CharIdent
;
427 /* ] */ t
[93] = ScCharFlags::Char
| ScCharFlags::Ident
;
429 else if (FormulaGrammar::CONV_XL_A1
== meConv
)
431 /* [ */ t
[91] = ScCharFlags::Char
;
433 /* ] */ t
[93] = ScCharFlags::Char
;
435 else if( FormulaGrammar::CONV_XL_R1C1
== meConv
)
437 /* [ */ t
[91] = ScCharFlags::Ident
;
439 /* ] */ t
[93] = ScCharFlags::Ident
;
448 /* ^ */ t
[94] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
449 /* _ */ t
[95] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
452 for (i
= 97; i
< 123; i
++)
453 /* a-z */ t
[i
] = ScCharFlags::CharWord
| ScCharFlags::Word
| ScCharFlags::CharIdent
| ScCharFlags::Ident
| ScCharFlags::CharName
| ScCharFlags::Name
;
455 /* { */ t
[123] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array open
456 /* | */ t
[124] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array row sep (Should be OOo specific)
457 /* } */ t
[125] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
; // array close
458 /* ~ */ t
[126] = ScCharFlags::Char
; // OOo specific
461 if( !(FormulaGrammar::CONV_XL_A1
== meConv
|| FormulaGrammar::CONV_XL_R1C1
== meConv
|| FormulaGrammar::CONV_XL_OOX
== meConv
) )
464 /* */ t
[32] |= ScCharFlags::Word
;
465 /* ! */ t
[33] |= ScCharFlags::Ident
| ScCharFlags::Word
;
466 /* " */ t
[34] |= ScCharFlags::Word
;
467 /* # */ t
[35] &= ~ScCharFlags::WordSep
;
468 /* # */ t
[35] |= ScCharFlags::Word
;
469 /* % */ t
[37] |= ScCharFlags::Word
;
470 /* & */ t
[38] |= ScCharFlags::Word
;
471 /* ' */ t
[39] |= ScCharFlags::Word
;
472 /* ( */ t
[40] |= ScCharFlags::Word
;
473 /* ) */ t
[41] |= ScCharFlags::Word
;
474 /* * */ t
[42] |= ScCharFlags::Word
;
475 /* + */ t
[43] |= ScCharFlags::Word
;
476 #if 0 /* this really needs to be locale specific. */
477 /* , */ t
[44] = ScCharFlags::Char
| ScCharFlags::WordSep
| ScCharFlags::ValueSep
;
479 /* , */ t
[44] |= ScCharFlags::Word
;
481 /* - */ t
[45] |= ScCharFlags::Word
;
483 /* ; */ t
[59] |= ScCharFlags::Word
;
484 /* < */ t
[60] |= ScCharFlags::Word
;
485 /* = */ t
[61] |= ScCharFlags::Word
;
486 /* > */ t
[62] |= ScCharFlags::Word
;
487 /* ? */ // question really is not permitted in sheet name
488 /* @ */ t
[64] |= ScCharFlags::Word
;
489 /* [ */ t
[91] |= ScCharFlags::Word
;
490 /* ] */ t
[93] |= ScCharFlags::Word
;
491 /* { */ t
[123]|= ScCharFlags::Word
;
492 /* | */ t
[124]|= ScCharFlags::Word
;
493 /* } */ t
[125]|= ScCharFlags::Word
;
494 /* ~ */ t
[126]|= ScCharFlags::Word
;
497 static bool lcl_isValidQuotedText( std::u16string_view rFormula
, size_t nSrcPos
, ParseResult
& rRes
)
499 // Tokens that start at ' can have anything in them until a final '
500 // but '' marks an escaped '
501 // We've earlier guaranteed that a string containing '' will be
503 if (nSrcPos
< rFormula
.size() && rFormula
[nSrcPos
] == '\'')
505 size_t nPos
= nSrcPos
+1;
506 while (nPos
< rFormula
.size())
508 if (rFormula
[nPos
] == '\'')
510 if ( (nPos
+1 == rFormula
.size()) || (rFormula
[nPos
+1] != '\'') )
512 rRes
.TokenType
= KParseType::SINGLE_QUOTE_NAME
;
513 rRes
.EndPos
= nPos
+1;
525 static bool lcl_parseExternalName(
526 const OUString
& rSymbol
,
529 const sal_Unicode cSep
,
530 const ScDocument
& rDoc
,
531 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
533 /* TODO: future versions will have to support sheet-local names too, thus
534 * return a possible sheet name as well. */
535 const sal_Unicode
* const pStart
= rSymbol
.getStr();
536 const sal_Unicode
* p
= pStart
;
537 sal_Int32 nLen
= rSymbol
.getLength();
539 OUStringBuffer aTmpName
;
541 bool bInName
= false;
544 // For XL use existing parser that resolves bracketed and quoted and
545 // indexed external document names.
547 OUString aStartTabName
, aEndTabName
;
548 ScRefFlags nFlags
= ScRefFlags::ZERO
;
549 p
= aRange
.Parse_XL_Header( p
, rDoc
, aTmpFile
, aStartTabName
,
550 aEndTabName
, nFlags
, true, pExternalLinks
);
551 if (!p
|| p
== pStart
)
553 i
= sal_Int32(p
- pStart
);
555 for ( ; i
< nLen
; ++i
, ++p
)
560 if (c
== '.' || c
== cSep
)
565 // Move to the next char and loop until the second single
567 sal_Unicode cPrev
= c
;
569 for (sal_Int32 j
= i
; j
< nLen
; ++j
, ++p
)
576 // empty quote e.g. (=''!Name)
582 // two consecutive quotes equal a single quote in
584 aTmpFile
+= OUStringChar(c
);
593 if (cPrev
== '\'' && j
!= i
)
595 // this is not a quote but the previous one is. This
596 // ends the parsing of the quoted segment. At this
597 // point, the current char must equal the separator
602 aTmpName
.append(c
); // Keep the separator as part of the name.
605 aTmpFile
+= OUStringChar(c
);
611 // premature ending of the quoted segment.
617 // only the separator is allowed after the closing quote.
629 // A second separator ? Not a valid external name.
639 aTmpName
.append(c
); // Keep the separator as part of the name.
645 if (rtl::isAsciiAlphanumeric(c
))
650 // non-ASCII character is allowed.
659 // these special characters are allowed.
669 aTmpFile
+= OUStringChar(c
);
676 // No name found - most likely the symbol has no '!'s.
680 sal_Int32 nNameLen
= aTmpName
.getLength();
683 // Name must be at least 2-char long (separator plus name).
687 if (aTmpName
[0] != cSep
)
689 // 1st char of the name must equal the separator.
693 if (aTmpName
[nNameLen
-1] == '!')
695 // Check against #REF!.
696 if (OUString::unacquired(aTmpName
).equalsIgnoreAsciiCase("#REF!"))
701 rName
= aTmpName
.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
705 static OUString
lcl_makeExternalNameStr(const OUString
& rFile
, const OUString
& rName
,
706 const sal_Unicode cSep
, bool bODF
)
708 OUString
aEscQuote(u
"''"_ustr
);
709 OUString
aFile(rFile
.replaceAll("'", aEscQuote
));
710 OUString
aName(rName
);
712 aName
= aName
.replaceAll("'", aEscQuote
);
713 OUStringBuffer
aBuf(aFile
.getLength() + aName
.getLength() + 9);
716 aBuf
.append( "'" + aFile
+ "'" + OUStringChar(cSep
) );
718 aBuf
.append( "$$'" );
722 return aBuf
.makeStringAndClear();
725 static bool lcl_getLastTabName( OUString
& rTabName2
, const OUString
& rTabName1
,
726 const vector
<OUString
>& rTabNames
, const ScRange
& rRef
)
728 SCTAB nTabSpan
= rRef
.aEnd
.Tab() - rRef
.aStart
.Tab();
731 size_t nCount
= rTabNames
.size();
732 vector
<OUString
>::const_iterator itrBeg
= rTabNames
.begin(), itrEnd
= rTabNames
.end();
733 vector
<OUString
>::const_iterator itr
= ::std::find(itrBeg
, itrEnd
, rTabName1
);
734 if (itr
== rTabNames
.end())
736 rTabName2
= ScResId(STR_NO_REF_TABLE
);
740 size_t nDist
= ::std::distance(itrBeg
, itr
);
741 if (nDist
+ static_cast<size_t>(nTabSpan
) >= nCount
)
743 rTabName2
= ScResId(STR_NO_REF_TABLE
);
747 rTabName2
= rTabNames
[nDist
+nTabSpan
];
750 rTabName2
= rTabName1
;
757 struct Convention_A1
: public ScCompiler::Convention
759 explicit Convention_A1( FormulaGrammar::AddressConvention eConv
) : ScCompiler::Convention( eConv
) { }
760 static void MakeColStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCCOL nCol
);
761 static void MakeRowStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCROW nRow
);
763 ParseResult
parseAnyToken( const OUString
& rFormula
,
765 const CharClass
* pCharClass
,
766 bool bGroupSeparator
) const override
769 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
772 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
773 KParseTokens::ASC_UNDERSCORE
| KParseTokens::ASC_DOLLAR
;
774 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
775 // '?' allowed in range names because of Xcl :-/
776 static constexpr OUString
aAddAllowed(u
"?#"_ustr
);
777 return pCharClass
->parseAnyToken( rFormula
,
778 nSrcPos
, nStartFlags
, aAddAllowed
,
779 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
783 virtual ScCharFlags
getCharTableFlags( sal_Unicode c
, sal_Unicode
/*cLast*/ ) const override
785 return mpCharTable
[static_cast<sal_uInt8
>(c
)];
791 void Convention_A1::MakeColStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCCOL nCol
)
793 if ( !rLimits
.ValidCol(nCol
) )
794 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
796 ::ScColToAlpha( rBuffer
, nCol
);
799 void Convention_A1::MakeRowStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
, SCROW nRow
)
801 if ( !rLimits
.ValidRow(nRow
) )
802 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
804 rBuffer
.append(sal_Int32(nRow
+ 1));
809 struct ConventionOOO_A1
: public Convention_A1
811 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO
) { }
812 explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv
) : Convention_A1 (eConv
) { }
814 static void MakeTabStr( OUStringBuffer
&rBuf
, const std::vector
<OUString
>& rTabNames
, SCTAB nTab
)
816 if (o3tl::make_unsigned(nTab
) >= rTabNames
.size())
817 rBuf
.append(ScResId(STR_NO_REF_TABLE
));
819 rBuf
.append(rTabNames
[nTab
]);
823 enum SingletonDisplay
830 static void MakeOneRefStrImpl(
831 const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuffer
,
832 std::u16string_view rErrRef
, const std::vector
<OUString
>& rTabNames
,
833 const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
,
834 bool bForceTab
, bool bODF
, SingletonDisplay eSingletonDisplay
)
836 if( rRef
.IsFlag3D() || bForceTab
)
838 if (!ValidTab(rAbsRef
.Tab()) || rRef
.IsTabDeleted())
840 if (!rRef
.IsTabRel())
842 rBuffer
.append(rErrRef
);
847 if (!rRef
.IsTabRel())
849 MakeTabStr(rBuffer
, rTabNames
, rAbsRef
.Tab());
855 if (eSingletonDisplay
!= SINGLETON_ROW
)
857 if (!rRef
.IsColRel())
859 if (!rLimits
.ValidCol(rAbsRef
.Col()) || rRef
.IsColDeleted())
860 rBuffer
.append(rErrRef
);
862 MakeColStr(rLimits
, rBuffer
, rAbsRef
.Col());
865 if (eSingletonDisplay
!= SINGLETON_COL
)
867 if (!rRef
.IsRowRel())
869 if (!rLimits
.ValidRow(rAbsRef
.Row()) || rRef
.IsRowDeleted())
870 rBuffer
.append(rErrRef
);
872 MakeRowStr(rLimits
, rBuffer
, rAbsRef
.Row());
876 static SingletonDisplay
getSingletonDisplay( const ScSheetLimits
& rLimits
, const ScAddress
& rAbs1
, const ScAddress
& rAbs2
,
877 const ScComplexRefData
& rRef
, bool bFromRangeName
)
879 // If any part is error, display as such.
880 if (!rLimits
.ValidCol(rAbs1
.Col()) || rRef
.Ref1
.IsColDeleted() || !rLimits
.ValidRow(rAbs1
.Row()) || rRef
.Ref1
.IsRowDeleted() ||
881 !rLimits
.ValidCol(rAbs2
.Col()) || rRef
.Ref2
.IsColDeleted() || !rLimits
.ValidRow(rAbs2
.Row()) || rRef
.Ref2
.IsRowDeleted())
882 return SINGLETON_NONE
;
884 // A:A or $A:$A or A:$A or $A:A
885 if (rRef
.IsEntireCol(rLimits
))
886 return SINGLETON_COL
;
888 // Same if not in named expression and both rows of entire columns are
889 // relative references.
890 if (!bFromRangeName
&& rAbs1
.Row() == 0 && rAbs2
.Row() == rLimits
.mnMaxRow
&&
891 rRef
.Ref1
.IsRowRel() && rRef
.Ref2
.IsRowRel())
892 return SINGLETON_COL
;
894 // 1:1 or $1:$1 or 1:$1 or $1:1
895 if (rRef
.IsEntireRow(rLimits
))
896 return SINGLETON_ROW
;
898 // Same if not in named expression and both columns of entire rows are
899 // relative references.
900 if (!bFromRangeName
&& rAbs1
.Col() == 0 && rAbs2
.Col() == rLimits
.mnMaxCol
&&
901 rRef
.Ref1
.IsColRel() && rRef
.Ref2
.IsColRel())
902 return SINGLETON_ROW
;
904 return SINGLETON_NONE
;
907 virtual void makeRefStr(
908 ScSheetLimits
& rLimits
,
909 OUStringBuffer
& rBuffer
,
910 formula::FormulaGrammar::Grammar
/*eGram*/,
911 const ScAddress
& rPos
,
912 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
913 const ScComplexRefData
& rRef
,
915 bool bFromRangeName
) const override
917 // In case absolute/relative positions weren't separately available:
918 // transform relative to absolute!
919 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
921 aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
923 SingletonDisplay eSingleton
= bSingleRef
? SINGLETON_NONE
:
924 getSingletonDisplay( rLimits
, aAbs1
, aAbs2
, rRef
, bFromRangeName
);
925 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref1
, aAbs1
, false, false, eSingleton
);
929 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), false,
934 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
938 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
940 case ScCompiler::Convention::SHEET_SEPARATOR
:
947 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
948 const ScDocument
& rDoc
,
949 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
951 return lcl_parseExternalName(rSymbol
, rFile
, rName
, '#', rDoc
, pExternalLinks
);
954 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
955 const OUString
& rName
) const override
957 return lcl_makeExternalNameStr( rFile
, rName
, '#', false);
960 static bool makeExternalSingleRefStr(
961 const ScSheetLimits
& rLimits
,
962 OUStringBuffer
& rBuffer
, const OUString
& rFileName
, const OUString
& rTabName
,
963 const ScSingleRefData
& rRef
, const ScAddress
& rPos
, bool bDisplayTabName
, bool bEncodeUrl
)
965 ScAddress aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
972 aFile
= INetURLObject::decode(rFileName
, INetURLObject::DecodeMechanism::Unambiguous
);
974 rBuffer
.append("'" + aFile
.replaceAll("'", "''") + "'#");
976 if (!rRef
.IsTabRel())
978 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
983 if (!rRef
.IsColRel())
985 MakeColStr( rLimits
, rBuffer
, aAbsRef
.Col());
986 if (!rRef
.IsRowRel())
988 MakeRowStr( rLimits
, rBuffer
, aAbsRef
.Row());
993 static void makeExternalRefStrImpl(
994 const ScSheetLimits
& rLimits
,
995 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, const OUString
& rFileName
,
996 const OUString
& rTabName
, const ScSingleRefData
& rRef
, bool bODF
)
999 rBuffer
.append( '[');
1001 bool bEncodeUrl
= bODF
;
1002 makeExternalSingleRefStr(rLimits
, rBuffer
, rFileName
, rTabName
, rRef
, rPos
, true, bEncodeUrl
);
1004 rBuffer
.append( ']');
1007 virtual void makeExternalRefStr(
1008 ScSheetLimits
& rLimits
,
1009 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1010 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1012 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabName
, rRef
, false);
1015 static void makeExternalRefStrImpl(
1016 const ScSheetLimits
& rLimits
,
1017 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, const OUString
& rFileName
,
1018 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1019 const ScComplexRefData
& rRef
, bool bODF
)
1021 ScRange aAbsRange
= rRef
.toAbs(rLimits
, rPos
);
1024 rBuffer
.append( '[');
1025 // Ensure that there's always a closing bracket, no premature returns.
1026 bool bEncodeUrl
= bODF
;
1030 if (!makeExternalSingleRefStr(rLimits
, rBuffer
, rFileName
, rTabName
, rRef
.Ref1
, rPos
, true, bEncodeUrl
))
1033 rBuffer
.append(':');
1035 OUString aLastTabName
;
1036 bool bDisplayTabName
= (aAbsRange
.aStart
.Tab() != aAbsRange
.aEnd
.Tab());
1037 if (bDisplayTabName
)
1039 // Get the name of the last table.
1040 if (!lcl_getLastTabName(aLastTabName
, rTabName
, rTabNames
, aAbsRange
))
1042 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1043 // aLastTabName contains #REF!, proceed.
1047 rBuffer
.append( '.'); // need at least the sheet separator in ODF
1048 makeExternalSingleRefStr(rLimits
,
1049 rBuffer
, rFileName
, aLastTabName
, rRef
.Ref2
, rPos
, bDisplayTabName
, bEncodeUrl
);
1053 rBuffer
.append( ']');
1056 virtual void makeExternalRefStr(
1057 ScSheetLimits
& rLimits
,
1058 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1059 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1060 const ScComplexRefData
& rRef
) const override
1062 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabNames
, rTabName
, rRef
, false);
1066 struct ConventionOOO_A1_ODF
: public ConventionOOO_A1
1068 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF
) { }
1070 virtual void makeRefStr(
1071 ScSheetLimits
& rLimits
,
1072 OUStringBuffer
& rBuffer
,
1073 formula::FormulaGrammar::Grammar eGram
,
1074 const ScAddress
& rPos
,
1075 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1076 const ScComplexRefData
& rRef
,
1078 bool bFromRangeName
) const override
1080 rBuffer
.append('[');
1081 // In case absolute/relative positions weren't separately available:
1082 // transform relative to absolute!
1083 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
1085 aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
1087 if (FormulaGrammar::isODFF(eGram
) && (rRef
.Ref1
.IsDeleted() || !rLimits
.ValidAddress(aAbs1
) ||
1088 (!bSingleRef
&& (rRef
.Ref2
.IsDeleted() || !rLimits
.ValidAddress(aAbs2
)))))
1090 rBuffer
.append(rErrRef
);
1091 // For ODFF write [#REF!], but not for PODF so apps reading ODF
1092 // 1.0/1.1 may have a better chance if they implemented the old
1097 SingletonDisplay eSingleton
= bSingleRef
? SINGLETON_NONE
:
1098 getSingletonDisplay( rLimits
, aAbs1
, aAbs2
, rRef
, bFromRangeName
);
1099 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref1
, aAbs1
, false, true, eSingleton
);
1102 rBuffer
.append(':');
1103 MakeOneRefStrImpl(rLimits
, rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), true,
1107 rBuffer
.append(']');
1110 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1111 const OUString
& rName
) const override
1113 return lcl_makeExternalNameStr( rFile
, rName
, '#', true);
1116 virtual void makeExternalRefStr(
1117 ScSheetLimits
& rLimits
,
1118 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1119 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1121 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabName
, rRef
, true);
1124 virtual void makeExternalRefStr(
1125 ScSheetLimits
& rLimits
,
1126 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1127 const std::vector
<OUString
>& rTabNames
,
1128 const OUString
& rTabName
, const ScComplexRefData
& rRef
) const override
1130 makeExternalRefStrImpl(rLimits
, rBuffer
, rPos
, rFileName
, rTabNames
, rTabName
, rRef
, true);
1136 virtual ~ConventionXL()
1141 const ScSheetLimits
& rLimits
,
1142 const ScAddress
& rPos
, const std::vector
<OUString
>& rTabNames
,
1143 const ScSingleRefData
& rRef
, OUString
& rTabName
)
1145 ScAddress aAbs
= rRef
.toAbs(rLimits
, rPos
);
1146 if (rRef
.IsTabDeleted() || o3tl::make_unsigned(aAbs
.Tab()) >= rTabNames
.size())
1148 rTabName
= ScResId( STR_NO_REF_TABLE
);
1151 rTabName
= rTabNames
[aAbs
.Tab()];
1154 static void MakeTabStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuf
,
1155 const ScAddress
& rPos
,
1156 const std::vector
<OUString
>& rTabNames
,
1157 const ScComplexRefData
& rRef
,
1160 if( !rRef
.Ref1
.IsFlag3D() )
1163 OUString aStartTabName
, aEndTabName
;
1165 GetTab(rLimits
, rPos
, rTabNames
, rRef
.Ref1
, aStartTabName
);
1167 if( !bSingleRef
&& rRef
.Ref2
.IsFlag3D() )
1169 GetTab(rLimits
, rPos
, rTabNames
, rRef
.Ref2
, aEndTabName
);
1172 rBuf
.append( aStartTabName
);
1173 if( !bSingleRef
&& rRef
.Ref2
.IsFlag3D() && aStartTabName
!= aEndTabName
)
1176 rBuf
.append( aEndTabName
);
1182 static sal_Unicode
getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType
)
1186 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
1188 case ScCompiler::Convention::SHEET_SEPARATOR
:
1194 static bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1195 const ScDocument
& rDoc
,
1196 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
1198 return lcl_parseExternalName( rSymbol
, rFile
, rName
, '!', rDoc
, pExternalLinks
);
1201 static OUString
makeExternalNameStr( const OUString
& rFile
, const OUString
& rName
)
1203 return lcl_makeExternalNameStr( rFile
, rName
, '!', false);
1206 static void makeExternalDocStr( OUStringBuffer
& rBuffer
, std::u16string_view rFullName
)
1208 // Format that is easier to deal with inside OOo, because we use file
1209 // URL, and all characters are allowed. Check if it makes sense to do
1210 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1211 // and allows relative file path.
1213 // ['file:///path/to/source/filename.xls']
1215 rBuffer
.append('[');
1216 rBuffer
.append('\'');
1217 OUString aFullName
= INetURLObject::decode(rFullName
, INetURLObject::DecodeMechanism::Unambiguous
);
1219 const sal_Unicode
* pBuf
= aFullName
.getStr();
1220 sal_Int32 nLen
= aFullName
.getLength();
1221 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
1223 const sal_Unicode c
= pBuf
[i
];
1228 rBuffer
.append('\'');
1229 rBuffer
.append(']');
1232 static void makeExternalTabNameRange( OUStringBuffer
& rBuf
, const OUString
& rTabName
,
1233 const vector
<OUString
>& rTabNames
,
1234 const ScRange
& rRef
)
1236 OUString aLastTabName
;
1237 if (!lcl_getLastTabName(aLastTabName
, rTabName
, rTabNames
, rRef
))
1239 ScRangeStringConverter::AppendTableName(rBuf
, aLastTabName
);
1243 ScRangeStringConverter::AppendTableName(rBuf
, rTabName
);
1244 if (rTabName
!= aLastTabName
)
1247 ScRangeStringConverter::AppendTableName(rBuf
, aLastTabName
);
1251 virtual void parseExternalDocName( const OUString
& rFormula
, sal_Int32
& rSrcPos
) const
1253 sal_Int32 nLen
= rFormula
.getLength();
1254 const sal_Unicode
* p
= rFormula
.getStr();
1255 sal_Unicode cPrev
= 0;
1256 for (sal_Int32 i
= rSrcPos
; i
< nLen
; ++i
)
1258 sal_Unicode c
= p
[i
];
1261 // first character must be '['.
1265 else if (i
== rSrcPos
+ 1)
1267 // second character must be a single quote.
1274 // two successive single quote is treated as a single
1282 // valid source document path found. Increment the
1283 // current position to skip the source path.
1285 if (rSrcPos
>= nLen
)
1294 // any other character
1295 if (i
> rSrcPos
+ 2 && cPrev
== '\'')
1296 // unless it's the 3rd character, a normal character
1297 // following immediately a single quote is invalid.
1305 struct ConventionXL_A1
: public Convention_A1
, public ConventionXL
1307 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1
) { }
1308 explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv
) : Convention_A1( eConv
) { }
1310 static void makeSingleCellStr( const ScSheetLimits
& rLimits
, OUStringBuffer
& rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbs
)
1312 if (!rRef
.IsColRel())
1314 MakeColStr(rLimits
, rBuf
, rAbs
.Col());
1315 if (!rRef
.IsRowRel())
1317 MakeRowStr(rLimits
, rBuf
, rAbs
.Row());
1320 virtual void makeRefStr(
1321 ScSheetLimits
& rLimits
,
1322 OUStringBuffer
& rBuf
,
1323 formula::FormulaGrammar::Grammar
/*eGram*/,
1324 const ScAddress
& rPos
,
1325 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1326 const ScComplexRefData
& rRef
,
1328 bool /*bFromRangeName*/ ) const override
1330 ScComplexRefData
aRef( rRef
);
1332 // Play fast and loose with invalid refs. There is not much point in producing
1333 // Foo!A1:#REF! versus #REF! at this point
1334 ScAddress aAbs1
= aRef
.Ref1
.toAbs(rLimits
, rPos
), aAbs2
;
1336 MakeTabStr(rLimits
, rBuf
, rPos
, rTabNames
, aRef
, bSingleRef
);
1338 if (!rLimits
.ValidAddress(aAbs1
))
1340 rBuf
.append(rErrRef
);
1346 aAbs2
= aRef
.Ref2
.toAbs(rLimits
, rPos
);
1347 if (!rLimits
.ValidAddress(aAbs2
))
1349 rBuf
.append(rErrRef
);
1353 if (aAbs1
.Col() == 0 && aAbs2
.Col() >= rLimits
.mnMaxCol
)
1355 if (!aRef
.Ref1
.IsRowRel())
1357 MakeRowStr(rLimits
, rBuf
, aAbs1
.Row());
1359 if (!aRef
.Ref2
.IsRowRel())
1361 MakeRowStr(rLimits
, rBuf
, aAbs2
.Row());
1365 if (aAbs1
.Row() == 0 && aAbs2
.Row() >= rLimits
.mnMaxRow
)
1367 if (!aRef
.Ref1
.IsColRel())
1369 MakeColStr(rLimits
, rBuf
, aAbs1
.Col());
1371 if (!aRef
.Ref2
.IsColRel())
1373 MakeColStr(rLimits
, rBuf
, aAbs2
.Col());
1378 makeSingleCellStr(rLimits
, rBuf
, aRef
.Ref1
, aAbs1
);
1382 makeSingleCellStr(rLimits
, rBuf
, aRef
.Ref2
, aAbs2
);
1386 virtual ParseResult
parseAnyToken( const OUString
& rFormula
,
1388 const CharClass
* pCharClass
,
1389 bool bGroupSeparator
) const override
1391 parseExternalDocName(rFormula
, nSrcPos
);
1394 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
1397 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
1398 KParseTokens::ASC_UNDERSCORE
| KParseTokens::ASC_DOLLAR
;
1399 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
1400 // '?' allowed in range names
1401 static constexpr OUString
aAddAllowed(u
"?!"_ustr
);
1402 return pCharClass
->parseAnyToken( rFormula
,
1403 nSrcPos
, nStartFlags
, aAddAllowed
,
1404 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
1408 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
1410 return ConventionXL::getSpecialSymbol(eSymType
);
1413 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1414 const ScDocument
& rDoc
,
1415 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
1417 return ConventionXL::parseExternalName( rSymbol
, rFile
, rName
, rDoc
, pExternalLinks
);
1420 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1421 const OUString
& rName
) const override
1423 return ConventionXL::makeExternalNameStr(rFile
, rName
);
1426 virtual void makeExternalRefStr(
1427 ScSheetLimits
& rLimits
,
1428 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1429 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1431 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1432 // This is a little different from the format Excel uses, as Excel
1433 // puts [] only around the file name. But we need to enclose the
1434 // whole file path with [] because the file name can contain any
1437 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1438 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
1439 rBuffer
.append('!');
1441 makeSingleCellStr(rLimits
, rBuffer
, rRef
, rRef
.toAbs(rLimits
, rPos
));
1444 virtual void makeExternalRefStr(
1445 ScSheetLimits
& rLimits
,
1446 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1447 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1448 const ScComplexRefData
& rRef
) const override
1450 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1452 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1453 ConventionXL::makeExternalTabNameRange(rBuffer
, rTabName
, rTabNames
, aAbsRef
);
1454 rBuffer
.append('!');
1456 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1457 if (aAbsRef
.aStart
!= aAbsRef
.aEnd
)
1459 rBuffer
.append(':');
1460 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1465 struct ConventionXL_OOX
: public ConventionXL_A1
1467 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX
) { }
1469 virtual void makeRefStr( ScSheetLimits
& rLimits
,
1470 OUStringBuffer
& rBuf
,
1471 formula::FormulaGrammar::Grammar eGram
,
1472 const ScAddress
& rPos
,
1473 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1474 const ScComplexRefData
& rRef
,
1476 bool bFromRangeName
) const override
1478 // In OOXML relative references in named expressions are relative to
1479 // column 0 and row 0. Relative sheet references don't exist.
1480 ScAddress
aPos( rPos
);
1483 // XXX NOTE: by decrementing the reference position we may end up
1484 // with resolved references with negative values. There's no proper
1485 // way to solve that or wrap them around without sheet dimensions
1486 // that are stored along. That, or blindly assume fixed dimensions
1487 // here and in import.
1488 /* TODO: maybe do that blind fixed dimensions wrap? */
1493 if (rRef
.Ref1
.IsDeleted() || (!bSingleRef
&& rRef
.Ref2
.IsDeleted()))
1495 // For OOXML write plain "#REF!" instead of detailed sheet/col/row
1497 rBuf
.append(rErrRef
);
1502 ScAddress aAbs1
= rRef
.Ref1
.toAbs(rLimits
, rPos
);
1503 if (!rLimits
.ValidAddress(aAbs1
)
1504 || o3tl::make_unsigned(aAbs1
.Tab()) >= rTabNames
.size())
1506 rBuf
.append(rErrRef
);
1513 ScAddress aAbs2
= rRef
.Ref2
.toAbs(rLimits
, rPos
);
1514 if (!rLimits
.ValidAddress(aAbs2
)
1515 || o3tl::make_unsigned(aAbs2
.Tab()) >= rTabNames
.size())
1517 rBuf
.append(rErrRef
);
1522 ConventionXL_A1::makeRefStr( rLimits
, rBuf
, eGram
, aPos
, rErrRef
, rTabNames
, rRef
, bSingleRef
, bFromRangeName
);
1525 virtual OUString
makeExternalNameStr( sal_uInt16 nFileId
, const OUString
& /*rFile*/,
1526 const OUString
& rName
) const override
1528 // [N]!DefinedName is a workbook global name.
1529 return OUString( "[" + OUString::number(nFileId
+1) + "]!" + rName
);
1531 /* TODO: add support for sheet local names, would be
1532 * [N]'Sheet Name'!DefinedName
1533 * Similar to makeExternalRefStr() but with DefinedName instead of
1537 virtual void parseExternalDocName(const OUString
& rFormula
, sal_Int32
& rSrcPos
) const override
1539 sal_Int32 nLen
= rFormula
.getLength();
1540 const sal_Unicode
* p
= rFormula
.getStr();
1541 for (sal_Int32 i
= rSrcPos
; i
< nLen
; ++i
)
1543 sal_Unicode c
= p
[i
];
1546 // first character must be '['.
1558 virtual void makeExternalRefStr(
1559 ScSheetLimits
& rLimits
,
1560 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16 nFileId
, const OUString
& /*rFileName*/,
1561 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1563 // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
1564 // Where N is a 1-based positive integer number of a file name in OOXML
1565 // xl/externalLinks/externalLinkN.xml
1567 OUString
aQuotedTab( rTabName
);
1568 ScCompiler::CheckTabQuotes( aQuotedTab
);
1569 if (!aQuotedTab
.isEmpty() && aQuotedTab
[0] == '\'')
1571 rBuffer
.append('\'');
1572 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1573 rBuffer
.append( aQuotedTab
.subView(1));
1577 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1578 rBuffer
.append( aQuotedTab
);
1580 rBuffer
.append('!');
1582 makeSingleCellStr(rLimits
, rBuffer
, rRef
, rRef
.toAbs(rLimits
, rPos
));
1585 virtual void makeExternalRefStr(
1586 ScSheetLimits
& rLimits
,
1587 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16 nFileId
, const OUString
& /*rFileName*/,
1588 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1589 const ScComplexRefData
& rRef
) const override
1591 // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
1592 // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
1593 // simpler to produce and more logical form with independently quoted
1594 // sheet names as well. The [N] having to be within the quoted sheet
1595 // name is ugly enough...
1597 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1599 OUStringBuffer aBuf
;
1600 ConventionXL::makeExternalTabNameRange( aBuf
, rTabName
, rTabNames
, aAbsRef
);
1601 if (!aBuf
.isEmpty() && aBuf
[0] == '\'')
1603 rBuffer
.append('\'');
1604 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1605 rBuffer
.append( aBuf
.subView(1));
1609 ConventionXL_OOX::makeExternalDocStr( rBuffer
, nFileId
);
1610 rBuffer
.append( aBuf
);
1612 rBuffer
.append('!');
1614 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1615 if (aAbsRef
.aStart
!= aAbsRef
.aEnd
)
1617 rBuffer
.append(':');
1618 makeSingleCellStr(rLimits
, rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1622 static void makeExternalDocStr( OUStringBuffer
& rBuffer
, sal_uInt16 nFileId
)
1624 rBuffer
.append("[" + OUString::number( static_cast<sal_Int32
>(nFileId
+1) ) + "]");
1631 r1c1_add_col( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1634 if( rRef
.IsColRel() )
1636 SCCOL nCol
= rRef
.Col();
1638 rBuf
.append("[" + OUString::number(nCol
) + "]");
1641 rBuf
.append( static_cast<sal_Int32
>(rAbsRef
.Col() + 1) );
1644 r1c1_add_row( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1647 if( rRef
.IsRowRel() )
1649 if (rRef
.Row() != 0)
1651 rBuf
.append("[" + OUString::number(rRef
.Row()) + "]");
1655 rBuf
.append( rAbsRef
.Row() + 1 );
1660 struct ConventionXL_R1C1
: public ScCompiler::Convention
, public ConventionXL
1662 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1
) { }
1664 virtual void makeRefStr( ScSheetLimits
& rLimits
,
1665 OUStringBuffer
& rBuf
,
1666 formula::FormulaGrammar::Grammar
/*eGram*/,
1667 const ScAddress
& rPos
,
1668 const OUString
& rErrRef
, const std::vector
<OUString
>& rTabNames
,
1669 const ScComplexRefData
& rRef
,
1671 bool /*bFromRangeName*/ ) const override
1673 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1674 ScComplexRefData
aRef( rRef
);
1676 MakeTabStr(rLimits
, rBuf
, rPos
, rTabNames
, aRef
, bSingleRef
);
1678 // Play fast and loose with invalid refs. There is not much point in producing
1679 // Foo!A1:#REF! versus #REF! at this point
1680 if (!rLimits
.ValidCol(aAbsRef
.aStart
.Col()) || !rLimits
.ValidRow(aAbsRef
.aStart
.Row()))
1682 rBuf
.append(rErrRef
);
1688 if (!rLimits
.ValidCol(aAbsRef
.aEnd
.Col()) || !rLimits
.ValidRow(aAbsRef
.aEnd
.Row()))
1690 rBuf
.append(rErrRef
);
1694 if (aAbsRef
.aStart
.Col() == 0 && aAbsRef
.aEnd
.Col() >= rLimits
.mnMaxCol
)
1696 r1c1_add_row(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1697 if (aAbsRef
.aStart
.Row() != aAbsRef
.aEnd
.Row() ||
1698 rRef
.Ref1
.IsRowRel() != rRef
.Ref2
.IsRowRel() )
1701 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1707 if (aAbsRef
.aStart
.Row() == 0 && aAbsRef
.aEnd
.Row() >= rLimits
.mnMaxRow
)
1709 r1c1_add_col(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1710 if (aAbsRef
.aStart
.Col() != aAbsRef
.aEnd
.Col() ||
1711 rRef
.Ref1
.IsColRel() != rRef
.Ref2
.IsColRel())
1714 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1720 r1c1_add_row(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1721 r1c1_add_col(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1725 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1726 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1730 ParseResult
parseAnyToken( const OUString
& rFormula
,
1732 const CharClass
* pCharClass
,
1733 bool bGroupSeparator
) const override
1735 parseExternalDocName(rFormula
, nSrcPos
);
1738 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, aRet
) )
1741 constexpr sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
|
1742 KParseTokens::ASC_UNDERSCORE
;
1743 constexpr sal_Int32 nContFlags
= nStartFlags
| KParseTokens::ASC_DOT
;
1744 // '?' allowed in range names
1745 static constexpr OUString
aAddAllowed(u
"?-[]!"_ustr
);
1747 return pCharClass
->parseAnyToken( rFormula
,
1748 nSrcPos
, nStartFlags
, aAddAllowed
,
1749 (bGroupSeparator
? nContFlags
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER
: nContFlags
),
1753 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const override
1755 return ConventionXL::getSpecialSymbol(eSymType
);
1758 virtual bool parseExternalName( const OUString
& rSymbol
, OUString
& rFile
, OUString
& rName
,
1759 const ScDocument
& rDoc
,
1760 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
) const override
1762 return ConventionXL::parseExternalName( rSymbol
, rFile
, rName
, rDoc
, pExternalLinks
);
1765 virtual OUString
makeExternalNameStr( sal_uInt16
/*nFileId*/, const OUString
& rFile
,
1766 const OUString
& rName
) const override
1768 return ConventionXL::makeExternalNameStr(rFile
, rName
);
1771 virtual void makeExternalRefStr(
1772 ScSheetLimits
& rLimits
,
1773 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1774 const OUString
& rTabName
, const ScSingleRefData
& rRef
) const override
1776 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1777 // This is a little different from the format Excel uses, as Excel
1778 // puts [] only around the file name. But we need to enclose the
1779 // whole file path with [] because the file name can contain any
1782 ScAddress aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1783 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1784 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
1785 rBuffer
.append('!');
1787 r1c1_add_row(rBuffer
, rRef
, aAbsRef
);
1788 r1c1_add_col(rBuffer
, rRef
, aAbsRef
);
1791 virtual void makeExternalRefStr(
1792 ScSheetLimits
& rLimits
,
1793 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, sal_uInt16
/*nFileId*/, const OUString
& rFileName
,
1794 const std::vector
<OUString
>& rTabNames
, const OUString
& rTabName
,
1795 const ScComplexRefData
& rRef
) const override
1797 ScRange aAbsRef
= rRef
.toAbs(rLimits
, rPos
);
1799 ConventionXL::makeExternalDocStr(rBuffer
, rFileName
);
1800 ConventionXL::makeExternalTabNameRange(rBuffer
, rTabName
, rTabNames
, aAbsRef
);
1801 rBuffer
.append('!');
1803 if (!rLimits
.ValidCol(aAbsRef
.aEnd
.Col()) || !rLimits
.ValidRow(aAbsRef
.aEnd
.Row()))
1805 rBuffer
.append(ScResId(STR_NO_REF_TABLE
));
1809 if (aAbsRef
.aStart
.Col() == 0 && aAbsRef
.aEnd
.Col() >= rLimits
.mnMaxCol
)
1811 r1c1_add_row(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1812 if (aAbsRef
.aStart
.Row() != aAbsRef
.aEnd
.Row() || rRef
.Ref1
.IsRowRel() != rRef
.Ref2
.IsRowRel())
1814 rBuffer
.append(':');
1815 r1c1_add_row(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1820 if (aAbsRef
.aStart
.Row() == 0 && aAbsRef
.aEnd
.Row() >= rLimits
.mnMaxRow
)
1822 r1c1_add_col(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1823 if (aAbsRef
.aStart
.Col() != aAbsRef
.aEnd
.Col() || rRef
.Ref1
.IsColRel() != rRef
.Ref2
.IsColRel())
1825 rBuffer
.append(':');
1826 r1c1_add_col(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1831 r1c1_add_row(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1832 r1c1_add_col(rBuffer
, rRef
.Ref1
, aAbsRef
.aStart
);
1833 rBuffer
.append(':');
1834 r1c1_add_row(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1835 r1c1_add_col(rBuffer
, rRef
.Ref2
, aAbsRef
.aEnd
);
1838 virtual ScCharFlags
getCharTableFlags( sal_Unicode c
, sal_Unicode cLast
) const override
1840 ScCharFlags nFlags
= mpCharTable
[static_cast<sal_uInt8
>(c
)];
1841 if (c
== '-' && cLast
== '[')
1842 // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1843 nFlags
|= ScCharFlags::Ident
;
1850 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
, ScTokenArray
& rArr
,
1851 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1852 : FormulaCompiler(rArr
, bComputeII
, bMatrixFlag
),
1853 rDoc(rCxt
.getDoc()),
1855 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1856 mnCurrentSheetTab(-1),
1857 mnCurrentSheetEndPos(0),
1858 pCharClass(&ScGlobal::getCharClass()),
1859 mbCharClassesDiffer(false),
1860 mnPredetectedReference(0),
1861 mnRangeOpPosInSymbol(-1),
1862 pConv(GetRefConvention(FormulaGrammar::CONV_OOO
)),
1863 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE
),
1864 mbCloseBrackets(true),
1866 mbRefConventionChartOOXML(false),
1867 maTabNames(rCxt
.getTabNames())
1869 SetGrammar(rCxt
.getGrammar());
1872 ScCompiler::ScCompiler( ScDocument
& rDocument
, const ScAddress
& rPos
, ScTokenArray
& rArr
,
1873 formula::FormulaGrammar::Grammar eGrammar
,
1874 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1875 : FormulaCompiler(rArr
, bComputeII
, bMatrixFlag
),
1878 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1879 mnCurrentSheetTab(-1),
1880 mnCurrentSheetEndPos(0),
1882 pCharClass( &ScGlobal::getCharClass() ),
1883 mbCharClassesDiffer(false),
1884 mnPredetectedReference(0),
1885 mnRangeOpPosInSymbol(-1),
1886 pConv( GetRefConvention( FormulaGrammar::CONV_OOO
) ),
1887 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE
),
1888 mbCloseBrackets( true ),
1890 mbRefConventionChartOOXML( false )
1892 SetGrammar( (eGrammar
== formula::FormulaGrammar::GRAM_UNSPECIFIED
) ?
1893 rDocument
.GetGrammar() :
1897 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
,
1898 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1899 : FormulaCompiler(bComputeII
, bMatrixFlag
),
1900 rDoc(rCxt
.getDoc()),
1902 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1903 mnCurrentSheetTab(-1),
1904 mnCurrentSheetEndPos(0),
1905 pCharClass(&ScGlobal::getCharClass()),
1906 mbCharClassesDiffer(false),
1907 mnPredetectedReference(0),
1908 mnRangeOpPosInSymbol(-1),
1909 pConv(GetRefConvention(FormulaGrammar::CONV_OOO
)),
1910 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE
),
1911 mbCloseBrackets(true),
1913 mbRefConventionChartOOXML(false),
1914 maTabNames(rCxt
.getTabNames())
1916 SetGrammar(rCxt
.getGrammar());
1919 ScCompiler::ScCompiler( ScDocument
& rDocument
, const ScAddress
& rPos
,
1920 formula::FormulaGrammar::Grammar eGrammar
,
1921 bool bComputeII
, bool bMatrixFlag
, ScInterpreterContext
* pContext
)
1922 : FormulaCompiler(bComputeII
, bMatrixFlag
),
1925 mrInterpreterContext(pContext
? *pContext
: rDoc
.GetNonThreadedContext()),
1926 mnCurrentSheetTab(-1),
1927 mnCurrentSheetEndPos(0),
1929 pCharClass( &ScGlobal::getCharClass() ),
1930 mbCharClassesDiffer(false),
1931 mnPredetectedReference(0),
1932 mnRangeOpPosInSymbol(-1),
1933 pConv( GetRefConvention( FormulaGrammar::CONV_OOO
) ),
1934 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE
),
1935 mbCloseBrackets( true ),
1937 mbRefConventionChartOOXML( false )
1939 SetGrammar( (eGrammar
== formula::FormulaGrammar::GRAM_UNSPECIFIED
) ?
1940 rDocument
.GetGrammar() :
1944 ScCompiler::~ScCompiler()
1948 void ScCompiler::CheckTabQuotes( OUString
& rString
,
1949 const FormulaGrammar::AddressConvention eConv
)
1951 sal_Int32 nStartFlags
= KParseTokens::ANY_LETTER_OR_NUMBER
| KParseTokens::ASC_UNDERSCORE
;
1952 sal_Int32 nContFlags
= nStartFlags
;
1953 ParseResult aRes
= ScGlobal::getCharClass().parsePredefinedToken(
1954 KParseType::IDENTNAME
, rString
, 0, nStartFlags
, OUString(), nContFlags
, OUString());
1955 bool bNeedsQuote
= !((aRes
.TokenType
& KParseType::IDENTNAME
) && aRes
.EndPos
== rString
.getLength());
1960 case FormulaGrammar::CONV_UNSPECIFIED
:
1962 case FormulaGrammar::CONV_OOO
:
1963 case FormulaGrammar::CONV_XL_A1
:
1964 case FormulaGrammar::CONV_XL_R1C1
:
1965 case FormulaGrammar::CONV_XL_OOX
:
1966 case FormulaGrammar::CONV_ODF
:
1969 // escape embedded quotes
1970 rString
= rString
.replaceAll( "'", "''" );
1975 if ( !bNeedsQuote
&& CharClass::isAsciiNumeric( rString
) )
1977 // Prevent any possible confusion resulting from pure numeric sheet names.
1983 rString
= "'" + rString
+ "'";
1987 sal_Int32
ScCompiler::GetDocTabPos( const OUString
& rString
)
1989 if (rString
[0] != '\'')
1991 sal_Int32 nPos
= ScGlobal::FindUnquoted( rString
, SC_COMPILER_FILE_TAB_SEP
);
1992 // it must be 'Doc'#
1993 if (nPos
!= -1 && rString
[nPos
-1] != '\'')
1998 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv
)
2000 const Convention
* p
= GetRefConvention(eConv
);
2002 SetRefConvention(p
);
2005 const ScCompiler::Convention
* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv
)
2010 case FormulaGrammar::CONV_OOO
:
2012 static const ConventionOOO_A1 ConvOOO_A1
;
2015 case FormulaGrammar::CONV_ODF
:
2017 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF
;
2018 return &ConvOOO_A1_ODF
;
2020 case FormulaGrammar::CONV_XL_A1
:
2022 static const ConventionXL_A1 ConvXL_A1
;
2025 case FormulaGrammar::CONV_XL_R1C1
:
2027 static const ConventionXL_R1C1 ConvXL_R1C1
;
2028 return &ConvXL_R1C1
;
2030 case FormulaGrammar::CONV_XL_OOX
:
2032 static const ConventionXL_OOX ConvXL_OOX
;
2035 case FormulaGrammar::CONV_UNSPECIFIED
:
2043 void ScCompiler::SetRefConvention( const ScCompiler::Convention
*pConvP
)
2046 meGrammar
= FormulaGrammar::mergeToGrammar( meGrammar
, pConv
->meConv
);
2047 assert( FormulaGrammar::isSupported( meGrammar
));
2050 void ScCompiler::SetError(FormulaError nError
)
2052 if( pArr
->GetCodeError() == FormulaError::NONE
)
2053 pArr
->SetCodeError( nError
);
2056 static sal_Unicode
* lcl_UnicodeStrNCpy( sal_Unicode
* pDst
, const sal_Unicode
* pSrc
, sal_Int32 nMax
)
2058 const sal_Unicode
* const pStop
= pDst
+ nMax
;
2059 while ( pDst
< pStop
)
2067 // p1 MUST contain at least n characters, or terminate with NIL.
2068 // p2 MUST pass upper case letters, if any.
2069 // n MUST not be greater than length of p2
2070 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode
* p1
, const char* p2
, size_t n
)
2072 for (size_t i
=0; i
<n
; ++i
)
2078 if (p1
[i
] < 'a' || 'z' < p1
[i
])
2079 return false; // not a lower case letter
2080 if (p2
[i
] < 'A' || 'Z' < p2
[i
])
2081 return false; // not a letter to match
2082 if (p1
[i
] != p2
[i
] + 0x20)
2083 return false; // lower case doesn't match either
2090 void ScCompiler::addWhitespace( std::vector
<ScCompiler::Whitespace
> & rvSpaces
,
2091 ScCompiler::Whitespace
& rSpace
, sal_Unicode c
, sal_Int32 n
)
2093 if (rSpace
.cChar
!= c
)
2095 if (rSpace
.cChar
&& rSpace
.nCount
> 0)
2096 rvSpaces
.emplace_back(rSpace
);
2104 // Parses the formula into separate symbols for further processing.
2105 // XXX NOTE: this is a rough sketch of the original idea, there are other
2106 // states that were added and didn't make it into this table and things are
2107 // more complicated. Use the source, Luke.
2109 // initial state = GetChar
2111 // old state | read character | action | new state
2112 //---------------+-------------------+-----------------------+---------------
2113 // GetChar | ;()+-*/^=& | Symbol=char | Stop
2114 // | <> | Symbol=char | GetBool
2115 // | $ letter | Symbol=char | GetWord
2116 // | number | Symbol=char | GetValue
2117 // | " | none | GetString
2118 // | other | none | GetChar
2119 //---------------+-------------------+-----------------------+---------------
2120 // GetBool | => | Symbol=Symbol+char | Stop
2121 // | other | Dec(CharPos) | Stop
2122 //---------------+-------------------+-----------------------+---------------
2123 // GetWord | SepSymbol | Dec(CharPos) | Stop
2124 // | ()+-*/^=<>&~ | |
2125 // | space | Dec(CharPos) | Stop
2127 // | letter, number | Symbol=Symbol+char | GetWord
2128 // | other | error | Stop
2129 //---------------+-------------------+-----------------------+---------------
2130 // GetValue | ;()*/^=<>& | |
2131 // | space | Dec(CharPos) | Stop
2132 // | number E+-%,. | Symbol=Symbol+char | GetValue
2133 // | other | error | Stop
2134 //---------------+-------------------+-----------------------+---------------
2135 // GetString | " | none | Stop
2136 // | other | Symbol=Symbol+char | GetString
2137 //---------------+-------------------+-----------------------+---------------
2139 std::vector
<ScCompiler::Whitespace
> ScCompiler::NextSymbol(bool bInArray
)
2141 std::vector
<Whitespace
> vSpaces
;
2142 cSymbol
[MAXSTRLEN
] = 0; // end
2143 sal_Unicode
* pSym
= cSymbol
;
2144 const sal_Unicode
* const pStart
= aFormula
.getStr();
2145 const sal_Unicode
* pSrc
= pStart
+ nSrcPos
;
2147 sal_Unicode c
= *pSrc
;
2148 sal_Unicode cLast
= 0;
2149 bool bQuote
= false;
2150 mnRangeOpPosInSymbol
= -1;
2151 ScanState eState
= ssGetChar
;
2153 sal_Unicode cSep
= mxSymbols
->getSymbolChar( ocSep
);
2154 sal_Unicode cArrayColSep
= mxSymbols
->getSymbolChar( ocArrayColSep
);
2155 sal_Unicode cArrayRowSep
= mxSymbols
->getSymbolChar( ocArrayRowSep
);
2156 sal_Unicode cDecSep
= (mxSymbols
->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0]);
2157 sal_Unicode cDecSepAlt
= (mxSymbols
->isEnglishLocale() ? 0 : ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar());
2159 // special symbols specific to address convention used
2160 sal_Unicode cSheetPrefix
= pConv
->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX
);
2161 sal_Unicode cSheetSep
= pConv
->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR
);
2164 bool bAutoIntersection
= false;
2165 size_t nAutoIntersectionSpacesPos
= 0;
2167 bool bErrorConstantHadSlash
= false;
2168 mnPredetectedReference
= 0;
2169 // try to parse simple tokens before calling i18n parser
2170 while ((c
!= 0) && (eState
!= ssStop
) )
2173 ScCharFlags nMask
= GetCharTableFlags( c
, cLast
);
2175 // The parameter separator and the array column and row separators end
2176 // things unconditionally if not in string or reference.
2177 if (c
== cSep
|| (bInArray
&& (c
== cArrayColSep
|| c
== cArrayRowSep
)))
2181 // these are to be continued
2184 case ssGetReference
:
2185 case ssSkipReference
:
2186 case ssGetTableRefItem
:
2187 case ssGetTableRefColumn
:
2190 if (eState
== ssGetChar
)
2197 Label_MaskStateMachine
:
2202 // Order is important!
2203 if (eLastOp
== ocTableRefOpen
&& c
!= '[' && c
!= '#' && c
!= ']')
2206 eState
= ssGetTableRefColumn
;
2208 else if( nMask
& ScCharFlags::OdfLabelOp
)
2210 // '!!' automatic intersection
2211 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::OdfLabelOp
)
2213 /* TODO: For now the UI "space operator" is used, this
2214 * could be enhanced using a specialized OpCode to get
2215 * rid of the space ambiguity, which would need some
2216 * places to be adapted though. And we would still need
2217 * to support the ambiguous space operator for UI
2218 * purposes anyway. However, we then could check for
2219 * invalid usage of '!!', which currently isn't
2221 if (!bAutoIntersection
)
2224 // Add 2 because it must match the character count
2226 addWhitespace( vSpaces
, aSpace
, 0x20, 2);
2227 // Position of Whitespace where it will be added to
2229 nAutoIntersectionSpacesPos
= vSpaces
.size();
2230 bAutoIntersection
= true;
2240 nMask
&= ~ScCharFlags::OdfLabelOp
;
2241 goto Label_MaskStateMachine
;
2244 else if( nMask
& ScCharFlags::OdfNameMarker
)
2246 // '$$' defined name marker
2247 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::OdfNameMarker
)
2249 // both eaten, not added to pSym
2254 nMask
&= ~ScCharFlags::OdfNameMarker
;
2255 goto Label_MaskStateMachine
;
2258 else if( nMask
& ScCharFlags::Char
)
2260 // '[' is a special case in Excel syntax, it can start an
2261 // external reference, ID in OOXML like [1]Sheet1!A1 or
2262 // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2263 // [filename]Sheet!R1C1 that needs to be scanned
2264 // entirely, or can be ocTableRefOpen, of which the first
2265 // transforms an ocDBArea into an ocTableRef.
2266 if (c
== '[' && FormulaGrammar::isExcelSyntax( meGrammar
)
2267 && eLastOp
!= ocDBArea
&& maTableRefs
.empty())
2269 // [0]!Global_Range_Name, is a special case in OOXML
2270 // syntax, where the '0' is referencing to self and we
2271 // do not need it, so we should skip it, in order to
2272 // later it will be more recognisable for IsNamedRange.
2273 if (FormulaGrammar::isRefConventionOOXML(meGrammar
) &&
2274 pSrc
[0] == '0' && pSrc
[1] == ']' && pSrc
[2] == '!')
2281 nMask
&= ~ScCharFlags::Char
;
2282 goto Label_MaskStateMachine
;
2290 else if( nMask
& ScCharFlags::OdfLBracket
)
2292 // eaten, not added to pSym
2293 eState
= ssGetReference
;
2294 mnPredetectedReference
= 1;
2296 else if( nMask
& ScCharFlags::CharBool
)
2301 else if( nMask
& ScCharFlags::CharValue
)
2304 eState
= ssGetValue
;
2306 else if( nMask
& ScCharFlags::CharString
)
2309 eState
= ssGetString
;
2311 else if( nMask
& ScCharFlags::CharErrConst
)
2315 if (!maTableRefs
.empty() && ((nLevel
= maTableRefs
.back().mnLevel
) == 2 || nLevel
== 1))
2316 eState
= ssGetTableRefItem
;
2318 eState
= ssGetErrorConstant
;
2320 else if( nMask
& ScCharFlags::CharDontCare
)
2322 addWhitespace( vSpaces
, aSpace
, c
);
2324 else if( nMask
& ScCharFlags::CharIdent
)
2325 { // try to get a simple ASCII identifier before calling
2326 // i18n, to gain performance during import
2328 eState
= ssGetIdent
;
2339 if ( nMask
& ScCharFlags::Ident
)
2340 { // This catches also $Sheet1.A$1, for example.
2341 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2343 SetError(FormulaError::StringOverflow
);
2349 else if (c
== '#' && lcl_isUnicodeIgnoreAscii( pSrc
, "REF!", 4))
2351 // Completely ugly means to catch broken
2352 // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2353 // references that were written in ODF named ranges
2354 // (without embracing [] hence no predetected reference)
2355 // and to OOXML and handle them as one symbol.
2356 // Also catches these in UI, so we can process them
2361 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2363 SetError(FormulaError::StringOverflow
);
2374 c
= *((--pSrc
)-1); // position last/next character correctly
2376 else if (c
== ':' && mnRangeOpPosInSymbol
< 0)
2378 // One range operator may form Sheet1.A:A, which we need to
2379 // pass as one entity to IsReference().
2380 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2382 SetError(FormulaError::StringOverflow
);
2387 mnRangeOpPosInSymbol
= pSym
- &cSymbol
[0];
2391 else if ( 128 <= c
|| '\'' == c
)
2392 { // High values need reparsing with i18n,
2393 // single quoted $'sheet' names too (otherwise we'd had to
2394 // implement everything twice).
2407 if( nMask
& ScCharFlags::Bool
)
2421 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2423 SetError(FormulaError::StringOverflow
);
2426 else if (c
== cDecSep
|| (cDecSepAlt
&& c
== cDecSepAlt
))
2430 // reparse with i18n, may be numeric sheet name as well
2437 else if( nMask
& ScCharFlags::Value
)
2439 else if( nMask
& ScCharFlags::ValueSep
)
2444 else if (c
== 'E' || c
== 'e')
2446 if (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::ValueExp
)
2450 // reparse with i18n
2455 else if( nMask
& ScCharFlags::ValueSign
)
2457 if (((cLast
== 'E') || (cLast
== 'e')) &&
2458 (GetCharTableFlags( pSrc
[0], 0 ) & ScCharFlags::ValueValue
))
2470 // reparse with i18n
2478 if( nMask
& ScCharFlags::StringSep
)
2483 bQuote
= true; // "" => literal "
2492 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2494 SetError(FormulaError::StringOverflow
);
2495 eState
= ssSkipString
;
2503 if( nMask
& ScCharFlags::StringSep
)
2506 case ssGetErrorConstant
:
2508 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2509 // BUT, in UI these may have been translated! So don't
2510 // check for ASCII alnum. Note that this construct can't be
2511 // parsed with i18n.
2512 /* TODO: be strict when reading ODFF, check for ASCII alnum
2513 * and proper continuation after '/'. However, even with
2514 * the lax parsing only the error constants we have defined
2515 * as opcode symbols will be recognized and others result
2516 * in ocBad, so the result is actually conformant. */
2522 // Check if this is #REF! that starts an invalid reference.
2523 // Note we have an implicit '!' here at the end.
2524 if (pSym
- &cSymbol
[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol
, "#REF", 4) &&
2525 (GetCharTableFlags( *pSrc
, c
) & ScCharFlags::Ident
))
2526 eState
= ssGetIdent
;
2532 if (!bErrorConstantHadSlash
)
2533 bErrorConstantHadSlash
= true;
2540 else if ((nMask
& ScCharFlags::WordSep
) ||
2541 (c
< 128 && !rtl::isAsciiAlphanumeric( c
)))
2550 if (pSym
== &cSymbol
[ MAXSTRLEN
])
2552 SetError( FormulaError::StringOverflow
);
2560 case ssGetTableRefItem
:
2562 // Scan whatever up to the next ']' closer.
2565 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2567 SetError( FormulaError::StringOverflow
);
2580 case ssGetTableRefColumn
:
2582 // Scan whatever up to the next unescaped ']' closer.
2583 if (c
!= ']' || cLast
== '\'')
2585 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2587 SetError( FormulaError::StringOverflow
);
2600 case ssGetReference
:
2601 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2603 SetError( FormulaError::StringOverflow
);
2604 eState
= ssSkipReference
;
2607 case ssSkipReference
:
2608 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2609 // mandatory also if no sheet name. 'External'# is optional,
2610 // sheet name is optional, quotes around sheet name are
2611 // optional if no quote contained. [#REF!] is valid.
2612 // 2nd usage: ['Sheet'.$$'DefinedName']
2613 // 3rd usage: ['External'#$$'DefinedName']
2614 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2615 // Also for all these names quotes are optional if no quote
2619 // nRefInName: 0 := not in sheet name yet. 'External'
2620 // is parsed as if it was a sheet name and nRefInName
2621 // is reset when # is encountered immediately after closing
2622 // quote. Same with 'DefinedName', nRefInName is cleared
2623 // when : is encountered.
2625 // Encountered leading $ before sheet name.
2626 constexpr int kDollar
= (1 << 1);
2627 // Encountered ' opening quote, which may be after $ or
2629 constexpr int kOpen
= (1 << 2);
2630 // Somewhere in name.
2631 constexpr int kName
= (1 << 3);
2632 // Encountered ' in name, will be cleared if double or
2633 // transformed to kClose if not, in which case kOpen is
2635 constexpr int kQuote
= (1 << 4);
2636 // Past ' closing quote.
2637 constexpr int kClose
= (1 << 5);
2638 // Encountered # file/sheet separator.
2639 constexpr int kFileSep
= (1 << 6);
2640 // Past . sheet name separator.
2641 constexpr int kPast
= (1 << 7);
2642 // Marked name $$ follows sheet name separator, detected
2643 // while we're still on the separator. Will be cleared when
2644 // entering the name.
2645 constexpr int kMarkAhead
= (1 << 8);
2646 // In marked defined name.
2647 constexpr int kDefName
= (1 << 9);
2648 // Encountered # of #REF!
2649 constexpr int kRefErr
= (1 << 10);
2651 bool bAddToSymbol
= true;
2652 if ((nMask
& ScCharFlags::OdfRBracket
) && !(nRefInName
& kOpen
))
2654 OSL_ENSURE( nRefInName
& (kPast
| kDefName
| kRefErr
),
2655 "ScCompiler::NextSymbol: reference: "
2656 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2657 // eaten, not added to pSym
2658 bAddToSymbol
= false;
2661 else if (cSheetSep
== c
&& nRefInName
== 0)
2663 // eat it, no sheet name [.A1]
2664 bAddToSymbol
= false;
2665 nRefInName
|= kPast
;
2666 if ('$' == pSrc
[0] && '$' == pSrc
[1])
2667 nRefInName
|= kMarkAhead
;
2669 else if (!(nRefInName
& kPast
) || (nRefInName
& (kMarkAhead
| kDefName
)))
2671 // Not in col/row yet.
2673 if (SC_COMPILER_FILE_TAB_SEP
== c
&& (nRefInName
& kFileSep
))
2675 else if ('$' == c
&& '$' == pSrc
[0] && !(nRefInName
& kOpen
))
2677 nRefInName
&= ~kMarkAhead
;
2678 if (!(nRefInName
& kDefName
))
2680 // eaten, not added to pSym (2 chars)
2681 bAddToSymbol
= false;
2683 nRefInName
&= kPast
;
2684 nRefInName
|= kDefName
;
2688 // ScAddress::Parse() will recognize this as
2690 if (eState
!= ssSkipReference
)
2694 if( pSym
== &cSymbol
[ MAXSTRLEN
] )
2696 SetError( FormulaError::StringOverflow
);
2702 bAddToSymbol
= false;
2705 else if (cSheetPrefix
== c
&& nRefInName
== 0)
2706 nRefInName
|= kDollar
;
2709 // TODO: The conventions' parseExternalName()
2710 // should handle quoted names, but as long as they
2711 // don't remove non-embedded quotes here.
2712 if (!(nRefInName
& kName
))
2714 nRefInName
|= (kOpen
| kName
);
2715 bAddToSymbol
= !(nRefInName
& kDefName
);
2717 else if (!(nRefInName
& kOpen
))
2719 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2720 "a ''' without the name being enclosed in '...' violates ODF spec");
2722 else if (nRefInName
& kQuote
)
2724 // escaped embedded quote
2725 nRefInName
&= ~kQuote
;
2732 // escapes embedded quote
2733 nRefInName
|= kQuote
;
2735 case SC_COMPILER_FILE_TAB_SEP
:
2736 // sheet name should follow
2737 nRefInName
|= kFileSep
;
2740 // quote not followed by quote => close
2741 nRefInName
|= kClose
;
2742 nRefInName
&= ~kOpen
;
2744 bAddToSymbol
= !(nRefInName
& kDefName
);
2747 else if ('#' == c
&& nRefInName
== 0)
2748 nRefInName
|= kRefErr
;
2749 else if (cSheetSep
== c
&& !(nRefInName
& kOpen
))
2751 // unquoted sheet name separator
2752 nRefInName
|= kPast
;
2753 if ('$' == pSrc
[0] && '$' == pSrc
[1])
2754 nRefInName
|= kMarkAhead
;
2756 else if (':' == c
&& !(nRefInName
& kOpen
))
2758 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2759 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2761 ++mnPredetectedReference
;
2763 else if (!(nRefInName
& kName
))
2765 // start unquoted name
2766 nRefInName
|= kName
;
2773 ++mnPredetectedReference
;
2775 if (bAddToSymbol
&& eState
!= ssSkipReference
)
2776 *pSym
++ = c
; // everything is part of reference
2780 ; // nothing, prevent warning
2787 if (aSpace
.nCount
&& aSpace
.cChar
)
2788 vSpaces
.emplace_back(aSpace
);
2792 const sal_Int32 nOldSrcPos
= nSrcPos
;
2793 for (const auto& r
: vSpaces
)
2794 nSrcPos
+= r
.nCount
;
2795 // If group separator is not a possible operator and not one of any
2796 // separators then it may be parsed away in numbers. This is
2797 // specifically the case with NO-BREAK SPACE, which actually triggers
2798 // the bi18n case (which we don't want to include as yet another
2799 // special case above as it is rare enough and doesn't generally occur
2801 const sal_Unicode cGroupSep
= ScGlobal::getLocaleData().getNumThousandSep()[0];
2802 const bool bGroupSeparator
= (128 <= cGroupSep
&& cGroupSep
!= cSep
&&
2803 cGroupSep
!= cArrayColSep
&& cGroupSep
!= cArrayRowSep
&&
2804 cGroupSep
!= cDecSep
&& cGroupSep
!= cDecSepAlt
&&
2805 cGroupSep
!= cSheetPrefix
&& cGroupSep
!= cSheetSep
);
2806 // If a numeric context triggered bi18n then use the default locale's
2807 // CharClass, this may accept group separator as well.
2808 const CharClass
* pMyCharClass
= (ScGlobal::getCharClass().isDigit( OUString(pStart
[nSrcPos
]), 0) ?
2809 &ScGlobal::getCharClass() : pCharClass
);
2810 OUStringBuffer aSymbol
;
2811 mnRangeOpPosInSymbol
= -1;
2812 FormulaError nErr
= FormulaError::NONE
;
2816 // special case (e.g. $'sheetname' in OOO A1)
2817 if ( pStart
[nSrcPos
] == cSheetPrefix
&& pStart
[nSrcPos
+1] == '\'' )
2818 aSymbol
.append(pStart
[nSrcPos
++]);
2820 ParseResult aRes
= pConv
->parseAnyToken( aFormula
, nSrcPos
, pMyCharClass
, bGroupSeparator
);
2822 if ( !aRes
.TokenType
)
2824 nErr
= FormulaError::IllegalChar
;
2825 SetError( nErr
); // parsed chars as string
2827 if ( aRes
.EndPos
<= nSrcPos
)
2829 // Could not parse anything meaningful.
2830 assert(!aRes
.TokenType
);
2831 nErr
= FormulaError::IllegalChar
;
2833 // Caller has to act on an empty symbol for
2834 // nSrcPos < aFormula.getLength()
2835 nSrcPos
= nOldSrcPos
;
2836 aSymbol
.setLength(0);
2840 // When having parsed a second reference part, ensure that the
2841 // i18n parser did not mistakenly parse a number that included
2842 // a separator which happened to be meant as a parameter
2843 // separator instead.
2844 if (mnRangeOpPosInSymbol
>= 0 && (aRes
.TokenType
& KParseType::ASC_NUMBER
))
2846 for (sal_Int32 i
= nSrcPos
; i
< aRes
.EndPos
; ++i
)
2848 if (pStart
[i
] == cSep
)
2849 aRes
.EndPos
= i
; // also ends for
2852 aSymbol
.append( pStart
+ nSrcPos
, aRes
.EndPos
- nSrcPos
);
2853 nSrcPos
= aRes
.EndPos
;
2854 c
= pStart
[nSrcPos
];
2855 if ( aRes
.TokenType
& KParseType::SINGLE_QUOTE_NAME
)
2856 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2857 bi18n
= (c
== cSheetSep
|| c
== SC_COMPILER_FILE_TAB_SEP
);
2859 // One range operator restarts parsing for second reference.
2860 if (c
== ':' && mnRangeOpPosInSymbol
< 0)
2862 mnRangeOpPosInSymbol
= aSymbol
.getLength();
2866 aSymbol
.append(pStart
[nSrcPos
++]);
2868 } while ( bi18n
&& nErr
== FormulaError::NONE
);
2869 sal_Int32 nLen
= aSymbol
.getLength();
2870 if ( nLen
> MAXSTRLEN
)
2872 SetError( FormulaError::StringOverflow
);
2875 if (mnRangeOpPosInSymbol
>= nLen
)
2876 mnRangeOpPosInSymbol
= -1;
2877 lcl_UnicodeStrNCpy( cSymbol
, aSymbol
.getStr(), nLen
);
2878 pSym
= &cSymbol
[nLen
];
2882 nSrcPos
= pSrc
- pStart
;
2885 if (mnRangeOpPosInSymbol
>= 0 && mnRangeOpPosInSymbol
== (pSym
-1) - &cSymbol
[0])
2887 // This is a trailing range operator, which is nonsense. Will be caught
2889 mnRangeOpPosInSymbol
= -1;
2894 aCorrectedSymbol
= OUString(cSymbol
, pSym
- cSymbol
);
2895 if (bAutoIntersection
&& vSpaces
[nAutoIntersectionSpacesPos
].nCount
> 1)
2896 --vSpaces
[nAutoIntersectionSpacesPos
].nCount
; // replace '!!' with only one space
2900 // Convert symbol to token
2902 bool ScCompiler::ParseOpCode( const OUString
& rName
, bool bInArray
)
2904 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
2905 bool bFound
= (iLook
!= mxSymbols
->getHashMap().end());
2908 OpCode eOp
= iLook
->second
;
2911 if (rName
== mxSymbols
->getSymbol(ocArrayColSep
))
2912 eOp
= ocArrayColSep
;
2913 else if (rName
== mxSymbols
->getSymbol(ocArrayRowSep
))
2914 eOp
= ocArrayRowSep
;
2916 else if (eOp
== ocArrayColSep
|| eOp
== ocArrayRowSep
)
2918 if (rName
== mxSymbols
->getSymbol(ocSep
))
2920 else if (rName
== ";")
2922 switch (FormulaGrammar::extractFormulaLanguage( meGrammar
))
2924 // Only for languages/grammars that actually use ';'
2925 // parameter separator.
2926 case css::sheet::FormulaLanguage::NATIVE
:
2927 case css::sheet::FormulaLanguage::ENGLISH
:
2928 case css::sheet::FormulaLanguage::ODFF
:
2929 case css::sheet::FormulaLanguage::ODF_11
:
2934 else if (eOp
== ocCeil
&& mxSymbols
->isOOXML())
2936 // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2937 // unassigned for import.
2940 else if (eOp
== ocFloor
&& mxSymbols
->isOOXML())
2942 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2943 // unassigned for import.
2946 maRawToken
.SetOpCode(eOp
);
2948 else if (mxSymbols
->isODFF())
2950 // ODFF names that are not written in the current mapping but to be
2951 // recognized. New names will be written in a future release, then
2952 // exchange (!) with the names in
2953 // formula/source/core/resource/core_resource.src to be able to still
2954 // read the old names as well.
2960 static const FunctionName aOdffAliases
[] = {
2961 // Renamed old names, still accept them:
2962 { "B", ocB
}, // B -> BINOM.DIST.RANGE
2963 { "TDIST", ocTDist
}, // TDIST -> LEGACY.TDIST
2964 { "EASTERSUNDAY", ocEasterSunday
}, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2965 { "ZGZ", ocRRI
}, // ZGZ -> RRI
2966 { "COLOR", ocColor
}, // COLOR -> ORG.LIBREOFFICE.COLOR
2967 { "GOALSEEK", ocBackSolver
}, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2968 { "COM.MICROSOFT.F.DIST", ocFDist_LT
}, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2969 { "COM.MICROSOFT.F.INV", ocFInv_LT
} // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2970 // Renamed new names, prepare to read future names:
2971 //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
2973 for (const FunctionName
& rOdffAlias
: aOdffAliases
)
2975 if (rName
.equalsIgnoreAsciiCaseAscii( rOdffAlias
.pName
))
2977 maRawToken
.SetOpCode( rOdffAlias
.eOp
);
2983 else if (mxSymbols
->isOOXML())
2985 // OOXML names that are not written in the current mapping but to be
2986 // recognized as old versions wrote them.
2992 static const FunctionName aOoxmlAliases
[] = {
2993 { "EFFECTIVE", ocEffect
}, // EFFECTIVE -> EFFECT
2994 { "ERRORTYPE", ocErrorType
}, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
2995 { "MULTIRANGE", ocMultiArea
}, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
2996 { "GOALSEEK", ocBackSolver
}, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
2997 { "EASTERSUNDAY", ocEasterSunday
}, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
2998 { "CURRENT", ocCurrent
}, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
2999 { "STYLE", ocStyle
} // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
3001 for (const FunctionName
& rOoxmlAlias
: aOoxmlAliases
)
3003 if (rName
.equalsIgnoreAsciiCaseAscii( rOoxmlAlias
.pName
))
3005 maRawToken
.SetOpCode( rOoxmlAlias
.eOp
);
3011 else if (mxSymbols
->isPODF())
3013 // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
3014 // We can't rename them in
3015 // formula/source/core/resource/core_resource.src but can add
3016 // additional names to be recognized here so they match the UI names if
3017 // those are renamed.
3023 static const FunctionName aPodfAliases
[] = {
3024 { "EFFECT", ocEffect
} // EFFECTIVE -> EFFECT
3026 for (const FunctionName
& rPodfAlias
: aPodfAliases
)
3028 if (rName
.equalsIgnoreAsciiCaseAscii( rPodfAlias
.pName
))
3030 maRawToken
.SetOpCode( rPodfAlias
.eOp
);
3040 if (mxSymbols
->hasExternals())
3042 // If symbols are set by filters get mapping to exact name.
3043 ExternalHashMap::const_iterator
iExt(
3044 mxSymbols
->getExternalHashMap().find( rName
));
3045 if (iExt
!= mxSymbols
->getExternalHashMap().end())
3047 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt
).second
))
3048 aIntName
= (*iExt
).second
;
3053 // Old (deprecated) addins first for legacy.
3054 if (ScGlobal::GetLegacyFuncCollection()->findByName(OUString(cSymbol
)))
3059 // bLocalFirst=false for (English) upper full original name
3060 // (service.function)
3061 aIntName
= ScGlobal::GetAddInCollection()->FindFunction(
3062 rName
, !mxSymbols
->isEnglish());
3064 if (!aIntName
.isEmpty())
3066 maRawToken
.SetExternal( aIntName
); // international name
3072 OpCode eOp
= maRawToken
.GetOpCode();
3073 if (eOp
== ocSub
|| eOp
== ocNegSub
)
3075 bool bShouldBeNegSub
=
3076 (eLastOp
== ocOpen
|| eLastOp
== ocSep
|| eLastOp
== ocNegSub
||
3077 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_BIN_OP
) ||
3078 eLastOp
== ocArrayOpen
||
3079 eLastOp
== ocArrayColSep
|| eLastOp
== ocArrayRowSep
);
3080 if (bShouldBeNegSub
&& eOp
== ocSub
)
3081 maRawToken
.NewOpCode( ocNegSub
);
3082 //TODO: if ocNegSub had ForceArray we'd have to set it here
3083 else if (!bShouldBeNegSub
&& eOp
== ocNegSub
)
3084 maRawToken
.NewOpCode( ocSub
);
3089 bool ScCompiler::ParseOpCode2( std::u16string_view rName
)
3091 for (sal_uInt16 i
= ocInternalBegin
; i
<= ocInternalEnd
; i
++)
3093 if (o3tl::equalsAscii(rName
, pInternal
[i
- ocInternalBegin
]))
3095 maRawToken
.SetOpCode(static_cast<OpCode
>(i
));
3103 static bool lcl_ParenthesisFollows( const sal_Unicode
* p
)
3110 bool ScCompiler::ParseValue( const OUString
& rSym
)
3112 const sal_Int32 nFormulaLanguage
= FormulaGrammar::extractFormulaLanguage( GetGrammar());
3113 if (nFormulaLanguage
== css::sheet::FormulaLanguage::ODFF
|| nFormulaLanguage
== css::sheet::FormulaLanguage::OOXML
)
3115 // Speedup things for ODFF, only well-formed numbers, not locale
3116 // dependent nor user input.
3117 rtl_math_ConversionStatus eStatus
;
3118 sal_Int32 nParseEnd
;
3119 double fVal
= rtl::math::stringToDouble( rSym
, '.', 0, &eStatus
, &nParseEnd
);
3120 if (nParseEnd
!= rSym
.getLength())
3122 // Not (only) a number.
3125 return false; // partially a number => no such thing
3127 if (lcl_ParenthesisFollows( aFormula
.getStr() + nSrcPos
))
3128 return false; // some function name, not a constant
3130 // Could be TRUE or FALSE constant.
3131 OpCode eOpFunc
= ocNone
;
3132 if (rSym
.equalsIgnoreAsciiCase("TRUE"))
3134 else if (rSym
.equalsIgnoreAsciiCase("FALSE"))
3136 if (eOpFunc
!= ocNone
)
3138 maRawToken
.SetOpCode(eOpFunc
);
3139 // add missing trailing parentheses
3140 maPendingOpCodes
.push(ocOpen
);
3141 maPendingOpCodes
.push(ocClose
);
3146 if (eStatus
== rtl_math_ConversionStatus_OutOfRange
)
3148 // rtl::math::stringToDouble() recognizes XMLSchema-2 "INF" and
3149 // "NaN" (case sensitive) that could be named expressions or DB
3151 // rSym is already upper so "NaN" is not possible here.
3152 if (!std::isfinite(fVal
) && rSym
== "INF")
3155 if (GetRangeData( nSheet
, rSym
))
3157 if (rDoc
.GetDBCollection()->getNamedDBs().findByUpperName(rSym
))
3160 /* TODO: is there a specific reason why we don't accept an infinity
3161 * value that would raise an error in the interpreter, instead of
3162 * setting the hard error at the token array already? */
3163 SetError( FormulaError::IllegalArgument
);
3165 maRawToken
.SetDouble( fVal
);
3170 sal_uInt32 nIndex
= mxSymbols
->isEnglishLocale() ? mrInterpreterContext
.NFGetStandardIndex(LANGUAGE_ENGLISH_US
) : 0;
3172 if (!mrInterpreterContext
.NFIsNumberFormat(rSym
, nIndex
, fVal
))
3175 SvNumFormatType nType
= mrInterpreterContext
.NFGetType(nIndex
);
3177 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3178 // Dates should never be entered directly and automatically converted
3179 // to serial, because the serial would be wrong if null-date changed.
3180 // Usually it wouldn't be accepted anyway because the date separator
3181 // clashed with other separators or operators.
3182 if (nType
& (SvNumFormatType::TIME
| SvNumFormatType::DATE
))
3185 if (nType
== SvNumFormatType::LOGICAL
)
3187 if (lcl_ParenthesisFollows( aFormula
.getStr() + nSrcPos
))
3188 return false; // Boolean function instead.
3191 if( nType
== SvNumFormatType::TEXT
)
3192 // HACK: number too big!
3193 SetError( FormulaError::IllegalArgument
);
3194 maRawToken
.SetDouble( fVal
);
3198 bool ScCompiler::ParseString()
3200 if ( cSymbol
[0] != '"' )
3202 const sal_Unicode
* p
= cSymbol
+1;
3205 sal_Int32 nLen
= sal::static_int_cast
<sal_Int32
>( p
- cSymbol
- 1 );
3206 if (!nLen
|| cSymbol
[nLen
] != '"')
3208 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(OUString(cSymbol
+1, nLen
-1));
3209 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
3213 bool ScCompiler::ParsePredetectedErrRefReference( const OUString
& rName
, const OUString
* pErrRef
)
3215 switch (mnPredetectedReference
)
3218 return ParseSingleReference( rName
, pErrRef
);
3220 return ParseDoubleReference( rName
, pErrRef
);
3226 bool ScCompiler::ParsePredetectedReference( const OUString
& rName
)
3228 // Speedup documents with lots of broken references, e.g. sheet deleted.
3229 // It could also be a broken invalidated reference that contains #REF!
3230 // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3231 // between 2013 and 2016 until 5.1.4
3232 constexpr OUString
aErrRef(u
"#REF!"_ustr
); // not localized in ODFF
3233 sal_Int32 nPos
= rName
.indexOf( aErrRef
);
3236 /* TODO: this may be enhanced by reusing scan information from
3237 * NextSymbol(), the positions of quotes and special characters found
3238 * there for $'sheet'.A1:... could be stored in a vector. We don't
3239 * fully rescan here whether found positions are within single quotes
3240 * for performance reasons. This code does not check for possible
3241 * occurrences of insane "valid" sheet names like
3242 * 'haha.#REF!1fooledyou' and will generate an error on such. */
3245 // Per ODFF the correct string for a reference error is just #REF!,
3247 if (rName
.getLength() == 5)
3248 return ParseErrorConstant( rName
);
3249 // #REF!.AB42 or #REF!42 or #REF!#REF!
3250 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3252 sal_Unicode c
= rName
[nPos
-1]; // before #REF!
3257 // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3258 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3260 c
= rName
[nPos
-2]; // before $#REF!
3262 sal_Unicode c2
= nPos
+5 < rName
.getLength() ? rName
[nPos
+5] : 0; // after #REF!
3266 if ('$' == c2
|| '#' == c2
|| ('0' <= c2
&& c2
<= '9'))
3268 // sheet.#REF!42 or sheet.#REF!#REF!
3269 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3273 if (mnPredetectedReference
> 1 &&
3274 ('.' == c2
|| '$' == c2
|| '#' == c2
||
3275 ('0' <= c2
&& c2
<= '9')))
3277 // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3278 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3282 if (rtl::isAsciiAlpha(c
) &&
3283 ((mnPredetectedReference
> 1 && ':' == c2
) || 0 == c2
))
3285 // AB#REF!: or AB#REF!
3286 return ParsePredetectedErrRefReference( rName
, &aErrRef
);
3290 switch (mnPredetectedReference
)
3293 return ParseSingleReference( rName
);
3295 return ParseDoubleReference( rName
);
3300 bool ScCompiler::ParseDoubleReference( const OUString
& rName
, const OUString
* pErrRef
)
3302 ScRange
aRange( aPos
, aPos
);
3303 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
3304 ScAddress::ExternalInfo aExtInfo
;
3305 ScRefFlags nFlags
= aRange
.Parse( rName
, rDoc
, aDetails
, &aExtInfo
, &maExternalLinks
, pErrRef
);
3306 if( nFlags
& ScRefFlags::VALID
)
3308 ScComplexRefData aRef
;
3309 aRef
.InitRange( aRange
);
3310 aRef
.Ref1
.SetColRel( (nFlags
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
);
3311 aRef
.Ref1
.SetRowRel( (nFlags
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
);
3312 aRef
.Ref1
.SetTabRel( (nFlags
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
);
3313 if ( !(nFlags
& ScRefFlags::TAB_VALID
) )
3314 aRef
.Ref1
.SetTabDeleted( true ); // #REF!
3315 aRef
.Ref1
.SetFlag3D( ( nFlags
& ScRefFlags::TAB_3D
) != ScRefFlags::ZERO
);
3316 aRef
.Ref2
.SetColRel( (nFlags
& ScRefFlags::COL2_ABS
) == ScRefFlags::ZERO
);
3317 aRef
.Ref2
.SetRowRel( (nFlags
& ScRefFlags::ROW2_ABS
) == ScRefFlags::ZERO
);
3318 aRef
.Ref2
.SetTabRel( (nFlags
& ScRefFlags::TAB2_ABS
) == ScRefFlags::ZERO
);
3319 if ( !(nFlags
& ScRefFlags::TAB2_VALID
) )
3320 aRef
.Ref2
.SetTabDeleted( true ); // #REF!
3321 aRef
.Ref2
.SetFlag3D( ( nFlags
& ScRefFlags::TAB2_3D
) != ScRefFlags::ZERO
);
3322 aRef
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
3323 if (aExtInfo
.mbExternal
)
3325 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3326 const OUString
* pRealTab
= pRefMgr
->getRealTableName(aExtInfo
.mnFileId
, aExtInfo
.maTabName
);
3327 maRawToken
.SetExternalDoubleRef(
3328 aExtInfo
.mnFileId
, pRealTab
? *pRealTab
: aExtInfo
.maTabName
, aRef
);
3329 maExternalFiles
.push_back(aExtInfo
.mnFileId
);
3333 maRawToken
.SetDoubleReference(aRef
);
3337 return ( nFlags
& ScRefFlags::VALID
) != ScRefFlags::ZERO
;
3340 bool ScCompiler::ParseSingleReference( const OUString
& rName
, const OUString
* pErrRef
)
3342 mnCurrentSheetEndPos
= 0;
3343 mnCurrentSheetTab
= -1;
3344 ScAddress
aAddr( aPos
);
3345 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
3346 ScAddress::ExternalInfo aExtInfo
;
3347 ScRefFlags nFlags
= aAddr
.Parse( rName
, rDoc
, aDetails
,
3348 &aExtInfo
, &maExternalLinks
, &mnCurrentSheetEndPos
, pErrRef
);
3349 // Something must be valid in order to recognize Sheet1.blah or blah.a1
3350 // as a (wrong) reference.
3351 if( nFlags
& ( ScRefFlags::COL_VALID
|ScRefFlags::ROW_VALID
|ScRefFlags::TAB_VALID
) )
3353 // Valid given tab and invalid col or row may indicate a sheet-local
3354 // named expression, bail out early and don't create a reference token.
3355 if (!(nFlags
& ScRefFlags::VALID
) && mnCurrentSheetEndPos
> 0 &&
3356 (nFlags
& ScRefFlags::TAB_VALID
) && (nFlags
& ScRefFlags::TAB_3D
))
3358 if (aExtInfo
.mbExternal
)
3360 // External names are handled separately.
3361 mnCurrentSheetEndPos
= 0;
3362 mnCurrentSheetTab
= -1;
3366 mnCurrentSheetTab
= aAddr
.Tab();
3371 if( HasPossibleNamedRangeConflict( aAddr
.Tab()))
3373 // A named range named e.g. 'num1' is valid with 1k columns, but would become a reference
3374 // when the document is opened later with 16k columns. Resolve the conflict by not
3375 // considering it a reference.
3376 OUString
aUpper( ScGlobal::getCharClass().uppercase( rName
));
3377 mnCurrentSheetTab
= aAddr
.Tab(); // temporarily set for ParseNamedRange()
3378 if(ParseNamedRange( aUpper
, true )) // only check
3380 mnCurrentSheetTab
= -1;
3383 ScSingleRefData aRef
;
3384 aRef
.InitAddress( aAddr
);
3385 aRef
.SetColRel( (nFlags
& ScRefFlags::COL_ABS
) == ScRefFlags::ZERO
);
3386 aRef
.SetRowRel( (nFlags
& ScRefFlags::ROW_ABS
) == ScRefFlags::ZERO
);
3387 aRef
.SetTabRel( (nFlags
& ScRefFlags::TAB_ABS
) == ScRefFlags::ZERO
);
3388 aRef
.SetFlag3D( ( nFlags
& ScRefFlags::TAB_3D
) != ScRefFlags::ZERO
);
3389 // the reference is really invalid
3390 if( !( nFlags
& ScRefFlags::VALID
) )
3392 if( !( nFlags
& ScRefFlags::COL_VALID
) )
3393 aRef
.SetColDeleted(true);
3394 if( !( nFlags
& ScRefFlags::ROW_VALID
) )
3395 aRef
.SetRowDeleted(true);
3396 if( !( nFlags
& ScRefFlags::TAB_VALID
) )
3397 aRef
.SetTabDeleted(true);
3398 nFlags
|= ScRefFlags::VALID
;
3400 aRef
.SetAddress(rDoc
.GetSheetLimits(), aAddr
, aPos
);
3402 if (aExtInfo
.mbExternal
)
3404 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3405 const OUString
* pRealTab
= pRefMgr
->getRealTableName(aExtInfo
.mnFileId
, aExtInfo
.maTabName
);
3406 maRawToken
.SetExternalSingleRef(
3407 aExtInfo
.mnFileId
, pRealTab
? *pRealTab
: aExtInfo
.maTabName
, aRef
);
3408 maExternalFiles
.push_back(aExtInfo
.mnFileId
);
3411 maRawToken
.SetSingleReference(aRef
);
3414 return ( nFlags
& ScRefFlags::VALID
) != ScRefFlags::ZERO
;
3417 bool ScCompiler::ParseReference( const OUString
& rName
, const OUString
* pErrRef
)
3419 // Has to be called before ParseValue
3421 // A later ParseNamedRange() relies on these, being set in ParseSingleReference()
3422 // if so, reset in all cases.
3423 mnCurrentSheetEndPos
= 0;
3424 mnCurrentSheetTab
= -1;
3426 sal_Unicode ch1
= rName
[0];
3427 sal_Unicode cDecSep
= ( mxSymbols
->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0] );
3428 if ( ch1
== cDecSep
)
3430 // Code further down checks only if cDecSep=='.' so simply obtaining the
3431 // alternative decimal separator if it's not is sufficient.
3434 cDecSep
= ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar();
3435 if ( ch1
== cDecSep
)
3438 // Who was that imbecile introducing '.' as the sheet name separator!?!
3439 if ( rtl::isAsciiDigit( ch1
) && pConv
->getSpecialSymbol( Convention::SHEET_SEPARATOR
) == '.' )
3441 // Numerical sheet name is valid.
3442 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3443 // Don't create a #REF! of values. But also do not bail out on
3444 // something like 3:3, meaning entire row 3.
3447 const sal_Int32 nPos
= ScGlobal::FindUnquoted( rName
, '.');
3450 if (ScGlobal::FindUnquoted( rName
, ':') != -1)
3451 break; // may be 3:3, continue as usual
3454 sal_Unicode
const * const pTabSep
= rName
.getStr() + nPos
;
3455 sal_Unicode ch2
= pTabSep
[1]; // maybe a column identifier
3456 if ( !(ch2
== '$' || rtl::isAsciiAlpha( ch2
)) )
3458 if ( cDecSep
== '.' && (ch2
== 'E' || ch2
== 'e') // E + - digit
3459 && (GetCharTableFlags( pTabSep
[2], pTabSep
[1] ) & ScCharFlags::ValueExp
) )
3461 // If it is an 1.E2 expression check if "1" is an existent sheet
3462 // name. If so, a desired value 1.E2 would have to be entered as
3463 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3464 // require numerical sheet names always being entered quoted, which
3465 // is not desirable (too many 1999, 2000, 2001 sheets in use).
3466 // Furthermore, XML files created with versions prior to SRC640e
3467 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3468 // and would produce wrong formulas if the conditions here are met.
3469 // If you can live with these restrictions you may remove the
3470 // check and return an unconditional FALSE.
3471 OUString
aTabName( rName
.copy( 0, nPos
) );
3473 if ( !rDoc
.GetTable( aTabName
, nTab
) )
3475 // If sheet "1" exists and the expression is 1.E+2 continue as
3476 // usual, the ScRange/ScAddress parser will take care of it.
3481 if (ParseSingleReference( rName
, pErrRef
))
3484 // Though the range operator is handled explicitly, when encountering
3485 // something like Sheet1.A:A we will have to treat it as one entity if it
3486 // doesn't pass as single cell reference.
3487 if (mnRangeOpPosInSymbol
> 0) // ":foo" would be nonsense
3489 if (ParseDoubleReference( rName
, pErrRef
))
3491 // Now try with a symbol up to the range operator, rewind source
3493 assert(mnRangeOpPosInSymbol
< MAXSTRLEN
); // We should have caught the maldoers.
3494 if (mnRangeOpPosInSymbol
>= MAXSTRLEN
) // TODO: this check and return
3495 return false; // can be removed when sure.
3496 sal_Int32 nLen
= mnRangeOpPosInSymbol
;
3497 while (cSymbol
[++nLen
])
3499 cSymbol
[mnRangeOpPosInSymbol
] = 0;
3500 nSrcPos
-= (nLen
- mnRangeOpPosInSymbol
);
3501 mnRangeOpPosInSymbol
= -1;
3503 return true; // end all checks
3507 switch (pConv
->meConv
)
3509 case FormulaGrammar::CONV_XL_A1
:
3510 case FormulaGrammar::CONV_XL_OOX
:
3511 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3512 // sickness, mnRangeOpPosInSymbol did not catch the range
3513 // operator as it is within a quoted name.
3514 if (rName
[0] != '\'')
3515 return false; // Document name has to be single quoted.
3517 case FormulaGrammar::CONV_XL_R1C1
:
3518 // C2 or C[1] are valid entire column references.
3519 if (ParseDoubleReference( rName
, pErrRef
))
3529 bool ScCompiler::ParseMacro( const OUString
& rName
)
3531 #if !HAVE_FEATURE_SCRIPTING
3537 // Calling SfxObjectShell::GetBasic() may result in all sort of things
3538 // including obtaining the model and deep down in
3539 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3540 // formulas are compiled from a threaded import may result in a deadlock.
3541 // Check first if we actually could acquire it and if not bail out.
3542 /* FIXME: yes, but how ... */
3543 vcl::SolarMutexTryAndBuyGuard g
;
3544 if (!g
.isAcquired())
3546 SAL_WARN( "sc.core", "ScCompiler::ParseMacro - SolarMutex would deadlock, not obtaining Basic");
3547 return false; // bad luck
3550 OUString
aName( rName
);
3551 StarBASIC
* pObj
= nullptr;
3552 ScDocShell
* pDocSh
= rDoc
.GetDocumentShell();
3557 pObj
= pDocSh
->GetBasic();
3559 pObj
= SfxApplication::GetBasic();
3569 // ODFF recommends to store user-defined functions prefixed with "USER.",
3570 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3571 // function name so a function "USER.FOO" could not exist, and macro check
3572 // is assigned the lowest priority in function name check.
3573 if (FormulaGrammar::isODFF( GetGrammar()) && aName
.startsWithIgnoreAsciiCase("USER."))
3574 aName
= aName
.copy(5);
3576 SbxMethod
* pMeth
= static_cast<SbxMethod
*>(pObj
->Find( aName
, SbxClassType::Method
));
3581 // It really should be a BASIC function!
3582 if( pMeth
->GetType() == SbxVOID
3583 || ( pMeth
->IsFixed() && pMeth
->GetType() == SbxEMPTY
)
3584 || dynamic_cast<const SbMethod
*>( pMeth
) == nullptr )
3588 maRawToken
.SetExternal( aName
);
3589 maRawToken
.eOp
= ocMacro
;
3594 const ScRangeData
* ScCompiler::GetRangeData( SCTAB
& rSheet
, const OUString
& rUpperName
) const
3596 // try local names first
3597 rSheet
= aPos
.Tab();
3598 const ScRangeName
* pRangeName
= rDoc
.GetRangeName(rSheet
);
3599 const ScRangeData
* pData
= nullptr;
3601 pData
= pRangeName
->findByUpperName(rUpperName
);
3604 pRangeName
= rDoc
.GetRangeName();
3606 pData
= pRangeName
->findByUpperName(rUpperName
);
3613 bool ScCompiler::HasPossibleNamedRangeConflict( SCTAB nTab
) const
3615 const ScRangeName
* pRangeName
= rDoc
.GetRangeName();
3616 if (pRangeName
&& pRangeName
->hasPossibleAddressConflict())
3618 pRangeName
= rDoc
.GetRangeName(nTab
);
3619 if (pRangeName
&& pRangeName
->hasPossibleAddressConflict())
3624 bool ScCompiler::ParseNamedRange( const OUString
& rUpperName
, bool onlyCheck
)
3626 // ParseNamedRange is called only from NextNewToken, with an upper-case string
3629 const ScRangeData
* pData
= GetRangeData( nSheet
, rUpperName
);
3633 maRawToken
.SetName( nSheet
, pData
->GetIndex());
3637 // Sheet-local name with sheet specified.
3638 if (mnCurrentSheetEndPos
> 0 && mnCurrentSheetTab
>= 0)
3640 OUString
aName( rUpperName
.copy( mnCurrentSheetEndPos
));
3641 const ScRangeName
* pRangeName
= rDoc
.GetRangeName( mnCurrentSheetTab
);
3644 pData
= pRangeName
->findByUpperName(aName
);
3648 maRawToken
.SetName( mnCurrentSheetTab
, pData
->GetIndex());
3657 bool ScCompiler::ParseLambdaFuncName( const OUString
& aOrg
)
3659 if (m_aLambda
.bInLambdaFunction
&& !aOrg
.isEmpty())
3661 OUString aName
= aOrg
;
3662 if (aOrg
.startsWithIgnoreAsciiCase(u
"_xlpm."))
3663 aName
= aName
.copy(6);
3665 if (m_aLambda
.nParaPos
% 2 == 1 && m_aLambda
.nParaCount
> m_aLambda
.nParaPos
)
3666 m_aLambda
.aNameSet
.insert(aName
);
3669 // should already exist the name
3670 if (m_aLambda
.aNameSet
.find(aName
) == m_aLambda
.aNameSet
.end())
3673 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aName
);
3674 maRawToken
.SetStringName(aSS
.getData(), aSS
.getDataIgnoreCase());
3680 bool ScCompiler::ParseExternalNamedRange( const OUString
& rSymbol
, bool& rbInvalidExternalNameRange
)
3682 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3683 * correctly parses external named references in OOo, as required per RFE
3684 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3685 * spec first. Until then don't pretend to support external names that
3686 * wouldn't survive a save and reload cycle, return false instead. */
3688 rbInvalidExternalNameRange
= false;
3693 OUString aFile
, aName
;
3694 if (!pConv
->parseExternalName( rSymbol
, aFile
, aName
, rDoc
, &maExternalLinks
))
3697 if (aFile
.getLength() > MAXSTRLEN
|| aName
.getLength() > MAXSTRLEN
)
3700 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
3701 OUString aTmp
= aFile
;
3702 pRefMgr
->convertToAbsName(aTmp
);
3704 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aFile
);
3705 if (!pRefMgr
->isValidRangeName(nFileId
, aName
))
3707 rbInvalidExternalNameRange
= true;
3708 // range name doesn't exist in the source document.
3712 const OUString
* pRealName
= pRefMgr
->getRealRangeName(nFileId
, aName
);
3713 maRawToken
.SetExternalName(nFileId
, pRealName
? *pRealName
: aTmp
);
3714 maExternalFiles
.push_back(nFileId
);
3718 bool ScCompiler::ParseDBRange( const OUString
& rName
)
3720 ScDBCollection::NamedDBs
& rDBs
= rDoc
.GetDBCollection()->getNamedDBs();
3721 const ScDBData
* p
= rDBs
.findByUpperName(rName
);
3725 maRawToken
.SetName( -1, p
->GetIndex()); // DB range is always global.
3726 maRawToken
.eOp
= ocDBArea
;
3730 bool ScCompiler::ParseColRowName( const OUString
& rName
)
3732 bool bInList
= false;
3733 bool bFound
= false;
3734 ScSingleRefData aRef
;
3735 OUString
aName( rName
);
3737 SCTAB nThisTab
= aPos
.Tab();
3738 for ( short jThisTab
= 1; jThisTab
>= 0 && !bInList
; jThisTab
-- )
3739 { // first check ranges on this sheet, in case of duplicated names
3740 for ( short jRow
=0; jRow
<2 && !bInList
; jRow
++ )
3742 ScRangePairList
* pRL
;
3744 pRL
= rDoc
.GetColNameRanges();
3746 pRL
= rDoc
.GetRowNameRanges();
3747 for ( size_t iPair
= 0, nPairs
= pRL
->size(); iPair
< nPairs
&& !bInList
; ++iPair
)
3749 const ScRangePair
& rR
= (*pRL
)[iPair
];
3750 const ScRange
& rNameRange
= rR
.GetRange(0);
3751 if ( jThisTab
&& (rNameRange
.aStart
.Tab() > nThisTab
||
3752 nThisTab
> rNameRange
.aEnd
.Tab()) )
3754 ScCellIterator
aIter( rDoc
, rNameRange
);
3755 for (bool bHas
= aIter
.first(); bHas
&& !bInList
; bHas
= aIter
.next())
3757 // Don't crash if cell (via CompileNameFormula) encounters
3758 // a formula cell without code and
3759 // HasStringData/Interpret/Compile is executed and all that
3761 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3762 CellType eType
= aIter
.getType();
3764 if (eType
== CELLTYPE_FORMULA
)
3766 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3767 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
3772 if (bOk
&& aIter
.hasString())
3774 OUString aStr
= aIter
.getString();
3775 if ( ScGlobal::GetTransliteration().isEqual( aStr
, aName
) )
3779 aRef
.SetColRel( true ); // ColName
3781 aRef
.SetRowRel( true ); // RowName
3782 aRef
.SetAddress(rDoc
.GetSheetLimits(), aIter
.GetPos(), aPos
);
3783 bInList
= bFound
= true;
3790 if ( !bInList
&& rDoc
.GetDocOptions().IsLookUpColRowNames() )
3791 { // search in current sheet
3792 tools::Long nDistance
= 0, nMax
= 0;
3793 tools::Long nMyCol
= static_cast<tools::Long
>(aPos
.Col());
3794 tools::Long nMyRow
= static_cast<tools::Long
>(aPos
.Row());
3796 ScAddress
aOne( 0, 0, aPos
.Tab() );
3797 ScAddress
aTwo( rDoc
.MaxCol(), rDoc
.MaxRow(), aPos
.Tab() );
3799 ScAutoNameCache
* pNameCache
= rDoc
.GetAutoNameCache();
3802 // use GetNameOccurrences to collect all positions of aName on the sheet
3803 // (only once), similar to the outer part of the loop in the "else" branch.
3805 const ScAutoNameAddresses
& rAddresses
= pNameCache
->GetNameOccurrences( aName
, aPos
.Tab() );
3807 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3808 // The order of addresses in the vector is the same as from ScCellIterator.
3810 for ( const ScAddress
& aAddress
: rAddresses
)
3813 { // stop if everything else is further away
3814 if ( nMax
< static_cast<tools::Long
>(aAddress
.Col()) )
3817 if ( aAddress
!= aPos
)
3819 // same treatment as in isEqual case below
3821 SCCOL nCol
= aAddress
.Col();
3822 SCROW nRow
= aAddress
.Row();
3823 tools::Long nC
= nMyCol
- nCol
;
3824 tools::Long nR
= nMyRow
- nRow
;
3827 tools::Long nD
= nC
* nC
+ nR
* nR
;
3828 if ( nD
< nDistance
)
3830 if ( nC
< 0 || nR
< 0 )
3833 aTwo
.Set( nCol
, nRow
, aAddress
.Tab() );
3834 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3837 else if ( nRow
>= aOne
.Row() || nMyRow
< static_cast<tools::Long
>(aOne
.Row()) )
3839 // upper left, only if not further up than the
3840 // current entry and nMyRow is below (CellIter
3841 // runs column-wise)
3843 aOne
.Set( nCol
, nRow
, aAddress
.Tab() );
3844 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
3851 aOne
.Set( nCol
, nRow
, aAddress
.Tab() );
3852 nDistance
= nC
* nC
+ nR
* nR
;
3853 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3862 ScCellIterator
aIter( rDoc
, ScRange( aOne
, aTwo
) );
3863 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
3866 { // stop if everything else is further away
3867 if ( nMax
< static_cast<tools::Long
>(aIter
.GetPos().Col()) )
3870 CellType eType
= aIter
.getType();
3872 if (eType
== CELLTYPE_FORMULA
)
3874 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3875 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
3880 if (bOk
&& aIter
.hasString())
3882 OUString aStr
= aIter
.getString();
3883 if ( ScGlobal::GetTransliteration().isEqual( aStr
, aName
) )
3885 SCCOL nCol
= aIter
.GetPos().Col();
3886 SCROW nRow
= aIter
.GetPos().Row();
3887 tools::Long nC
= nMyCol
- nCol
;
3888 tools::Long nR
= nMyRow
- nRow
;
3891 tools::Long nD
= nC
* nC
+ nR
* nR
;
3892 if ( nD
< nDistance
)
3894 if ( nC
< 0 || nR
< 0 )
3897 aTwo
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3898 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3901 else if ( nRow
>= aOne
.Row() || nMyRow
< static_cast<tools::Long
>(aOne
.Row()) )
3903 // upper left, only if not further up than the
3904 // current entry and nMyRow is below (CellIter
3905 // runs column-wise)
3907 aOne
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3908 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
3915 aOne
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3916 nDistance
= nC
* nC
+ nR
* nR
;
3917 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
3930 if ( nMyCol
>= static_cast<tools::Long
>(aOne
.Col()) && nMyRow
>= static_cast<tools::Long
>(aOne
.Row()) )
3931 aAdr
= aOne
; // upper left takes precedence
3934 if ( nMyCol
< static_cast<tools::Long
>(aOne
.Col()) )
3935 { // two to the right
3936 if ( nMyRow
>= static_cast<tools::Long
>(aTwo
.Row()) )
3937 aAdr
= aTwo
; // directly right
3942 { // two below or below and right, take the nearest
3943 tools::Long nC1
= nMyCol
- aOne
.Col();
3944 tools::Long nR1
= nMyRow
- aOne
.Row();
3945 tools::Long nC2
= nMyCol
- aTwo
.Col();
3946 tools::Long nR2
= nMyRow
- aTwo
.Row();
3947 if ( nC1
* nC1
+ nR1
* nR1
<= nC2
* nC2
+ nR2
* nR2
)
3956 aRef
.InitAddress( aAdr
);
3957 // Prioritize on column label; row label only if the next cell
3958 // above/below the found label cell is text, or if both are not and
3959 // the cell below is empty and the next cell to the right is
3961 if ((aAdr
.Row() < rDoc
.MaxRow() && rDoc
.HasStringData(
3962 aAdr
.Col(), aAdr
.Row() + 1, aAdr
.Tab()))
3963 || (aAdr
.Row() > 0 && rDoc
.HasStringData(
3964 aAdr
.Col(), aAdr
.Row() - 1, aAdr
.Tab()))
3965 || (aAdr
.Row() < rDoc
.MaxRow() && rDoc
.GetRefCellValue(
3966 ScAddress( aAdr
.Col(), aAdr
.Row() + 1, aAdr
.Tab())).isEmpty()
3967 && aAdr
.Col() < rDoc
.MaxCol() && rDoc
.GetRefCellValue(
3968 ScAddress( aAdr
.Col() + 1, aAdr
.Row(), aAdr
.Tab())).hasNumeric()))
3969 aRef
.SetRowRel( true ); // RowName
3971 aRef
.SetColRel( true ); // ColName
3972 aRef
.SetAddress(rDoc
.GetSheetLimits(), aAdr
, aPos
);
3977 maRawToken
.SetSingleReference( aRef
);
3978 maRawToken
.eOp
= ocColRowName
;
3985 bool ScCompiler::ParseBoolean( const OUString
& rName
)
3987 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
) );
3988 if( iLook
!= mxSymbols
->getHashMap().end() &&
3989 ((*iLook
).second
== ocTrue
||
3990 (*iLook
).second
== ocFalse
) )
3992 maRawToken
.SetOpCode( (*iLook
).second
);
3999 bool ScCompiler::ParseErrorConstant( const OUString
& rName
)
4001 FormulaError nError
= GetErrorConstant( rName
);
4002 if (nError
!= FormulaError::NONE
)
4004 maRawToken
.SetErrorConstant( nError
);
4011 bool ScCompiler::ParseTableRefItem( const OUString
& rName
)
4014 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
4015 if (iLook
!= mxSymbols
->getHashMap().end())
4017 // Only called when there actually is a current TableRef, hence
4018 // accessing maTableRefs.back() is safe.
4019 ScTableRefToken
* p
= maTableRefs
.back().mxToken
.get();
4020 assert(p
); // not a ScTableRefToken can't be
4022 switch ((*iLook
).second
)
4024 case ocTableRefItemAll
:
4026 p
->AddItem( ScTableRefToken::ALL
);
4028 case ocTableRefItemHeaders
:
4030 p
->AddItem( ScTableRefToken::HEADERS
);
4032 case ocTableRefItemData
:
4034 p
->AddItem( ScTableRefToken::DATA
);
4036 case ocTableRefItemTotals
:
4038 p
->AddItem( ScTableRefToken::TOTALS
);
4040 case ocTableRefItemThisRow
:
4042 p
->AddItem( ScTableRefToken::THIS_ROW
);
4048 maRawToken
.SetOpCode( (*iLook
).second
);
4054 OUString
unescapeTableRefColumnSpecifier( const OUString
& rStr
)
4056 // '#', '[', ']' and '\'' are escaped with '\''
4058 if (rStr
.indexOf( '\'' ) < 0)
4061 const sal_Int32 n
= rStr
.getLength();
4062 OUStringBuffer
aBuf( n
);
4063 const sal_Unicode
* p
= rStr
.getStr();
4064 const sal_Unicode
* const pStop
= p
+ n
;
4065 bool bEscaped
= false;
4066 for ( ; p
< pStop
; ++p
)
4068 const sal_Unicode c
= *p
;
4075 bEscaped
= true; // unescaped escaping '\''
4079 return aBuf
.makeStringAndClear();
4083 bool ScCompiler::ParseTableRefColumn( const OUString
& rName
)
4085 // Only called when there actually is a current TableRef, hence
4086 // accessing maTableRefs.back() is safe.
4087 ScTableRefToken
* p
= maTableRefs
.back().mxToken
.get();
4088 assert(p
); // not a ScTableRefToken can't be
4090 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex( p
->GetIndex());
4094 OUString
aName( unescapeTableRefColumnSpecifier( rName
));
4097 pDBData
->GetArea( aRange
);
4098 aRange
.aEnd
.SetTab( aRange
.aStart
.Tab());
4099 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
4101 // Prefer the stored internal table column name, which is also needed for
4102 // named expressions during document load time when cell content isn't
4103 // available yet. Also, avoiding a possible calculation step in case the
4104 // header cell is a formula cell is "a good thing".
4105 sal_Int32 nOffset
= pDBData
->GetColumnNameOffset( aName
);
4108 // This is sneaky... we always use the top row of the database range,
4109 // regardless of whether it is a header row or not. Code evaluating
4110 // this reference must take that into account and may have to act
4111 // differently if it is a header-less table. Which are two places,
4112 // HandleTableRef() (no change necessary there) and
4113 // CreateStringFromSingleRef() (must not fallback to cell lookup).
4114 ScSingleRefData aRef
;
4115 ScAddress
aAdr( aRange
.aStart
);
4116 aAdr
.IncCol( nOffset
);
4117 aRef
.InitAddress( aAdr
);
4118 maRawToken
.SetSingleReference( aRef
);
4122 if (pDBData
->HasHeader())
4124 // Quite similar to IsColRowName() but limited to one row of headers.
4125 ScCellIterator
aIter( rDoc
, aRange
);
4126 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
4128 CellType eType
= aIter
.getType();
4130 if (eType
== CELLTYPE_FORMULA
)
4132 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
4133 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
4138 if (bOk
&& aIter
.hasString())
4140 OUString aStr
= aIter
.getString();
4141 if (ScGlobal::GetTransliteration().isEqual( aStr
, aName
))
4143 // If this is successful and the internal column name
4144 // lookup was not, it may be worth a warning.
4145 SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
4147 /* XXX NOTE: we could init the column as relative so copying a
4148 * formula across columns would point to the relative column,
4149 * but do it absolute because:
4150 * a) it makes the reference work in named expressions without
4151 * having to distinguish
4152 * b) Excel does it the same. */
4153 ScSingleRefData aRef
;
4154 aRef
.InitAddress( aIter
.GetPos());
4155 maRawToken
.SetSingleReference( aRef
);
4165 void ScCompiler::SetAutoCorrection( bool bVal
)
4167 assert(mbJumpCommandReorder
);
4168 bAutoCorrect
= bVal
;
4169 mbStopOnError
= !bVal
;
4172 void ScCompiler::AutoCorrectParsedSymbol()
4174 sal_Int32 nPos
= aCorrectedSymbol
.getLength();
4179 const sal_Unicode cQuote
= '\"';
4180 const sal_Unicode cx
= 'x';
4181 const sal_Unicode cX
= 'X';
4182 sal_Unicode c1
= aCorrectedSymbol
[0];
4183 sal_Unicode c2
= aCorrectedSymbol
[nPos
];
4184 sal_Unicode c2p
= nPos
> 0 ? aCorrectedSymbol
[nPos
-1] : 0;
4185 if ( c1
== cQuote
&& c2
!= cQuote
)
4187 // What's not a word doesn't belong to it.
4188 // Don't be pedantic: c < 128 should be sufficient here.
4189 while ( nPos
&& ((aCorrectedSymbol
[nPos
] < 128) &&
4190 ((GetCharTableFlags(aCorrectedSymbol
[nPos
], aCorrectedSymbol
[nPos
-1]) &
4191 (ScCharFlags::Word
| ScCharFlags::CharDontCare
)) == ScCharFlags::NONE
)) )
4193 if ( nPos
== MAXSTRLEN
- 1 )
4194 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
, 1, rtl::OUStringChar(cQuote
) ); // '"' the MAXSTRLENth character
4196 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
+ 1, 0, rtl::OUStringChar(cQuote
) );
4199 else if ( c1
!= cQuote
&& c2
== cQuote
)
4201 aCorrectedSymbol
= OUStringChar(cQuote
) + aCorrectedSymbol
;
4204 else if ( nPos
== 0 && (c1
== cx
|| c1
== cX
) )
4206 aCorrectedSymbol
= mxSymbols
->getSymbol(ocMul
);
4209 else if ( (GetCharTableFlags( c1
, 0 ) & ScCharFlags::CharValue
)
4210 && (GetCharTableFlags( c2
, c2p
) & ScCharFlags::CharValue
) )
4212 if ( aCorrectedSymbol
.indexOf(cx
) >= 0 ) // At least two tokens separated by cx
4214 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
4215 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUStringChar(cx
), OUStringChar(c
));
4218 if ( aCorrectedSymbol
.indexOf(cX
) >= 0 ) // At least two tokens separated by cX
4220 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
4221 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUStringChar(cX
), OUStringChar(c
));
4227 OUString
aSymbol( aCorrectedSymbol
);
4229 if ( aSymbol
[0] == '\'' )
4231 sal_Int32 nPosition
= aSymbol
.indexOf( "'#" );
4232 if (nPosition
!= -1)
4233 { // Split off 'Doc'#, may be d:\... or whatever
4234 aDoc
= aSymbol
.copy(0, nPosition
+ 2);
4235 aSymbol
= aSymbol
.copy(nPosition
+ 2);
4238 sal_Int32 nRefs
= comphelper::string::getTokenCount(aSymbol
, ':');
4241 { // duplicated or too many ':'? B:2::C10 => B2:C10
4243 sal_Int32 nIndex
= 0;
4244 OUString
aTmp1( aSymbol
.getToken( 0, ':', nIndex
) );
4245 sal_Int32 nLen1
= aTmp1
.getLength();
4246 OUStringBuffer aSym
;
4248 bool bLastAlp
= true;
4249 sal_Int32 nStrip
= 0;
4250 sal_Int32 nCount
= nRefs
;
4251 for ( sal_Int32 j
=1; j
<nCount
; j
++ )
4253 aTmp2
= aSymbol
.getToken( 0, ':', nIndex
);
4254 sal_Int32 nLen2
= aTmp2
.getLength();
4255 if ( nLen1
|| nLen2
)
4260 bLastAlp
= CharClass::isAsciiAlpha( aTmp1
);
4264 bool bNextNum
= CharClass::isAsciiNumeric( aTmp2
);
4265 if ( bLastAlp
== bNextNum
&& nStrip
< 1 )
4267 // Must be alternating number/string, only
4268 // strip within a reference.
4274 if ( !aSym
.isEmpty() && aSym
[aSym
.getLength()-1] != ':')
4278 bLastAlp
= !bNextNum
;
4284 { // B10::C10 ? append ':' on next round
4285 if ( !bLastAlp
&& !CharClass::isAsciiNumeric( aTmp1
) )
4295 aSymbol
= aSym
+ aTmp1
;
4300 if ( nRefs
&& nRefs
<= 2 )
4301 { // reference twisted? 4A => A4 etc.
4302 OUString aTab
[2], aRef
[2];
4303 const ScAddress::Details
aDetails( pConv
->meConv
, aPos
);
4306 sal_Int32 nIdx
{ 0 };
4307 aRef
[0] = aSymbol
.getToken( 0, ':', nIdx
);
4308 aRef
[1] = aSymbol
.getToken( 0, ':', nIdx
);
4313 bool bChanged
= false;
4315 ScRefFlags nMask
= ScRefFlags::VALID
| ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
;
4316 for ( int j
=0; j
<nRefs
; j
++ )
4319 sal_Int32 nDotPos
= -1;
4320 while ( (nTmp
= aRef
[j
].indexOf( '.', nTmp
)) != -1 )
4321 nDotPos
= nTmp
++; // the last one counts
4322 if ( nDotPos
!= -1 )
4324 aTab
[j
] = aRef
[j
].copy( 0, nDotPos
+ 1 ); // with '.'
4325 aRef
[j
] = aRef
[j
].copy( nDotPos
+ 1 );
4327 OUString
aOld( aRef
[j
] );
4328 OUStringBuffer aStr2
;
4329 const sal_Unicode
* p
= aRef
[j
].getStr();
4330 while ( *p
&& rtl::isAsciiDigit( *p
) )
4332 aRef
[j
] = OUString( p
);
4334 if ( bColons
|| aRef
[j
] != aOld
)
4338 bOk
&= ((aAdr
.Parse( aRef
[j
], rDoc
, aDetails
) & nMask
) == nMask
);
4341 if ( bChanged
&& bOk
)
4343 aCorrectedSymbol
= aDoc
;
4344 aCorrectedSymbol
+= aTab
[0];
4345 aCorrectedSymbol
+= aRef
[0];
4348 aCorrectedSymbol
+= ":";
4349 aCorrectedSymbol
+= aTab
[1];
4350 aCorrectedSymbol
+= aRef
[1];
4358 bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString
& rUpper
, const OUString
& rOrg
) const
4360 if (FormulaGrammar::isODFF( meGrammar
) || FormulaGrammar::isOOXML( meGrammar
))
4362 // ODFF and OOXML have defined sets of English function names, avoid
4364 rUpper
= rOrg
.toAsciiUpperCase();
4369 // One of localized or English.
4370 rUpper
= pCharClass
->uppercase(rOrg
);
4375 short ScCompiler::GetPossibleParaCount( const std::u16string_view
& rLambdaFormula
) const
4377 sal_Unicode cSep
= mxSymbols
->getSymbolChar(ocSep
);
4378 sal_Unicode cOpen
= mxSymbols
->getSymbolChar(ocOpen
);
4379 sal_Unicode cClose
= mxSymbols
->getSymbolChar(ocClose
);
4380 sal_Unicode cArrayOpen
= mxSymbols
->getSymbolChar(ocArrayOpen
);
4381 sal_Unicode cArrayClose
= mxSymbols
->getSymbolChar(ocArrayClose
);
4382 short nBrackets
= 0;
4384 short nCount
= std::count_if(rLambdaFormula
.begin(), rLambdaFormula
.end(),
4385 [&](sal_Unicode c
) {
4386 if (c
== cOpen
|| c
== cArrayOpen
|| c
== '[') {
4390 else if (c
== cClose
|| c
== cArrayClose
|| c
== ']') {
4402 return static_cast<short>(nCount
+ 1);
4405 bool ScCompiler::NextNewToken( bool bInArray
)
4407 if (!maPendingOpCodes
.empty())
4409 maRawToken
.SetOpCode(maPendingOpCodes
.front());
4410 maPendingOpCodes
.pop();
4414 bool bAllowBooleans
= bInArray
;
4415 const std::vector
<Whitespace
> & vSpaces
= NextSymbol(bInArray
);
4419 if (nSrcPos
< aFormula
.getLength())
4421 // Nothing could be parsed, remainder as bad string.
4422 // NextSymbol() must had set an error for this.
4423 assert( pArr
->GetCodeError() != FormulaError::NONE
);
4424 const OUString
aBad( aFormula
.copy( nSrcPos
));
4425 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern( aBad
);
4426 maRawToken
.SetString( aSS
.getData(), aSS
.getDataIgnoreCase());
4427 maRawToken
.NewOpCode( ocBad
);
4428 nSrcPos
= aFormula
.getLength();
4429 // Add bad string as last token.
4435 if (!vSpaces
.empty())
4438 for (const auto& rSpace
: vSpaces
)
4440 if (rSpace
.cChar
== 0x20)
4442 // For now keep this a FormulaByteToken for the nasty
4443 // significant whitespace intersection. This probably can be
4444 // changed to a FormulaSpaceToken but then other places may
4445 // need to be adapted.
4446 aToken
.SetOpCode( ocSpaces
);
4447 aToken
.sbyte
.cByte
= static_cast<sal_uInt8
>( std::min
<sal_Int32
>(rSpace
.nCount
, 255) );
4451 aToken
.SetOpCode( ocWhitespace
);
4452 aToken
.whitespace
.nCount
= static_cast<sal_uInt8
>( std::min
<sal_Int32
>(rSpace
.nCount
, 255) );
4453 aToken
.whitespace
.cChar
= rSpace
.cChar
;
4455 if (!static_cast<ScTokenArray
*>(pArr
)->AddRawToken( aToken
))
4457 SetError(FormulaError::CodeOverflow
);
4463 // Short cut for references when reading ODF to speedup things.
4464 if (mnPredetectedReference
)
4466 OUString
aStr( cSymbol
);
4467 bool bInvalidExternalNameRange
;
4468 if (!ParsePredetectedReference( aStr
) && !ParseExternalNamedRange( aStr
, bInvalidExternalNameRange
))
4470 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aStr
);
4471 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4472 maRawToken
.NewOpCode( ocBad
);
4477 if ( (cSymbol
[0] == '#' || cSymbol
[0] == '$') && cSymbol
[1] == 0 &&
4479 { // special case to speed up broken [$]#REF documents
4480 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4481 * be processed as usual. That would need some special treatment,
4482 * also in NextSymbol() because of possible combinations of
4483 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4484 * handled by IsPredetectedReference(), this case here remains for
4485 * manual/API input. */
4486 OUString
aBad( aFormula
.copy( nSrcPos
-1 ) );
4487 const FormulaToken
* pBadToken
= pArr
->AddBad(aBad
);
4488 eLastOp
= pBadToken
? pBadToken
->GetOpCode() : ocNone
;
4495 bool bMayBeFuncName
;
4496 bool bAsciiNonAlnum
; // operators, separators, ...
4497 if ( cSymbol
[0] < 128 )
4499 bMayBeFuncName
= rtl::isAsciiAlpha( cSymbol
[0] );
4500 if (!bMayBeFuncName
&& (cSymbol
[0] == '_' && cSymbol
[1] == '_') && !comphelper::IsFuzzing())
4502 bMayBeFuncName
= officecfg::Office::Common::Misc::ExperimentalMode::get();
4505 bAsciiNonAlnum
= !bMayBeFuncName
&& !rtl::isAsciiDigit( cSymbol
[0] );
4509 OUString
aTmpStr( cSymbol
[0] );
4510 bMayBeFuncName
= pCharClass
->isLetter( aTmpStr
, 0 );
4511 bAsciiNonAlnum
= false;
4514 // Within a TableRef anything except an unescaped '[' or ']' is an item
4515 // or a column specifier, do not attempt to recognize any other single
4516 // operator there so even [,] or [+] for a single character column
4517 // specifier works. Note that space between two ocTableRefOpen is not
4518 // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
4519 // without any item or column specifier is valid.
4520 if (bAsciiNonAlnum
&& cSymbol
[1] == 0 && (eLastOp
!= ocTableRefOpen
|| cSymbol
[0] == '[' || cSymbol
[0] == ']'))
4522 // Shortcut for operators and separators that need no further checks or upper.
4523 if (ParseOpCode( OUString( cSymbol
), bInArray
))
4527 if ( bMayBeFuncName
)
4529 // a function name must be followed by a parenthesis
4530 const sal_Unicode
* p
= aFormula
.getStr() + nSrcPos
;
4533 bMayBeFuncName
= ( *p
== '(' );
4536 // Italian ARCTAN.2 resulted in #REF! => ParseOpcode() before
4537 // ParseReference().
4540 bool bAsciiUpper
= false;
4546 const OUString
aOrg( cSymbol
);
4548 // Check for TableRef column specifier first, it may be anything.
4549 if (cSymbol
[0] != '#' && !maTableRefs
.empty() && maTableRefs
.back().mnLevel
)
4551 if (ParseTableRefColumn( aOrg
))
4553 // Do not attempt to resolve as any other name.
4554 aUpper
= aOrg
; // for ocBad
4555 break; // do; create ocBad token or set error.
4560 bAsciiUpper
= false;
4564 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4565 if (cSymbol
[0] == '#')
4567 // Check for TableRef item specifiers first.
4569 if (!maTableRefs
.empty() && ((nLevel
= maTableRefs
.back().mnLevel
) == 2 || nLevel
== 1))
4571 if (ParseTableRefItem( aUpper
))
4575 // This can be either an error constant ...
4576 if (ParseErrorConstant( aUpper
))
4579 // ... or some invalidated reference starting with #REF!
4580 // which is handled after the do loop.
4582 break; // do; create ocBad token or set error.
4584 if (ParseOpCode( aUpper
, bInArray
))
4590 if (aUpper
.isEmpty())
4591 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4592 if (ParseOpCode( aUpper
, bInArray
))
4596 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4597 // referred => ParseReference() before ParseValue().
4598 // Preserve case of file names in external references.
4599 if (ParseReference( aOrg
))
4601 if (mbRewind
) // Range operator, but no direct reference.
4602 continue; // do; up to range operator.
4603 // If a syntactically correct reference was recognized but invalid
4604 // e.g. because of non-existing sheet name => entire reference
4605 // ocBad to preserve input instead of #REF!.A1
4606 if (!maRawToken
.IsValidReference(rDoc
))
4608 aUpper
= aOrg
; // ensure for ocBad
4609 break; // do; create ocBad token or set error.
4614 if (aUpper
.isEmpty())
4615 bAsciiUpper
= ToUpperAsciiOrI18nIsAscii( aUpper
, aOrg
);
4617 // ParseBoolean() before ParseValue() to catch inline bools without the kludge
4618 // for inline arrays.
4619 if (bAllowBooleans
&& ParseBoolean( aUpper
))
4622 if (ParseValue( aUpper
))
4625 // User defined names and such do need i18n upper also in ODF.
4626 if (bAsciiUpper
|| mbCharClassesDiffer
)
4628 // Use current system locale here because user defined symbols are
4629 // more likely in that localized language than in the formula
4630 // language. This in corner cases needs to continue to work for
4631 // existing documents and environments.
4632 // Do not change bAsciiUpper from here on for the lowercase() call
4633 // below in the ocBad case to use the correct CharClass.
4634 aUpper
= ScGlobal::getCharClass().uppercase( aOrg
);
4637 if (ParseNamedRange( aUpper
))
4640 // Compiling a named expression during collecting them in import shall
4641 // not match arbitrary names that otherwise if all named expressions
4642 // were present would be recognized as named expression. Such name will
4643 // flag an error below and will be recompiled in a second step later
4644 // with ScRangeData::CompileUnresolvedXML()
4645 if (meExtendedErrorDetection
== EXTENDED_ERROR_DETECTION_NAME_NO_BREAK
&& rDoc
.IsImportingXML())
4648 // Preserve case of file names in external references.
4649 bool bInvalidExternalNameRange
;
4650 if (ParseExternalNamedRange( aOrg
, bInvalidExternalNameRange
))
4652 // Preserve case of file names in external references even when range
4653 // is not valid and previous check failed tdf#89330
4654 if (bInvalidExternalNameRange
)
4656 // add ocBad but do not lowercase
4657 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aOrg
);
4658 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4659 maRawToken
.NewOpCode( ocBad
);
4662 if (ParseDBRange( aUpper
))
4664 // If followed by '(' (with or without space inbetween) it can not be a
4665 // column/row label. Prevent arbitrary content detection.
4666 if (!bMayBeFuncName
&& ParseColRowName( aUpper
))
4668 if (bMayBeFuncName
&& ParseMacro( aUpper
))
4670 if (bMayBeFuncName
&& ParseOpCode2( aUpper
))
4673 if (ParseLambdaFuncName( aOrg
))
4678 // Last chance: it could be a broken invalidated reference that contains
4679 // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4680 // and 2016 until 5.1.4
4681 OUString
aErrRef( mxSymbols
->getSymbol( ocErrRef
));
4682 if (aUpper
.indexOf( aErrRef
) >= 0 && ParseReference( aUpper
, &aErrRef
))
4689 if ( meExtendedErrorDetection
!= EXTENDED_ERROR_DETECTION_NONE
)
4692 SetError( FormulaError::NoName
);
4693 if (meExtendedErrorDetection
== EXTENDED_ERROR_DETECTION_NAME_BREAK
)
4694 return false; // end compilation
4697 // Provide single token information and continue. Do not set an error, that
4698 // would prematurely end compilation. Simple unknown names are handled by
4700 // Use the same CharClass that was used for uppercase.
4701 aUpper
= ((bAsciiUpper
|| mbCharClassesDiffer
) ? ScGlobal::getCharClass() : *pCharClass
).lowercase( aUpper
);
4702 svl::SharedString aSS
= rDoc
.GetSharedStringPool().intern(aUpper
);
4703 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
4704 maRawToken
.NewOpCode( ocBad
);
4706 AutoCorrectParsedSymbol();
4710 void ScCompiler::CreateStringFromXMLTokenArray( OUString
& rFormula
, OUString
& rFormulaNmsp
)
4712 bool bExternal
= GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
;
4713 sal_uInt16 nExpectedCount
= bExternal
? 2 : 1;
4714 OSL_ENSURE( pArr
->GetLen() == nExpectedCount
, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4715 if( pArr
->GetLen() == nExpectedCount
)
4717 FormulaToken
** ppTokens
= pArr
->GetArray();
4718 // string tokens expected, GetString() will assert if token type is wrong
4719 rFormula
= ppTokens
[0]->GetString().getString();
4721 rFormulaNmsp
= ppTokens
[1]->GetString().getString();
4727 class ExternalFileInserter
4730 ScExternalRefManager
& mrRefMgr
;
4732 ExternalFileInserter(const ScAddress
& rPos
, ScExternalRefManager
& rRefMgr
) :
4733 maPos(rPos
), mrRefMgr(rRefMgr
) {}
4735 void operator() (sal_uInt16 nFileId
) const
4737 mrRefMgr
.insertRefCell(nFileId
, maPos
);
4743 std::unique_ptr
<ScTokenArray
> ScCompiler::CompileString( const OUString
& rFormula
)
4745 OSL_ENSURE( meGrammar
!= FormulaGrammar::GRAM_EXTERNAL
, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4746 if( meGrammar
== FormulaGrammar::GRAM_EXTERNAL
)
4747 SetGrammar( FormulaGrammar::GRAM_PODF
);
4749 ScTokenArray
aArr(rDoc
);
4751 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
4752 aFormula
= comphelper::string::strip(rFormula
, ' ');
4758 aCorrectedFormula
.clear();
4759 aCorrectedSymbol
.clear();
4761 sal_uInt8 nForced
= 0; // ==formula forces recalc even if cell is not visible
4762 if( nSrcPos
< aFormula
.getLength() && aFormula
[nSrcPos
] == '=' )
4767 aCorrectedFormula
+= "=";
4769 if( nSrcPos
< aFormula
.getLength() && aFormula
[nSrcPos
] == '=' )
4774 aCorrectedFormula
+= "=";
4776 struct FunctionStack
4781 // FunctionStack only used if PODF or OOXML!
4782 bool bPODF
= FormulaGrammar::isPODF( meGrammar
);
4783 bool bOOXML
= FormulaGrammar::isOOXML( meGrammar
);
4784 bool bUseFunctionStack
= (bPODF
|| bOOXML
);
4785 const size_t nAlloc
= 512;
4786 FunctionStack aFuncs
[ nAlloc
];
4787 FunctionStack
* pFunctionStack
= (bUseFunctionStack
&& o3tl::make_unsigned(rFormula
.getLength()) > nAlloc
?
4788 new FunctionStack
[rFormula
.getLength()] : &aFuncs
[0]);
4789 pFunctionStack
[0].eOp
= ocNone
;
4790 pFunctionStack
[0].nSep
= 0;
4791 size_t nFunction
= 0;
4792 size_t nHighWatermark
= 0;
4793 short nBrackets
= 0;
4794 bool bInArray
= false;
4796 while( NextNewToken( bInArray
) )
4798 const OpCode eOp
= maRawToken
.GetOpCode();
4806 if (eLastOp
== ocLet
)
4808 m_aLambda
.bInLambdaFunction
= true;
4809 m_aLambda
.nBracketPos
= nBrackets
;
4810 m_aLambda
.nParaPos
++;
4811 m_aLambda
.nParaCount
= GetPossibleParaCount(rFormula
.subView(nSrcPos
- 1));
4815 if (bUseFunctionStack
)
4818 pFunctionStack
[ nFunction
].eOp
= eLastOp
;
4819 pFunctionStack
[ nFunction
].nSep
= 0;
4820 nHighWatermark
= nFunction
;
4828 SetError( FormulaError::PairExpected
);
4832 aCorrectedSymbol
.clear();
4838 if (m_aLambda
.bInLambdaFunction
&& m_aLambda
.nBracketPos
== nBrackets
)
4840 m_aLambda
.bInLambdaFunction
= false;
4841 m_aLambda
.nBracketPos
= nBrackets
;
4844 if (bUseFunctionStack
&& nFunction
)
4850 if (bUseFunctionStack
)
4851 ++pFunctionStack
[ nFunction
].nSep
;
4853 if (m_aLambda
.bInLambdaFunction
&& m_aLambda
.nBracketPos
+ 1 == nBrackets
)
4854 m_aLambda
.nParaPos
++;
4860 SetError( FormulaError::NestedArray
);
4863 // Don't count following column separator as parameter separator.
4864 if (bUseFunctionStack
)
4867 pFunctionStack
[ nFunction
].eOp
= eOp
;
4868 pFunctionStack
[ nFunction
].nSep
= 0;
4869 nHighWatermark
= nFunction
;
4881 SetError( FormulaError::PairExpected
);
4885 aCorrectedSymbol
.clear();
4888 if (bUseFunctionStack
&& nFunction
)
4892 case ocTableRefOpen
:
4894 // Don't count following item separator as parameter separator.
4895 if (bUseFunctionStack
)
4898 pFunctionStack
[ nFunction
].eOp
= eOp
;
4899 pFunctionStack
[ nFunction
].nSep
= 0;
4900 nHighWatermark
= nFunction
;
4904 case ocTableRefClose
:
4906 if (bUseFunctionStack
&& nFunction
)
4911 case ocColRowNameAuto
:
4912 // The current implementation of column / row labels doesn't
4913 // function correctly in grouped cells.
4914 aArr
.SetShareable(false);
4919 if ((eLastOp
!= ocOpen
|| eOp
!= ocClose
) &&
4920 (eLastOp
== ocOpen
||
4922 eLastOp
== ocArrayRowSep
||
4923 eLastOp
== ocArrayColSep
||
4924 eLastOp
== ocArrayOpen
) &&
4927 eOp
== ocArrayRowSep
||
4928 eOp
== ocArrayColSep
||
4929 eOp
== ocArrayClose
))
4931 // TODO: should we check for known functions with optional empty
4932 // args so the correction dialog can do better?
4933 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaMissingToken
) )
4935 SetError(FormulaError::CodeOverflow
); break;
4940 // Append a parameter for WEEKNUM, all 1.0
4941 // Function is already closed, parameter count is nSep+1
4942 size_t nFunc
= nFunction
+ 1;
4943 if (eOp
== ocClose
&& nFunc
<= nHighWatermark
&&
4944 pFunctionStack
[ nFunc
].nSep
== 0 &&
4945 pFunctionStack
[ nFunc
].eOp
== ocWeek
) // 2nd week start
4947 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaToken( svSep
, ocSep
)) ||
4948 !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaDoubleToken( 1.0)))
4950 SetError(FormulaError::CodeOverflow
); break;
4956 /* TODO: for now this is the only PODF adapter. If there were more,
4957 * factor this out. */
4958 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4960 pFunctionStack
[ nFunction
].eOp
== ocAddress
&&
4961 pFunctionStack
[ nFunction
].nSep
== 3)
4963 if ( !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaToken( svSep
, ocSep
)) ||
4964 !static_cast<ScTokenArray
*>(pArr
)->Add( new FormulaDoubleToken( 1.0)))
4966 SetError(FormulaError::CodeOverflow
); break;
4968 ++pFunctionStack
[ nFunction
].nSep
;
4971 FormulaToken
* pNewToken
= static_cast<ScTokenArray
*>(pArr
)->Add( maRawToken
.CreateToken(rDoc
.GetSheetLimits()));
4972 if (!pNewToken
&& eOp
== ocArrayClose
&& pArr
->OpCodeBefore( pArr
->GetLen()) == ocArrayClose
)
4974 // Nested inline array or non-value/non-string in array. The
4975 // original tokens are still in the ScTokenArray and not merged
4976 // into an ScMatrixToken. Set error but keep on tokenizing.
4977 SetError( FormulaError::BadArrayContent
);
4979 else if (!pNewToken
)
4981 SetError(FormulaError::CodeOverflow
);
4984 else if (eLastOp
== ocRange
&& pNewToken
->GetOpCode() == ocPush
&& pNewToken
->GetType() == svSingleRef
)
4986 static_cast<ScTokenArray
*>(pArr
)->MergeRangeReference( aPos
);
4988 else if (eLastOp
== ocDBArea
&& pNewToken
->GetOpCode() == ocTableRefOpen
)
4990 sal_uInt16 nIdx
= pArr
->GetLen() - 1;
4991 const FormulaToken
* pPrev
= pArr
->PeekPrev( nIdx
);
4992 if (pPrev
&& pPrev
->GetOpCode() == ocDBArea
)
4994 ScTableRefToken
* pTableRefToken
= new ScTableRefToken( pPrev
->GetIndex(), ScTableRefToken::TABLE
);
4995 maTableRefs
.emplace_back( pTableRefToken
);
4996 // pPrev may be dead hereafter.
4997 static_cast<ScTokenArray
*>(pArr
)->ReplaceToken( nIdx
, pTableRefToken
,
4998 FormulaTokenArray::ReplaceMode::CODE_ONLY
);
5003 case ocTableRefOpen
:
5004 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
5005 if (maTableRefs
.empty())
5006 SetError(FormulaError::Pair
);
5008 ++maTableRefs
.back().mnLevel
;
5010 case ocTableRefClose
:
5011 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
5012 if (maTableRefs
.empty())
5013 SetError(FormulaError::Pair
);
5016 if (--maTableRefs
.back().mnLevel
== 0)
5017 maTableRefs
.pop_back();
5023 eLastOp
= maRawToken
.GetOpCode();
5025 aCorrectedFormula
+= aCorrectedSymbol
;
5027 if ( mbCloseBrackets
)
5031 FormulaByteToken
aToken( ocArrayClose
);
5032 if( !pArr
->AddToken( aToken
) )
5034 SetError(FormulaError::CodeOverflow
);
5036 else if ( bAutoCorrect
)
5037 aCorrectedFormula
+= mxSymbols
->getSymbol(ocArrayClose
);
5042 FormulaToken
aToken( svSep
, ocClose
);
5043 while( nBrackets
-- )
5045 if( !pArr
->AddToken( aToken
) )
5047 SetError(FormulaError::CodeOverflow
);
5051 aCorrectedFormula
+= mxSymbols
->getSymbol(ocClose
);
5056 pArr
->SetRecalcModeForced();
5058 if (pFunctionStack
!= &aFuncs
[0])
5059 delete [] pFunctionStack
;
5061 // remember pArr, in case a subsequent CompileTokenArray() is executed.
5062 std::unique_ptr
<ScTokenArray
> pNew(new ScTokenArray( aArr
));
5064 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
5066 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
5068 if (!maExternalFiles
.empty())
5070 // Remove duplicates, and register all external files found in this cell.
5071 std::sort(maExternalFiles
.begin(), maExternalFiles
.end());
5072 std::vector
<sal_uInt16
>::iterator itEnd
= std::unique(maExternalFiles
.begin(), maExternalFiles
.end());
5073 std::for_each(maExternalFiles
.begin(), itEnd
, ExternalFileInserter(aPos
, *rDoc
.GetExternalRefManager()));
5074 maExternalFiles
.erase(itEnd
, maExternalFiles
.end());
5080 std::unique_ptr
<ScTokenArray
> ScCompiler::CompileString( const OUString
& rFormula
, const OUString
& rFormulaNmsp
)
5082 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
) || rFormulaNmsp
.isEmpty(),
5083 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
5084 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL
) try
5086 ScFormulaParserPool
& rParserPool
= rDoc
.GetFormulaParserPool();
5087 uno::Reference
< sheet::XFormulaParser
> xParser( rParserPool
.getFormulaParser( rFormulaNmsp
), uno::UNO_SET_THROW
);
5088 table::CellAddress aReferencePos
;
5089 ScUnoConversion::FillApiAddress( aReferencePos
, aPos
);
5090 uno::Sequence
< sheet::FormulaToken
> aTokenSeq
= xParser
->parseFormula( rFormula
, aReferencePos
);
5091 ScTokenArray
aTokenArray(rDoc
);
5092 if( ScTokenConversion::ConvertToTokenArray( rDoc
, aTokenArray
, aTokenSeq
) )
5094 // remember pArr, in case a subsequent CompileTokenArray() is executed.
5095 std::unique_ptr
<ScTokenArray
> pNew(new ScTokenArray( aTokenArray
));
5096 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
5098 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
5102 catch( uno::Exception
& )
5105 // no success - fallback to some internal grammar and hope the best
5106 return CompileString( rFormula
);
5109 ScRangeData
* ScCompiler::GetRangeData( const FormulaToken
& rToken
) const
5111 return rDoc
.FindRangeNameBySheetAndIndex( rToken
.GetSheet(), rToken
.GetIndex());
5114 bool ScCompiler::HandleStringName()
5116 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5117 pNew
->AddString(mpToken
->GetString());
5118 PushTokenArray(pNew
, true);
5122 bool ScCompiler::HandleRange()
5125 const ScRangeData
* pRangeData
= GetRangeData( *mpToken
);
5128 FormulaError nErr
= pRangeData
->GetErrCode();
5129 if( nErr
!= FormulaError::NONE
)
5131 else if (mbJumpCommandReorder
)
5133 // put named formula into parentheses.
5134 // But only if there aren't any yet, parenthetical
5135 // ocSep doesn't work, e.g. SUM((...;...))
5136 // or if not directly between ocSep/parenthesis,
5137 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
5138 // in short: if it isn't a self-contained expression.
5139 FormulaToken
* p1
= maArrIterator
.PeekPrevNoSpaces();
5140 FormulaToken
* p2
= maArrIterator
.PeekNextNoSpaces();
5141 OpCode eOp1
= (p1
? p1
->GetOpCode() : ocSep
);
5142 OpCode eOp2
= (p2
? p2
->GetOpCode() : ocSep
);
5143 bool bBorder1
= (eOp1
== ocSep
|| eOp1
== ocOpen
);
5144 bool bBorder2
= (eOp2
== ocSep
|| eOp2
== ocClose
);
5145 bool bAddPair
= !(bBorder1
&& bBorder2
);
5148 pNew
= new ScTokenArray(rDoc
);
5149 pNew
->AddOpCode( ocClose
);
5150 PushTokenArray( pNew
, true );
5152 pNew
= pRangeData
->GetCode()->Clone().release();
5153 pNew
->SetFromRangeName( true );
5154 PushTokenArray( pNew
, true );
5155 if( pRangeData
->HasReferences() )
5157 // Relative sheet references in sheet-local named expressions
5158 // shall still point to the same sheet as if used on the
5159 // original sheet, not shifted to the current position where
5161 SCTAB nSheetTab
= mpToken
->GetSheet();
5162 if (nSheetTab
>= 0 && nSheetTab
!= aPos
.Tab())
5163 AdjustSheetLocalNameRelReferences( nSheetTab
- aPos
.Tab());
5165 SetRelNameReference();
5168 maArrIterator
.Reset();
5171 pNew
= new ScTokenArray(rDoc
);
5172 pNew
->AddOpCode( ocOpen
);
5173 PushTokenArray( pNew
, true );
5180 // No ScRangeData for an already compiled token can happen in BIFF .xls
5181 // import if the original range is not present in the document.
5182 pNew
= new ScTokenArray(rDoc
);
5183 pNew
->Add( new FormulaErrorToken( FormulaError::NoName
));
5184 PushTokenArray( pNew
, true );
5190 bool ScCompiler::HandleExternalReference(const FormulaToken
& _aToken
)
5192 // Handle external range names.
5193 switch (_aToken
.GetType())
5195 case svExternalSingleRef
:
5196 case svExternalDoubleRef
:
5198 case svExternalName
:
5200 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
5201 const OUString
* pFile
= pRefMgr
->getExternalFileName(_aToken
.GetIndex());
5204 SetError(FormulaError::NoName
);
5208 OUString aName
= _aToken
.GetString().getString();
5209 ScExternalRefCache::TokenArrayRef xNew
= pRefMgr
->getRangeNameTokens(
5210 _aToken
.GetIndex(), aName
, &aPos
);
5214 SetError(FormulaError::NoName
);
5218 ScTokenArray
* pNew
= xNew
->Clone().release();
5219 PushTokenArray( pNew
, true);
5220 if (FormulaTokenArrayPlainIterator(*pNew
).GetNextReference() != nullptr)
5222 SetRelNameReference();
5225 maArrIterator
.Reset();
5229 OSL_FAIL("Wrong type for external reference!");
5235 void ScCompiler::AdjustSheetLocalNameRelReferences( SCTAB nDelta
)
5237 for ( auto t
: pArr
->References() )
5239 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
5240 if (rRef1
.IsTabRel())
5241 rRef1
.IncTab( nDelta
);
5242 if ( t
->GetType() == svDoubleRef
)
5244 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
5245 if (rRef2
.IsTabRel())
5246 rRef2
.IncTab( nDelta
);
5251 // reference of named range with relative references
5253 void ScCompiler::SetRelNameReference()
5255 for ( auto t
: pArr
->References() )
5257 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
5258 if ( rRef1
.IsColRel() || rRef1
.IsRowRel() || rRef1
.IsTabRel() )
5259 rRef1
.SetRelName( true );
5260 if ( t
->GetType() == svDoubleRef
)
5262 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
5263 if ( rRef2
.IsColRel() || rRef2
.IsRowRel() || rRef2
.IsTabRel() )
5264 rRef2
.SetRelName( true );
5269 // Wrap-adjust relative references of a RangeName to current position,
5270 // don't call for other token arrays!
5271 void ScCompiler::MoveRelWrap()
5273 for ( auto t
: pArr
->References() )
5275 if ( t
->GetType() == svSingleRef
|| t
->GetType() == svExternalSingleRef
)
5276 ScRefUpdate::MoveRelWrap( rDoc
, aPos
, rDoc
.MaxCol(), rDoc
.MaxRow(), SingleDoubleRefModifier( *t
->GetSingleRef() ).Ref() );
5278 ScRefUpdate::MoveRelWrap( rDoc
, aPos
, rDoc
.MaxCol(), rDoc
.MaxRow(), *t
->GetDoubleRef() );
5282 // Wrap-adjust relative references of a RangeName to current position,
5283 // don't call for other token arrays!
5284 void ScCompiler::MoveRelWrap( const ScTokenArray
& rArr
, const ScDocument
& rDoc
, const ScAddress
& rPos
,
5285 SCCOL nMaxCol
, SCROW nMaxRow
)
5287 for ( auto t
: rArr
.References() )
5289 if ( t
->GetType() == svSingleRef
|| t
->GetType() == svExternalSingleRef
)
5290 ScRefUpdate::MoveRelWrap( rDoc
, rPos
, nMaxCol
, nMaxRow
, SingleDoubleRefModifier( *t
->GetSingleRef() ).Ref() );
5292 ScRefUpdate::MoveRelWrap( rDoc
, rPos
, nMaxCol
, nMaxRow
, *t
->GetDoubleRef() );
5296 bool ScCompiler::IsCharFlagAllConventions(
5297 OUString
const & rStr
, sal_Int32 nPos
, ScCharFlags nFlags
)
5299 sal_Unicode c
= rStr
[ nPos
];
5300 sal_Unicode cLast
= nPos
> 0 ? rStr
[ nPos
-1 ] : 0;
5303 for ( int nConv
= formula::FormulaGrammar::CONV_UNSPECIFIED
;
5304 ++nConv
< formula::FormulaGrammar::CONV_LAST
; )
5306 if (pConventions
[nConv
] &&
5307 ((pConventions
[nConv
]->getCharTableFlags(c
, cLast
) & nFlags
) != nFlags
))
5309 // convention not known => assume valid
5314 return ScGlobal::getCharClass().isLetterNumeric( rStr
, nPos
);
5317 void ScCompiler::CreateStringFromExternal( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
) const
5319 const FormulaToken
* t
= pTokenP
;
5320 sal_uInt16 nFileId
= t
->GetIndex();
5321 ScExternalRefManager
* pRefMgr
= rDoc
.GetExternalRefManager();
5322 sal_uInt16 nUsedFileId
= pRefMgr
->convertFileIdToUsedFileId(nFileId
);
5323 const OUString
* pFileName
= pRefMgr
->getExternalFileName(nFileId
);
5327 switch (t
->GetType())
5329 case svExternalName
:
5330 rBuffer
.append(pConv
->makeExternalNameStr( nFileId
, *pFileName
, t
->GetString().getString()));
5332 case svExternalSingleRef
:
5333 pConv
->makeExternalRefStr(rDoc
.GetSheetLimits(),
5334 rBuffer
, GetPos(), nUsedFileId
, *pFileName
, t
->GetString().getString(),
5335 *t
->GetSingleRef());
5337 case svExternalDoubleRef
:
5339 vector
<OUString
> aTabNames
;
5340 pRefMgr
->getAllCachedTableNames(nFileId
, aTabNames
);
5341 // No sheet names is a valid case if external sheets were not
5342 // cached in this document and external document is not reachable,
5343 // else not and worth to be investigated.
5344 SAL_WARN_IF( aTabNames
.empty(), "sc.core", "wrecked cache of external document? '" <<
5345 *pFileName
<< "' '" << t
->GetString().getString() << "'");
5347 pConv
->makeExternalRefStr(
5348 rDoc
.GetSheetLimits(), rBuffer
, GetPos(), nUsedFileId
, *pFileName
, aTabNames
, t
->GetString().getString(),
5349 *t
->GetDoubleRef());
5353 // warning, not error, otherwise we may end up with a never
5354 // ending message box loop if this was the cursor cell to be redrawn.
5355 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
5359 void ScCompiler::CreateStringFromMatrix( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
) const
5361 const ScMatrix
* pMatrix
= pTokenP
->GetMatrix();
5362 SCSIZE nC
, nMaxC
, nR
, nMaxR
;
5364 pMatrix
->GetDimensions( nMaxC
, nMaxR
);
5366 rBuffer
.append( mxSymbols
->getSymbol(ocArrayOpen
) );
5367 for( nR
= 0 ; nR
< nMaxR
; nR
++)
5371 rBuffer
.append( mxSymbols
->getSymbol(ocArrayRowSep
) );
5374 for( nC
= 0 ; nC
< nMaxC
; nC
++)
5378 rBuffer
.append( mxSymbols
->getSymbol(ocArrayColSep
) );
5381 if( pMatrix
->IsValue( nC
, nR
) )
5383 if (pMatrix
->IsBoolean(nC
, nR
))
5384 AppendBoolean(rBuffer
, pMatrix
->GetDouble(nC
, nR
) != 0.0);
5387 FormulaError nErr
= pMatrix
->GetError(nC
, nR
);
5388 if (nErr
!= FormulaError::NONE
)
5389 rBuffer
.append(ScGlobal::GetErrorString(nErr
));
5391 AppendDouble(rBuffer
, pMatrix
->GetDouble(nC
, nR
));
5394 else if( pMatrix
->IsEmpty( nC
, nR
) )
5396 else if( pMatrix
->IsStringOrEmpty( nC
, nR
) )
5397 AppendString( rBuffer
, pMatrix
->GetString(nC
, nR
).getString() );
5400 rBuffer
.append( mxSymbols
->getSymbol(ocArrayClose
) );
5404 void escapeTableRefColumnSpecifier( OUString
& rStr
)
5406 const sal_Int32 n
= rStr
.getLength();
5407 OUStringBuffer
aBuf( n
* 2 );
5408 const sal_Unicode
* p
= rStr
.getStr();
5409 const sal_Unicode
* const pStop
= p
+ n
;
5410 for ( ; p
< pStop
; ++p
)
5412 const sal_Unicode c
= *p
;
5419 aBuf
.append( '\'' );
5426 rStr
= aBuf
.makeStringAndClear();
5430 void ScCompiler::CreateStringFromSingleRef( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5432 const FormulaToken
* p
;
5433 OUString aErrRef
= GetCurrentOpCodeMap()->getSymbol(ocErrRef
);
5434 const OpCode eOp
= _pTokenP
->GetOpCode();
5435 const ScSingleRefData
& rRef
= *_pTokenP
->GetSingleRef();
5436 ScComplexRefData aRef
;
5437 aRef
.Ref1
= aRef
.Ref2
= rRef
;
5438 if ( eOp
== ocColRowName
)
5440 ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5441 if (rDoc
.HasStringData(aAbs
.Col(), aAbs
.Row(), aAbs
.Tab()))
5443 OUString aStr
= rDoc
.GetString(aAbs
, &mrInterpreterContext
);
5444 // Enquote to SingleQuoted.
5445 aStr
= aStr
.replaceAll(u
"'", u
"''");
5446 rBuffer
.append('\'');
5447 rBuffer
.append(aStr
);
5448 rBuffer
.append('\'');
5452 rBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5453 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
,
5454 GetSetupTabNames(), aRef
, true, (pArr
&& pArr
->IsFromRangeName()));
5457 else if (pArr
&& (p
= maArrIterator
.PeekPrevNoSpaces()) && p
->GetOpCode() == ocTableRefOpen
)
5460 ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5461 const ScDBData
* pData
= rDoc
.GetDBAtCursor( aAbs
.Col(), aAbs
.Row(), aAbs
.Tab(), ScDBDataPortion::AREA
);
5462 SAL_WARN_IF( !pData
, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " <<
5463 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5465 aStr
= pData
->GetTableColumnName( aAbs
.Col());
5468 if (pData
&& pData
->HasHeader())
5470 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " <<
5471 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5472 aStr
= rDoc
.GetString(aAbs
, &mrInterpreterContext
);
5476 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " <<
5477 aAbs
.Format( ScRefFlags::VALID
| ScRefFlags::TAB_3D
, &rDoc
));
5481 escapeTableRefColumnSpecifier( aStr
);
5482 rBuffer
.append(aStr
);
5485 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
,
5486 GetSetupTabNames(), aRef
, true, (pArr
&& pArr
->IsFromRangeName()));
5489 void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5491 OUString aErrRef
= GetCurrentOpCodeMap()->getSymbol(ocErrRef
);
5492 pConv
->makeRefStr(rDoc
.GetSheetLimits(), rBuffer
, meGrammar
, aPos
, aErrRef
, GetSetupTabNames(),
5493 *_pTokenP
->GetDoubleRef(), false, (pArr
&& pArr
->IsFromRangeName()));
5496 void ScCompiler::CreateStringFromIndex( OUStringBuffer
& rBuffer
, const FormulaToken
* _pTokenP
) const
5498 const OpCode eOp
= _pTokenP
->GetOpCode();
5499 OUStringBuffer aBuffer
;
5504 const ScRangeData
* pData
= GetRangeData( *_pTokenP
);
5507 SCTAB nTab
= _pTokenP
->GetSheet();
5508 if (nTab
>= 0 && (nTab
!= aPos
.Tab() || mbRefConventionChartOOXML
))
5510 // Sheet-local on other sheet.
5512 if (rDoc
.GetName( nTab
, aName
))
5514 ScCompiler::CheckTabQuotes( aName
, pConv
->meConv
);
5515 aBuffer
.append( aName
);
5518 aBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5519 aBuffer
.append( pConv
->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR
));
5521 else if (mbRefConventionChartOOXML
)
5523 aBuffer
.append("[0]"
5524 + OUStringChar(pConv
->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR
)));
5526 aBuffer
.append(pData
->GetName());
5532 const ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
5534 aBuffer
.append(pDBData
->GetName());
5539 if (NeedsTableRefTransformation())
5541 // Write the resulting reference if TableRef is not supported.
5542 const ScTableRefToken
* pTR
= dynamic_cast<const ScTableRefToken
*>(_pTokenP
);
5544 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5547 const FormulaToken
* pRef
= pTR
->GetAreaRefRPN();
5549 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5552 switch (pRef
->GetType())
5555 CreateStringFromSingleRef( aBuffer
, pRef
);
5558 CreateStringFromDoubleRef( aBuffer
, pRef
);
5561 AppendErrorConstant( aBuffer
, pRef
->GetError());
5564 AppendErrorConstant( aBuffer
, FormulaError::NoCode
);
5571 const ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
5573 aBuffer
.append(pDBData
->GetName());
5580 if ( !aBuffer
.isEmpty() )
5581 rBuffer
.append(aBuffer
);
5583 rBuffer
.append(ScCompiler::GetNativeSymbol(ocErrName
));
5586 void ScCompiler::LocalizeString( OUString
& rName
) const
5588 ScGlobal::GetAddInCollection()->LocalizeString( rName
);
5591 FormulaTokenRef
ScCompiler::ExtendRangeReference( FormulaToken
& rTok1
, FormulaToken
& rTok2
)
5593 return extendRangeReference( rDoc
.GetSheetLimits(), rTok1
, rTok2
, aPos
, true/*bReuseDoubleRef*/ );
5596 void ScCompiler::fillAddInToken(::std::vector
< css::sheet::FormulaOpCodeMapEntry
>& _rVec
,bool _bIsEnglish
) const
5598 // All known AddIn functions.
5599 sheet::FormulaOpCodeMapEntry aEntry
;
5600 aEntry
.Token
.OpCode
= ocExternal
;
5602 const LanguageTag
aEnglishLanguageTag(LANGUAGE_ENGLISH_US
);
5603 ScUnoAddInCollection
* pColl
= ScGlobal::GetAddInCollection();
5604 const tools::Long nCount
= pColl
->GetFuncCount();
5605 for (tools::Long i
=0; i
< nCount
; ++i
)
5607 const ScUnoAddInFuncData
* pFuncData
= pColl
->GetFuncData(i
);
5612 // This is used with OOXML import, so GetExcelName() is really
5613 // wanted here until we'll have a parameter to differentiate
5614 // from the general css::sheet::XFormulaOpCodeMapper case and
5615 // use pFuncData->GetUpperEnglish().
5617 if (pFuncData
->GetExcelName( aEnglishLanguageTag
, aName
))
5618 aEntry
.Name
= aName
;
5620 aEntry
.Name
= pFuncData
->GetUpperName();
5623 aEntry
.Name
= pFuncData
->GetUpperLocal();
5624 aEntry
.Token
.Data
<<= pFuncData
->GetOriginalName();
5625 _rVec
.push_back( aEntry
);
5628 // FIXME: what about those old non-UNO AddIns?
5631 bool ScCompiler::HandleColRowName()
5633 ScSingleRefData
& rRef
= *mpToken
->GetSingleRef();
5634 const ScAddress aAbs
= rRef
.toAbs(rDoc
, aPos
);
5635 if (!rDoc
.ValidAddress(aAbs
))
5637 SetError( FormulaError::NoRef
);
5640 SCCOL nCol
= aAbs
.Col();
5641 SCROW nRow
= aAbs
.Row();
5642 SCTAB nTab
= aAbs
.Tab();
5643 bool bColName
= rRef
.IsColRel();
5644 SCCOL nMyCol
= aPos
.Col();
5645 SCROW nMyRow
= aPos
.Row();
5646 bool bInList
= false;
5647 bool bValidName
= false;
5648 ScRangePairList
* pRL
= (bColName
?
5649 rDoc
.GetColNameRanges() : rDoc
.GetRowNameRanges());
5651 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5653 const ScRangePair
& rR
= (*pRL
)[i
];
5654 if ( rR
.GetRange(0).Contains( aAbs
) )
5656 bInList
= bValidName
= true;
5657 aRange
= rR
.GetRange(1);
5660 aRange
.aStart
.SetCol( nCol
);
5661 aRange
.aEnd
.SetCol( nCol
);
5665 aRange
.aStart
.SetRow( nRow
);
5666 aRange
.aEnd
.SetRow( nRow
);
5671 if ( !bInList
&& rDoc
.GetDocOptions().IsLookUpColRowNames() )
5672 { // automagically or created by copying and NamePos isn't in list
5673 ScRefCellValue
aCell(rDoc
, aAbs
);
5674 bool bString
= aCell
.hasString();
5675 if (!bString
&& aCell
.isEmpty())
5676 bString
= true; // empty cell is ok
5678 { // corresponds with ScInterpreter::ScColRowNameAuto()
5682 SCROW nStartRow
= nRow
+ 1;
5683 if ( nStartRow
> rDoc
.MaxRow() )
5684 nStartRow
= rDoc
.MaxRow();
5685 SCROW nMaxRow
= rDoc
.MaxRow();
5686 if ( nMyCol
== nCol
)
5687 { // formula cell in same column
5688 if ( nMyRow
== nStartRow
)
5689 { // take remainder under name cell
5691 if ( nStartRow
> rDoc
.MaxRow() )
5692 nStartRow
= rDoc
.MaxRow();
5694 else if ( nMyRow
> nStartRow
)
5695 { // from name cell down to formula cell
5696 nMaxRow
= nMyRow
- 1;
5699 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5700 { // next defined ColNameRange below limits row
5701 const ScRangePair
& rR
= (*pRL
)[i
];
5702 const ScRange
& rRange
= rR
.GetRange(1);
5703 if ( rRange
.aStart
.Col() <= nCol
&& nCol
<= rRange
.aEnd
.Col() )
5704 { // identical column range
5705 SCROW nTmp
= rRange
.aStart
.Row();
5706 if ( nStartRow
< nTmp
&& nTmp
<= nMaxRow
)
5710 aRange
.aStart
.Set( nCol
, nStartRow
, nTab
);
5711 aRange
.aEnd
.Set( nCol
, nMaxRow
, nTab
);
5715 SCCOL nStartCol
= nCol
+ 1;
5716 if ( nStartCol
> rDoc
.MaxCol() )
5717 nStartCol
= rDoc
.MaxCol();
5718 SCCOL nMaxCol
= rDoc
.MaxCol();
5719 if ( nMyRow
== nRow
)
5720 { // formula cell in same row
5721 if ( nMyCol
== nStartCol
)
5722 { // take remainder right from name cell
5724 if ( nStartCol
> rDoc
.MaxCol() )
5725 nStartCol
= rDoc
.MaxCol();
5727 else if ( nMyCol
> nStartCol
)
5728 { // from name cell right to formula cell
5729 nMaxCol
= nMyCol
- 1;
5732 for ( size_t i
= 0, nPairs
= pRL
->size(); i
< nPairs
; ++i
)
5733 { // next defined RowNameRange to the right limits column
5734 const ScRangePair
& rR
= (*pRL
)[i
];
5735 const ScRange
& rRange
= rR
.GetRange(1);
5736 if ( rRange
.aStart
.Row() <= nRow
&& nRow
<= rRange
.aEnd
.Row() )
5737 { // identical row range
5738 SCCOL nTmp
= rRange
.aStart
.Col();
5739 if ( nStartCol
< nTmp
&& nTmp
<= nMaxCol
)
5743 aRange
.aStart
.Set( nStartCol
, nRow
, nTab
);
5744 aRange
.aEnd
.Set( nMaxCol
, nRow
, nTab
);
5750 // And now the magic to distinguish between a range and a single
5751 // cell thereof, which is picked position-dependent of the formula
5752 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5753 // SingleRef matching the column/row of the formula cell is
5754 // generated. A ocColRowName or ocIntersect as a neighbor results
5755 // in a range. Special case: if label is valid for a single cell, a
5756 // position independent SingleRef is generated.
5757 bool bSingle
= (aRange
.aStart
== aRange
.aEnd
);
5763 FormulaToken
* p1
= maArrIterator
.PeekPrevNoSpaces();
5764 FormulaToken
* p2
= maArrIterator
.PeekNextNoSpaces();
5765 // begin/end of a formula => single
5766 OpCode eOp1
= p1
? p1
->GetOpCode() : ocAdd
;
5767 OpCode eOp2
= p2
? p2
->GetOpCode() : ocAdd
;
5768 if ( eOp1
!= ocColRowName
&& eOp1
!= ocIntersect
5769 && eOp2
!= ocColRowName
&& eOp2
!= ocIntersect
)
5771 if ( (SC_OPCODE_START_BIN_OP
<= eOp1
&& eOp1
< SC_OPCODE_STOP_BIN_OP
) ||
5772 (SC_OPCODE_START_BIN_OP
<= eOp2
&& eOp2
< SC_OPCODE_STOP_BIN_OP
))
5776 { // column and/or row must match range
5779 bFound
= (aRange
.aStart
.Row() <= nMyRow
5780 && nMyRow
<= aRange
.aEnd
.Row());
5782 aRange
.aStart
.SetRow( nMyRow
);
5786 bFound
= (aRange
.aStart
.Col() <= nMyCol
5787 && nMyCol
<= aRange
.aEnd
.Col());
5789 aRange
.aStart
.SetCol( nMyCol
);
5796 SetError(FormulaError::NoRef
);
5797 else if (mbJumpCommandReorder
)
5799 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5802 ScSingleRefData aRefData
;
5803 aRefData
.InitAddress( aRange
.aStart
);
5805 aRefData
.SetColRel( true );
5807 aRefData
.SetRowRel( true );
5808 aRefData
.SetAddress(rDoc
.GetSheetLimits(), aRange
.aStart
, aPos
);
5809 pNew
->AddSingleReference( aRefData
);
5813 ScComplexRefData aRefData
;
5814 aRefData
.InitRange( aRange
);
5817 aRefData
.Ref1
.SetColRel( true );
5818 aRefData
.Ref2
.SetColRel( true );
5822 aRefData
.Ref1
.SetRowRel( true );
5823 aRefData
.Ref2
.SetRowRel( true );
5825 aRefData
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
5827 pNew
->AddDoubleReference( aRefData
);
5830 pNew
->Add( new ScDoubleRefToken( rDoc
.GetSheetLimits(), aRefData
, ocColRowNameAuto
) );
5833 PushTokenArray( pNew
, true );
5838 SetError(FormulaError::NoName
);
5842 bool ScCompiler::HandleDbData()
5844 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex(mpToken
->GetIndex());
5846 SetError(FormulaError::NoName
);
5847 else if (mbJumpCommandReorder
)
5849 ScComplexRefData aRefData
;
5850 aRefData
.InitFlags();
5852 pDBData
->GetArea(aRange
);
5853 aRange
.aEnd
.SetTab(aRange
.aStart
.Tab());
5854 aRefData
.SetRange(rDoc
.GetSheetLimits(), aRange
, aPos
);
5855 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
5856 pNew
->AddDoubleReference( aRefData
);
5857 PushTokenArray( pNew
, true );
5863 bool ScCompiler::GetTokenIfOpCode( OpCode eOp
)
5865 const formula::FormulaToken
* p
= maArrIterator
.PeekNextNoSpaces();
5866 if (p
&& p
->GetOpCode() == eOp
)
5872 /* Documentation on MS-Excel Table structured references:
5873 * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
5874 * * as of Excel 2013
5875 * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
5876 * * look for structure-reference
5879 bool ScCompiler::HandleTableRef()
5881 ScTableRefToken
* pTR
= dynamic_cast<ScTableRefToken
*>(mpToken
.get());
5884 SetError(FormulaError::UnknownToken
);
5888 ScDBData
* pDBData
= rDoc
.GetDBCollection()->getNamedDBs().findByIndex( pTR
->GetIndex());
5890 SetError(FormulaError::NoName
);
5891 else if (mbJumpCommandReorder
)
5894 pDBData
->GetArea(aDBRange
);
5895 aDBRange
.aEnd
.SetTab(aDBRange
.aStart
.Tab());
5896 ScRange
aRange( aDBRange
);
5897 FormulaError nError
= FormulaError::NONE
;
5898 bool bForwardToClose
= false;
5899 ScTableRefToken::Item eItem
= pTR
->GetItem();
5902 case ScTableRefToken::TABLE
:
5904 // The table name without items references the table data,
5905 // without headers or totals.
5906 if (pDBData
->HasHeader())
5907 aRange
.aStart
.IncRow();
5908 if (pDBData
->HasTotals())
5909 aRange
.aEnd
.IncRow(-1);
5910 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5911 nError
= FormulaError::NoRef
;
5912 bForwardToClose
= true;
5915 case ScTableRefToken::ALL
:
5917 bForwardToClose
= true;
5920 case ScTableRefToken::HEADERS
:
5922 if (pDBData
->HasHeader())
5923 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
5925 nError
= FormulaError::NoRef
;
5926 bForwardToClose
= true;
5929 case ScTableRefToken::DATA
:
5931 if (pDBData
->HasHeader())
5932 aRange
.aStart
.IncRow();
5935 case ScTableRefToken::HEADERS_DATA
:
5937 if (pDBData
->HasTotals())
5938 aRange
.aEnd
.IncRow(-1);
5939 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5940 nError
= FormulaError::NoRef
;
5941 bForwardToClose
= true;
5944 case ScTableRefToken::TOTALS
:
5946 if (pDBData
->HasTotals())
5947 aRange
.aStart
.SetRow( aRange
.aEnd
.Row());
5949 nError
= FormulaError::NoRef
;
5950 bForwardToClose
= true;
5953 case ScTableRefToken::DATA_TOTALS
:
5955 if (pDBData
->HasHeader())
5956 aRange
.aStart
.IncRow();
5957 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5958 nError
= FormulaError::NoRef
;
5959 bForwardToClose
= true;
5962 case ScTableRefToken::THIS_ROW
:
5964 if (aRange
.aStart
.Row() <= aPos
.Row() && aPos
.Row() <= aRange
.aEnd
.Row())
5966 aRange
.aStart
.SetRow( aPos
.Row());
5967 aRange
.aEnd
.SetRow( aPos
.Row());
5971 nError
= FormulaError::NoValue
;
5972 // For *some* relative row reference in named
5973 // expressions' thisrow special handling below.
5974 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
5976 bForwardToClose
= true;
5980 bool bColumnRange
= false;
5981 bool bCol1Rel
= false;
5982 bool bCol1RelName
= false;
5984 if (bForwardToClose
&& GetTokenIfOpCode( ocTableRefOpen
))
5998 const formula::FormulaToken
* p
= maArrIterator
.PeekNextNoSpaces();
6003 switch (p
->GetOpCode())
6005 case ocTableRefOpen
:
6006 eState
= ((eState
== sOpen
|| eState
== sSep
) ? sOpen
: sStop
);
6009 SetError( FormulaError::Pair
);
6013 case ocTableRefItemAll
:
6014 case ocTableRefItemHeaders
:
6015 case ocTableRefItemData
:
6016 case ocTableRefItemTotals
:
6017 case ocTableRefItemThisRow
:
6018 eState
= ((eState
== sOpen
) ? sItem
: sStop
);
6020 case ocTableRefClose
:
6021 eState
= ((eState
== sItem
|| eState
== sClose
) ? sClose
: sStop
);
6022 if (eState
!= sStop
&& --nLevel
== 0)
6026 eState
= ((eState
== sClose
) ? sSep
: sStop
);
6029 if (eState
== sOpen
&& p
->GetType() == svSingleRef
)
6031 bColumnRange
= true;
6032 bCol1Rel
= p
->GetSingleRef()->IsColRel();
6033 bCol1RelName
= p
->GetSingleRef()->IsRelName();
6043 if (nError
== FormulaError::NONE
)
6044 nError
= FormulaError::NoName
;
6049 if (eState
!= sStop
)
6051 if (eState
== sLast
)
6054 } while (eState
!= sStop
);
6056 ScTokenArray
* pNew
= new ScTokenArray(rDoc
);
6057 if (nError
== FormulaError::NONE
|| nError
== FormulaError::NoValue
)
6059 bool bCol2Rel
= false;
6060 bool bCol2RelName
= false;
6061 // The FormulaError::NoValue case generates a thisrow reference that can be
6062 // used to save named expressions in A1 syntax notation.
6065 // Limit range to specified columns.
6066 ScRange
aColRange( ScAddress::INITIALIZE_INVALID
);
6067 switch (mpToken
->GetType())
6071 aColRange
.aStart
= aColRange
.aEnd
= mpToken
->GetSingleRef()->toAbs(rDoc
, aPos
);
6072 if ( GetTokenIfOpCode( ocTableRefClose
) && (nLevel
--) &&
6073 GetTokenIfOpCode( ocRange
) &&
6074 GetTokenIfOpCode( ocTableRefOpen
) && (++nLevel
) &&
6075 GetTokenIfOpCode( ocPush
))
6077 if (mpToken
->GetType() != svSingleRef
)
6078 aColRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
6081 aColRange
.aEnd
= mpToken
->GetSingleRef()->toAbs(rDoc
, aPos
);
6082 aColRange
.PutInOrder();
6083 bCol2Rel
= mpToken
->GetSingleRef()->IsColRel();
6084 bCol2RelName
= mpToken
->GetSingleRef()->IsRelName();
6092 // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses
6093 if (aColRange
.aStart
.Row() != aDBRange
.aStart
.Row() || aColRange
.aEnd
.Row() != aDBRange
.aStart
.Row())
6094 aRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
6097 aColRange
.aEnd
.SetRow( aRange
.aEnd
.Row());
6098 aRange
= aRange
.Intersection( aColRange
);
6101 if (aRange
.IsValid())
6103 if (aRange
.aStart
== aRange
.aEnd
)
6105 ScSingleRefData aRefData
;
6106 aRefData
.InitFlags();
6107 aRefData
.SetColRel( bCol1Rel
);
6108 if (eItem
== ScTableRefToken::THIS_ROW
)
6110 aRefData
.SetRowRel( true);
6112 bCol1RelName
= pArr
->IsFromRangeName();
6114 aRefData
.SetRelName( bCol1RelName
);
6115 aRefData
.SetFlag3D( true);
6116 if (nError
!= FormulaError::NONE
)
6118 aRefData
.SetAddress( rDoc
.GetSheetLimits(), aRange
.aStart
, aRange
.aStart
);
6119 pTR
->SetAreaRefRPN( new ScSingleRefToken(rDoc
.GetSheetLimits(), aRefData
)); // set reference at TableRef
6120 pNew
->Add( new FormulaErrorToken( nError
)); // set error in RPN
6124 aRefData
.SetAddress( rDoc
.GetSheetLimits(), aRange
.aStart
, aPos
);
6125 pTR
->SetAreaRefRPN( pNew
->AddSingleReference( aRefData
));
6130 ScComplexRefData aRefData
;
6131 aRefData
.InitFlags();
6132 aRefData
.Ref1
.SetColRel( bCol1Rel
);
6133 aRefData
.Ref2
.SetColRel( bCol2Rel
);
6134 bool bRelName
= bCol1RelName
|| bCol2RelName
;
6135 if (eItem
== ScTableRefToken::THIS_ROW
)
6137 aRefData
.Ref1
.SetRowRel( true);
6138 aRefData
.Ref2
.SetRowRel( true);
6140 bRelName
= pArr
->IsFromRangeName();
6142 aRefData
.Ref1
.SetRelName( bRelName
);
6143 aRefData
.Ref2
.SetRelName( bRelName
);
6144 aRefData
.Ref1
.SetFlag3D( true);
6145 if (nError
!= FormulaError::NONE
)
6147 aRefData
.SetRange( rDoc
.GetSheetLimits(), aRange
, aRange
.aStart
);
6148 pTR
->SetAreaRefRPN( new ScDoubleRefToken(rDoc
.GetSheetLimits(), aRefData
)); // set reference at TableRef
6149 pNew
->Add( new FormulaErrorToken( nError
)); // set error in RPN
6153 aRefData
.SetRange( rDoc
.GetSheetLimits(), aRange
, aPos
);
6154 pTR
->SetAreaRefRPN( pNew
->AddDoubleReference( aRefData
));
6160 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( FormulaError::NoRef
)));
6165 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( nError
)));
6167 while (nLevel
-- > 0)
6169 if (!GetTokenIfOpCode( ocTableRefClose
))
6170 SetError( FormulaError::Pair
);
6172 PushTokenArray( pNew
, true );
6178 formula::ParamClass
ScCompiler::GetForceArrayParameter( const formula::FormulaToken
* pToken
, sal_uInt16 nParam
) const
6180 return ScParameterClassification::GetParameterType( pToken
, nParam
);
6183 bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken
* token
, int parameter
)
6185 formula::ParamClass param
= ScParameterClassification::GetParameterType( token
, parameter
);
6186 return param
== Value
|| param
== Array
;
6189 bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken
* token
) const
6193 formula::ParamClass paramClass
= token
->GetInForceArray();
6194 if (paramClass
== formula::ForceArray
6195 || paramClass
== formula::ReferenceOrForceArray
6196 || paramClass
== formula::SuppressedReferenceOrForceArray
6197 || paramClass
== formula::ReferenceOrRefArray
)
6201 formula::ParamClass returnType
= ScParameterClassification::GetParameterType( token
, SAL_MAX_UINT16
);
6202 return returnType
== formula::Reference
;
6205 void ScCompiler::HandleIIOpCode(FormulaToken
* token
, FormulaToken
*** pppToken
, sal_uInt8 nNumParams
)
6210 if(!HandleIIOpCodeInternal(token
, pppToken
, nNumParams
))
6211 mUnhandledPossibleImplicitIntersectionsOpCodes
.insert(token
->GetOpCode());
6213 HandleIIOpCodeInternal(token
, pppToken
, nNumParams
);
6217 // return true if opcode is handled
6218 bool ScCompiler::HandleIIOpCodeInternal(FormulaToken
* token
, FormulaToken
*** pppToken
, sal_uInt8 nNumParams
)
6220 if (nNumParams
> 0 && *pppToken
[0] == nullptr)
6221 return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray())
6223 const OpCode nOpCode
= token
->GetOpCode();
6225 if (nOpCode
== ocPush
)
6227 if(token
->GetType() == svDoubleRef
)
6228 mUnhandledPossibleImplicitIntersections
.insert( token
);
6231 else if (nOpCode
== ocSumIf
|| nOpCode
== ocAverageIf
)
6233 if (nNumParams
!= 3)
6236 if (!(pppToken
[0] && pppToken
[2] && *pppToken
[0] && *pppToken
[2]))
6239 if ((*pppToken
[0])->GetType() != svDoubleRef
)
6242 const StackVar eSumRangeType
= (*pppToken
[2])->GetType();
6244 if ( eSumRangeType
!= svSingleRef
&& eSumRangeType
!= svDoubleRef
)
6247 const ScComplexRefData
& rBaseRange
= *(*pppToken
[0])->GetDoubleRef();
6249 ScComplexRefData aSumRange
;
6250 if (eSumRangeType
== svSingleRef
)
6252 aSumRange
.Ref1
= *(*pppToken
[2])->GetSingleRef();
6253 aSumRange
.Ref2
= aSumRange
.Ref1
;
6256 aSumRange
= *(*pppToken
[2])->GetDoubleRef();
6258 CorrectSumRange(rBaseRange
, aSumRange
, pppToken
[2]);
6259 // TODO mark parameters as handled
6262 else if (nOpCode
>= SC_OPCODE_START_1_PAR
&& nOpCode
< SC_OPCODE_STOP_1_PAR
)
6264 if (nNumParams
!= 1)
6267 if( !ParameterMayBeImplicitIntersection( token
, 0 ))
6269 if (SkipImplicitIntersectionOptimization(token
))
6272 if ((*pppToken
[0])->GetType() != svDoubleRef
)
6275 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6278 else if ((nOpCode
>= SC_OPCODE_START_BIN_OP
&& nOpCode
< SC_OPCODE_STOP_BIN_OP
6279 && nOpCode
!= ocAnd
&& nOpCode
!= ocOr
)
6280 || nOpCode
== ocRound
|| nOpCode
== ocRoundUp
|| nOpCode
== ocRoundDown
)
6282 if (nNumParams
!= 2)
6285 if( !ParameterMayBeImplicitIntersection( token
, 0 ) || !ParameterMayBeImplicitIntersection( token
, 1 ))
6287 if (SkipImplicitIntersectionOptimization(token
))
6290 // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
6291 if ((*pppToken
[0])->GetType() == svDoubleRef
&& (*pppToken
[1])->GetType() != svMatrix
)
6292 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6293 if ((*pppToken
[1])->GetType() == svDoubleRef
&& (*pppToken
[0])->GetType() != svMatrix
)
6294 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[1], token
);
6297 else if ((nOpCode
>= SC_OPCODE_START_UN_OP
&& nOpCode
< SC_OPCODE_STOP_UN_OP
)
6298 || nOpCode
== ocPercentSign
)
6300 if (nNumParams
!= 1)
6303 if( !ParameterMayBeImplicitIntersection( token
, 0 ))
6305 if (SkipImplicitIntersectionOptimization(token
))
6308 if ((*pppToken
[0])->GetType() == svDoubleRef
)
6309 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6312 else if (nOpCode
== ocVLookup
)
6314 if (nNumParams
!= 3 && nNumParams
!= 4)
6317 if (SkipImplicitIntersectionOptimization(token
))
6320 assert( ParameterMayBeImplicitIntersection( token
, 0 ));
6321 assert( !ParameterMayBeImplicitIntersection( token
, 1 ));
6322 assert( ParameterMayBeImplicitIntersection( token
, 2 ));
6323 assert( ParameterMayBeImplicitIntersection( token
, 3 ));
6324 if ((*pppToken
[2])->GetType() == svDoubleRef
)
6325 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[2], token
);
6326 if ((*pppToken
[0])->GetType() == svDoubleRef
)
6327 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[0], token
);
6328 if (nNumParams
== 4 && (*pppToken
[3])->GetType() == svDoubleRef
)
6329 mPendingImplicitIntersectionOptimizations
.emplace_back( pppToken
[3], token
);
6330 // a range for the second parameters is not an implicit intersection
6331 mUnhandledPossibleImplicitIntersections
.erase( *pppToken
[ 1 ] );
6336 bool possibleII
= false;
6337 for( int i
= 0; i
< nNumParams
; ++i
)
6339 if( ParameterMayBeImplicitIntersection( token
, i
)
6340 && (*pppToken
[i
])->GetType() == svDoubleRef
)
6348 // all parameters have been handled, they are not implicit intersections
6349 for( int i
= 0; i
< nNumParams
; ++i
)
6350 mUnhandledPossibleImplicitIntersections
.erase( *pppToken
[ i
] );
6358 void ScCompiler::PostProcessCode()
6360 for( const PendingImplicitIntersectionOptimization
& item
: mPendingImplicitIntersectionOptimizations
)
6362 if( *item
.parameterLocation
!= item
.parameter
) // the parameter has been changed somehow
6364 if( item
.parameterLocation
>= pCode
) // the location is not inside the code (pCode points after the end)
6366 // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
6367 if( item
.operation
->IsInForceArray())
6369 ReplaceDoubleRefII( item
.parameterLocation
);
6371 mPendingImplicitIntersectionOptimizations
.clear();
6374 void ScCompiler::AnnotateOperands()
6376 AnnotateTrimOnDoubleRefs();
6379 void ScCompiler::ReplaceDoubleRefII(FormulaToken
** ppDoubleRefTok
)
6381 const ScComplexRefData
* pRange
= (*ppDoubleRefTok
)->GetDoubleRef();
6385 const ScComplexRefData
& rRange
= *pRange
;
6387 // Can't do optimization reliably in this case (when row references are absolute).
6388 // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
6389 // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
6390 // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
6391 // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
6392 // fix this, but that is unknown at this stage, so skip such cases.
6393 if (!rRange
.Ref1
.IsRowRel() && !rRange
.Ref2
.IsRowRel())
6396 ScRange aAbsRange
= rRange
.toAbs(rDoc
, aPos
);
6397 if (aAbsRange
.aStart
== aAbsRange
.aEnd
)
6398 return; // Nothing to do (trivial case).
6402 if (!DoubleRefToPosSingleRefScalarCase(aAbsRange
, aAddr
, aPos
))
6405 ScSingleRefData aSingleRef
;
6406 aSingleRef
.InitFlags();
6407 aSingleRef
.SetColRel(rRange
.Ref1
.IsColRel());
6408 aSingleRef
.SetRowRel(true);
6409 aSingleRef
.SetTabRel(rRange
.Ref1
.IsTabRel());
6410 aSingleRef
.SetAddress(rDoc
.GetSheetLimits(), aAddr
, aPos
);
6412 // Replace the original doubleref token with computed singleref token
6413 FormulaToken
* pNewSingleRefTok
= new ScSingleRefToken(rDoc
.GetSheetLimits(), aSingleRef
);
6414 (*ppDoubleRefTok
)->DecRef();
6415 *ppDoubleRefTok
= pNewSingleRefTok
;
6416 pNewSingleRefTok
->IncRef();
6419 bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange
& rRange
, ScAddress
& rAdr
, const ScAddress
& rFormulaPos
)
6421 assert(rRange
.aStart
!= rRange
.aEnd
);
6424 SCCOL nMyCol
= rFormulaPos
.Col();
6425 SCROW nMyRow
= rFormulaPos
.Row();
6426 SCTAB nMyTab
= rFormulaPos
.Tab();
6430 nTab
= rRange
.aStart
.Tab();
6431 if ( rRange
.aStart
.Col() <= nMyCol
&& nMyCol
<= rRange
.aEnd
.Col() )
6433 nRow
= rRange
.aStart
.Row();
6434 if ( nRow
== rRange
.aEnd
.Row() )
6439 else if ( nTab
!= nMyTab
&& nTab
== rRange
.aEnd
.Tab()
6440 && rRange
.aStart
.Row() <= nMyRow
&& nMyRow
<= rRange
.aEnd
.Row() )
6447 else if ( rRange
.aStart
.Row() <= nMyRow
&& nMyRow
<= rRange
.aEnd
.Row() )
6449 nCol
= rRange
.aStart
.Col();
6450 if ( nCol
== rRange
.aEnd
.Col() )
6455 else if ( nTab
!= nMyTab
&& nTab
== rRange
.aEnd
.Tab()
6456 && rRange
.aStart
.Col() <= nMyCol
&& nMyCol
<= rRange
.aEnd
.Col() )
6465 if ( nTab
== rRange
.aEnd
.Tab() )
6467 else if ( nTab
<= nMyTab
&& nMyTab
<= rRange
.aEnd
.Tab() )
6472 rAdr
.Set( nCol
, nRow
, nTab
);
6478 static void lcl_GetColRowDeltas(const ScRange
& rRange
, SCCOL
& rXDelta
, SCROW
& rYDelta
)
6480 rXDelta
= rRange
.aEnd
.Col() - rRange
.aStart
.Col();
6481 rYDelta
= rRange
.aEnd
.Row() - rRange
.aStart
.Row();
6484 bool ScCompiler::AdjustSumRangeShape(const ScComplexRefData
& rBaseRange
, ScComplexRefData
& rSumRange
)
6486 ScRange aAbs
= rSumRange
.toAbs(rDoc
, aPos
);
6488 // Current sum-range end col/row
6489 SCCOL nEndCol
= aAbs
.aEnd
.Col();
6490 SCROW nEndRow
= aAbs
.aEnd
.Row();
6492 // Current behaviour is, we will get a #NAME? for the below case, so bail out.
6493 // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula
6494 // has a single-ref as the sum-range.
6495 if (!rDoc
.ValidCol(nEndCol
) || !rDoc
.ValidRow(nEndRow
))
6498 SCCOL nXDeltaSum
= 0;
6499 SCROW nYDeltaSum
= 0;
6501 lcl_GetColRowDeltas(aAbs
, nXDeltaSum
, nYDeltaSum
);
6503 aAbs
= rBaseRange
.toAbs(rDoc
, aPos
);
6507 lcl_GetColRowDeltas(aAbs
, nXDelta
, nYDelta
);
6509 if (nXDelta
== nXDeltaSum
&&
6510 nYDelta
== nYDeltaSum
)
6511 return false; // shapes of base-range match current sum-range
6513 // Try to make the sum-range to take the same shape as base-range,
6514 // by adjusting Ref2 member of rSumRange if the resultant sum-range don't
6515 // go out-of-bounds.
6517 SCCOL nXInc
= nXDelta
- nXDeltaSum
;
6518 SCROW nYInc
= nYDelta
- nYDeltaSum
;
6520 // Don't let a valid End[Col,Row] go beyond (rDoc.MaxCol(),rDoc.MaxRow()) to match
6521 // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks
6522 // the base-range by the (out-of-bound)amount clipped off the sum-range.
6523 // TODO: Probably we can optimize (from threading perspective) rBaseRange
6524 // by shrinking it here correspondingly (?)
6525 if (nEndCol
+ nXInc
> rDoc
.MaxCol())
6526 nXInc
= rDoc
.MaxCol() - nEndCol
;
6527 if (nEndRow
+ nYInc
> rDoc
.MaxRow())
6528 nYInc
= rDoc
.MaxRow() - nEndRow
;
6530 rSumRange
.Ref2
.IncCol(nXInc
);
6531 rSumRange
.Ref2
.IncRow(nYInc
);
6536 void ScCompiler::CorrectSumRange(const ScComplexRefData
& rBaseRange
,
6537 ScComplexRefData
& rSumRange
,
6538 FormulaToken
** ppSumRangeToken
)
6540 if (!AdjustSumRangeShape(rBaseRange
, rSumRange
))
6543 // Replace sum-range token
6544 FormulaToken
* pNewSumRangeTok
= new ScDoubleRefToken(rDoc
.GetSheetLimits(), rSumRange
);
6545 (*ppSumRangeToken
)->DecRef();
6546 *ppSumRangeToken
= pNewSumRangeTok
;
6547 pNewSumRangeTok
->IncRef();
6550 void ScCompiler::AnnotateTrimOnDoubleRefs()
6552 if (!pCode
|| !(*(pCode
- 1)))
6555 // OpCode of the "root" operator (which is already in RPN array).
6556 OpCode eOpCode
= (*(pCode
- 1))->GetOpCode();
6557 // Param number of the "root" operator (which is already in RPN array).
6558 sal_uInt8 nRootParam
= (*(pCode
- 1))->GetByte();
6559 // eOpCode can be some operator which does not change with operands with or contains zero values.
6560 if (eOpCode
== ocSum
)
6562 FormulaToken
** ppTok
= pCode
- 2; // exclude the root operator.
6563 // The following loop runs till a "pattern" is found or there is a mismatch
6564 // and marks the push DoubleRef arguments as trimmable when there is a match.
6566 // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6567 // such that one of the operands of ocEqual is a double-ref.
6568 // Examples of formula that matches this are:
6569 // SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
6570 // SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
6571 // SUM(IF(E:E=16,F:F)*$H$1*100)
6572 bool bTillClose
= true;
6573 bool bCloseTillIf
= false;
6574 sal_Int16 nToksTillIf
= 0;
6575 constexpr sal_Int16 MAXDIST_IF
= 15;
6578 FormulaToken
* pTok
= *ppTok
;
6579 OpCode eCurrOp
= pTok
->GetOpCode();
6582 // TODO : Is there a better way to handle this ?
6583 // ocIf is too far off from the sum opcode.
6584 if (nToksTillIf
> MAXDIST_IF
)
6601 bCloseTillIf
= true;
6611 if (!pTok
->IsInForceArray())
6614 const short nJumpCount
= pTok
->GetJump()[0];
6615 if (nJumpCount
!= 2) // Should have THEN but no ELSE.
6618 OpCode eCompOp
= (*(ppTok
- 1))->GetOpCode();
6619 if (eCompOp
!= ocEqual
)
6622 FormulaToken
* pLHS
= *(ppTok
- 2);
6623 FormulaToken
* pRHS
= *(ppTok
- 3);
6624 if (((pLHS
->GetType() == svSingleRef
|| pLHS
->GetType() == svDouble
) && pRHS
->GetType() == svDoubleRef
) ||
6625 ((pRHS
->GetType() == svSingleRef
|| pRHS
->GetType() == svDouble
) && pLHS
->GetType() == svDoubleRef
))
6627 if (pLHS
->GetType() == svDoubleRef
)
6628 pLHS
->GetDoubleRef()->SetTrimToData(true);
6630 pRHS
->GetDoubleRef()->SetTrimToData(true);
6641 else if (eOpCode
== ocSumProduct
)
6643 FormulaToken
** ppTok
= pCode
- 2; // exclude the root operator.
6644 // The following loop runs till a "pattern" is found or there is a mismatch
6645 // and marks the push DoubleRef arguments as trimmable when there is a match.
6647 // SUMPRODUCT(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6648 // such that one of the operands of ocEqual is a double-ref.
6649 // Examples of formula that matches this are:
6650 // SUMPRODUCT(IF($A:$A=$L12;$D:$D*G:G))
6651 // Also in case of DoubleRef arguments around other Binary operators can be trimmable inside one parameter
6652 // of the root operator:
6653 // SUMPRODUCT(($D:$D>M47:M47)*($D:$D<M48:M48)*($I:$I=N$41))
6654 bool bTillClose
= true;
6655 bool bCloseTillIf
= false;
6656 sal_Int16 nToksTillIf
= 0;
6657 constexpr sal_Int16 MAXDIST_IF
= 15;
6660 FormulaToken
* pTok
= *ppTok
;
6661 OpCode eCurrOp
= pTok
->GetOpCode();
6664 // TODO : Is there a better way to handle this ?
6665 // ocIf is too far off from the sum opcode.
6666 if (nToksTillIf
> MAXDIST_IF
)
6674 if (!pTok
->IsInForceArray())
6676 FormulaToken
* pLHS
= *(ppTok
- 1);
6677 FormulaToken
* pRHS
= *(ppTok
- 2);
6678 StackVar lhsType
= pLHS
->GetType();
6679 StackVar rhsType
= pRHS
->GetType();
6680 if (lhsType
== svDoubleRef
&& rhsType
== svDoubleRef
)
6682 pLHS
->GetDoubleRef()->SetTrimToData(true);
6683 pRHS
->GetDoubleRef()->SetTrimToData(true);
6696 case ocGreaterEqual
:
6704 // tdf#160616: Double refs with these operators only
6705 // trimmable in case of one parameter
6706 if (!pTok
->IsInForceArray() || nRootParam
> 1)
6708 FormulaToken
* pLHS
= *(ppTok
- 1);
6709 FormulaToken
* pRHS
= *(ppTok
- 2);
6710 StackVar lhsType
= pLHS
->GetType();
6711 StackVar rhsType
= pRHS
->GetType();
6712 if (lhsType
== svDoubleRef
&& (rhsType
== svSingleRef
|| rhsType
== svDoubleRef
))
6714 pLHS
->GetDoubleRef()->SetTrimToData(true);
6716 if (rhsType
== svDoubleRef
&& (lhsType
== svSingleRef
|| lhsType
== svDoubleRef
))
6718 pRHS
->GetDoubleRef()->SetTrimToData(true);
6728 bCloseTillIf
= true;
6738 if (!pTok
->IsInForceArray())
6741 const short nJumpCount
= pTok
->GetJump()[0];
6742 if (nJumpCount
!= 2) // Should have THEN but no ELSE.
6745 OpCode eCompOp
= (*(ppTok
- 1))->GetOpCode();
6746 if (eCompOp
!= ocEqual
)
6749 FormulaToken
* pLHS
= *(ppTok
- 2);
6750 FormulaToken
* pRHS
= *(ppTok
- 3);
6751 StackVar lhsType
= pLHS
->GetType();
6752 StackVar rhsType
= pRHS
->GetType();
6753 if (lhsType
== svDoubleRef
&& (rhsType
== svSingleRef
|| rhsType
== svDouble
))
6755 pLHS
->GetDoubleRef()->SetTrimToData(true);
6757 if ((lhsType
== svSingleRef
|| lhsType
== svDouble
) && rhsType
== svDoubleRef
)
6759 pRHS
->GetDoubleRef()->SetTrimToData(true);
6772 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */