xref: /core/sc/source/core/tool/compiler.cxx (revision 6873e0a9)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <compiler.hxx>
23 
24 #include <vcl/svapp.hxx>
25 #include <sfx2/app.hxx>
26 #include <sfx2/objsh.hxx>
27 #include <basic/sbmeth.hxx>
28 #include <basic/sbstar.hxx>
29 #include <svl/zforlist.hxx>
30 #include <svl/sharedstringpool.hxx>
31 #include <sal/macros.h>
32 #include <sal/log.hxx>
33 #include <osl/diagnose.h>
34 #include <rtl/character.hxx>
35 #include <tools/solar.h>
36 #include <unotools/charclass.hxx>
37 #include <unotools/configmgr.hxx>
38 #include <com/sun/star/lang/Locale.hpp>
39 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
40 #include <com/sun/star/sheet/FormulaLanguage.hpp>
41 #include <com/sun/star/i18n/KParseTokens.hpp>
42 #include <com/sun/star/i18n/KParseType.hpp>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/string.hxx>
45 #include <unotools/transliterationwrapper.hxx>
46 #include <tools/urlobj.hxx>
47 #include <rtl/math.hxx>
48 #include <rtl/ustring.hxx>
49 #include <svtools/miscopt.hxx>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <math.h>
53 #include <rangenam.hxx>
54 #include <dbdata.hxx>
55 #include <document.hxx>
56 #include <callform.hxx>
57 #include <addincol.hxx>
58 #include <refupdat.hxx>
59 #include <sc.hrc>
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 
77 using namespace formula;
78 using namespace ::com::sun::star;
79 using ::std::vector;
80 
81 CharClass*                          ScCompiler::pCharClassEnglish = nullptr;
82 const ScCompiler::Convention*       ScCompiler::pConventions[ ]   = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
83 
84 namespace {
85 
86 enum ScanState
87 {
88     ssGetChar,
89     ssGetBool,
90     ssGetValue,
91     ssGetString,
92     ssSkipString,
93     ssGetIdent,
94     ssGetReference,
95     ssSkipReference,
96     ssGetErrorConstant,
97     ssGetTableRefItem,
98     ssGetTableRefColumn,
99     ssStop
100 };
101 
102 }
103 
104 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
105 
106 using namespace ::com::sun::star::i18n;
107 
108 void ScCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& xMap,FormulaGrammar::Grammar _eGrammar  ) const
109 {
110     size_t nSymbolOffset;
111     switch( _eGrammar )
112     {
113         case FormulaGrammar::GRAM_PODF:
114             nSymbolOffset = offsetof( AddInMap, pUpper);
115             break;
116         default:
117         case FormulaGrammar::GRAM_ODFF:
118             nSymbolOffset = offsetof( AddInMap, pODFF);
119             break;
120         case FormulaGrammar::GRAM_ENGLISH:
121             nSymbolOffset = offsetof( AddInMap, pEnglish);
122             break;
123     }
124     const AddInMap* pMap = g_aAddInMap;
125     const AddInMap* const pStop = pMap + GetAddInMapCount();
126     for ( ; pMap < pStop; ++pMap)
127     {
128         char const * const * ppSymbol =
129             reinterpret_cast< char const * const * >(
130                     reinterpret_cast< char const * >(pMap) + nSymbolOffset);
131         xMap->putExternal( OUString::createFromAscii( *ppSymbol),
132                 OUString::createFromAscii( pMap->pOriginal));
133     }
134 }
135 
136 void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& xMap ) const
137 {
138     ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
139     long nCount = pColl->GetFuncCount();
140     for (long i=0; i < nCount; ++i)
141     {
142         const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
143         if (pFuncData)
144             xMap->putExternalSoftly( pFuncData->GetUpperName(),
145                     pFuncData->GetOriginalName());
146     }
147 }
148 
149 void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& xMap ) const
150 {
151     ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
152     long nCount = pColl->GetFuncCount();
153     for (long i=0; i < nCount; ++i)
154     {
155         const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
156         if (pFuncData)
157         {
158             OUString aName;
159             if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
160                 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
161             else
162                 xMap->putExternalSoftly( pFuncData->GetUpperName(),
163                         pFuncData->GetOriginalName());
164         }
165     }
166 }
167 
168 void ScCompiler::DeInit()
169 {
170     if (pCharClassEnglish)
171     {
172         delete pCharClassEnglish;
173         pCharClassEnglish = nullptr;
174     }
175 }
176 
177 bool ScCompiler::IsEnglishSymbol( const OUString& rName )
178 {
179     // function names are always case-insensitive
180     OUString aUpper = ScGlobal::pCharClass->uppercase(rName);
181 
182     // 1. built-in function name
183     OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper );
184     if ( eOp != ocNone )
185     {
186         return true;
187     }
188     // 2. old add in functions
189     if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
190     {
191         return true;
192     }
193 
194     // 3. new (uno) add in functions
195     OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
196     return !aIntName.isEmpty();       // no valid function name
197 }
198 
199 void ScCompiler::InitCharClassEnglish()
200 {
201     css::lang::Locale aLocale( "en", "US", "");
202     pCharClassEnglish = new CharClass(
203             ::comphelper::getProcessComponentContext(), LanguageTag( aLocale));
204 }
205 
206 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
207 {
208     assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
209     if (eGrammar == GetGrammar())
210         return;     // nothing to be done
211 
212     if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
213     {
214         meGrammar = eGrammar;
215         mxSymbols = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
216     }
217     else
218     {
219         FormulaGrammar::Grammar eMyGrammar = eGrammar;
220         const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
221         OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
222         OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
223         if (!xMap)
224         {
225             xMap = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
226             eMyGrammar = xMap->getGrammar();
227         }
228 
229         // Save old grammar for call to SetGrammarAndRefConvention().
230         FormulaGrammar::Grammar eOldGrammar = GetGrammar();
231         // This also sets the grammar associated with the map!
232         SetFormulaLanguage( xMap);
233 
234         // Override if necessary.
235         if (eMyGrammar != GetGrammar())
236             SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
237     }
238 }
239 
240 // Unclear how this was intended to be refreshed when the
241 // grammar or sheet count is changed ? Ideally this would be
242 // a method on Document that would globally cache these.
243 std::vector<OUString> &ScCompiler::GetSetupTabNames() const
244 {
245     std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;
246 
247     if (pDoc && rTabNames.empty())
248     {
249         rTabNames = pDoc->GetAllTableNames();
250         for (auto& rTabName : rTabNames)
251             ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(meGrammar));
252     }
253 
254     return rTabNames;
255 }
256 
257 void ScCompiler::SetNumberFormatter( SvNumberFormatter* pFormatter )
258 {
259     mpFormatter = pFormatter;
260 }
261 
262 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
263 {
264     if (xMap.get())
265     {
266         mxSymbols = xMap;
267         if (mxSymbols->isEnglish())
268         {
269             if (!pCharClassEnglish)
270                 InitCharClassEnglish();
271             pCharClass = pCharClassEnglish;
272         }
273         else
274             pCharClass = ScGlobal::pCharClass;
275         SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
276     }
277 }
278 
279 void ScCompiler::SetGrammarAndRefConvention(
280         const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
281 {
282     meGrammar = eNewGrammar;    // SetRefConvention needs the new grammar set!
283     FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
284     if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
285     {
286         if (pDoc)
287             SetRefConvention( pDoc->GetAddressConvention());
288         else
289             SetRefConvention( GetRefConvention( FormulaGrammar::CONV_OOO ) );
290     }
291     else
292         SetRefConvention( eConv );
293 }
294 
295 OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
296 {
297     return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst);    // bLocalFirst=false for english
298 }
299 
300 ScCompiler::Convention::~Convention()
301 {
302 }
303 
304 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
305         :
306     meConv( eConv )
307 {
308     int i;
309     ScCharFlags *t= new ScCharFlags [128];
310 
311     ScCompiler::pConventions[ meConv ] = this;
312     mpCharTable.reset( t );
313 
314     for (i = 0; i < 128; i++)
315         t[i] = ScCharFlags::Illegal;
316 
317 // tdf#56036: Allow tabs/newlines in imported formulas (for now simply treat them as (and convert to) space)
318 // TODO: tdf#76310: allow saving newlines as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace")
319 // This is compliant with the OASIS decision (see https://issues.oasis-open.org/browse/OFFICE-701)
320 // Also, this would enable correct roundtrip from/to OOXML without losing tabs/newlines
321 // This requires saving actual space characters in ocSpaces token, using them in UI and saving
322 /* tab */   t[ 9] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
323 /* lf  */   t[10] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
324 /* cr  */   t[13] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
325 
326 /*   */     t[32] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
327 /* ! */     t[33] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
328             if (FormulaGrammar::CONV_ODF == meConv)
329 /* ! */         t[33] |= ScCharFlags::OdfLabelOp;
330 /* " */     t[34] = ScCharFlags::CharString | ScCharFlags::StringSep;
331 /* # */     t[35] = ScCharFlags::WordSep | ScCharFlags::CharErrConst;
332 /* $ */     t[36] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident;
333             if (FormulaGrammar::CONV_ODF == meConv)
334 /* $ */         t[36] |= ScCharFlags::OdfNameMarker;
335 /* % */     t[37] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
336 /* & */     t[38] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
337 /* ' */     t[39] = ScCharFlags::NameSep;
338 /* ( */     t[40] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
339 /* ) */     t[41] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
340 /* * */     t[42] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
341 /* + */     t[43] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
342 /* , */     t[44] = ScCharFlags::CharValue | ScCharFlags::Value;
343 /* - */     t[45] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
344 /* . */     t[46] = ScCharFlags::Word | ScCharFlags::CharValue | ScCharFlags::Value | ScCharFlags::Ident | ScCharFlags::Name;
345 /* / */     t[47] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
346 
347             for (i = 48; i < 58; i++)
348 /* 0-9 */       t[i] = ScCharFlags::CharValue | ScCharFlags::Word | ScCharFlags::Value | ScCharFlags::ValueExp | ScCharFlags::ValueValue | ScCharFlags::Ident | ScCharFlags::Name;
349 
350 /* : */     t[58] = ScCharFlags::Char | ScCharFlags::Word;
351 /* ; */     t[59] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
352 /* < */     t[60] = ScCharFlags::CharBool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
353 /* = */     t[61] = ScCharFlags::Char | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
354 /* > */     t[62] = ScCharFlags::CharBool | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
355 /* ? */     t[63] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::Name;
356 /* @ */     // FREE
357 
358     for (i = 65; i < 91; i++)
359 /* A-Z */   t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
360 
361     if (FormulaGrammar::CONV_ODF == meConv)
362     {
363 /* [ */     t[91] = ScCharFlags::OdfLBracket;
364 /* \ */     // FREE
365 /* ] */     t[93] = ScCharFlags::OdfRBracket;
366     }
367     else if (FormulaGrammar::CONV_OOO == meConv)
368     {
369 /* [ */     t[91] = ScCharFlags::Char;
370 /* \ */     // FREE
371 /* ] */     t[93] = ScCharFlags::Char;
372     }
373     else if (FormulaGrammar::CONV_XL_OOX == meConv)
374     {
375 /* [ */     t[91] = ScCharFlags::Char | ScCharFlags::CharIdent;
376 /* \ */     // FREE
377 /* ] */     t[93] = ScCharFlags::Char | ScCharFlags::Ident;
378     }
379     else if (FormulaGrammar::CONV_XL_A1 == meConv)
380     {
381 /* [ */     t[91] = ScCharFlags::Char;
382 /* \ */     // FREE
383 /* ] */     t[93] = ScCharFlags::Char;
384     }
385     else if( FormulaGrammar::CONV_XL_R1C1 == meConv )
386     {
387 /* [ */     t[91] = ScCharFlags::Ident;
388 /* \ */     // FREE
389 /* ] */     t[93] = ScCharFlags::Ident;
390     }
391     else
392     {
393 /* [ */     // FREE
394 /* \ */     // FREE
395 /* ] */     // FREE
396     }
397 
398 /* ^ */     t[94] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
399 /* _ */     t[95] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
400 /* ` */     // FREE
401 
402             for (i = 97; i < 123; i++)
403 /* a-z */       t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
404 
405 /* { */     t[123] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open
406 /* | */     t[124] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific)
407 /* } */     t[125] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close
408 /* ~ */     t[126] = ScCharFlags::Char;        // OOo specific
409 /* 127 */   // FREE
410 
411     if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
412     {
413 /*   */     t[32] |=   ScCharFlags::Word;
414 /* ! */     t[33] |=   ScCharFlags::Ident | ScCharFlags::Word;
415 /* " */     t[34] |=   ScCharFlags::Word;
416 /* # */     t[35] &=  ~ScCharFlags::WordSep;
417 /* # */     t[35] |=   ScCharFlags::Word;
418 /* % */     t[37] |=   ScCharFlags::Word;
419 /* ' */     t[39] |=   ScCharFlags::Word;
420 
421 /* % */     t[37] |=   ScCharFlags::Word;
422 /* & */     t[38] |=   ScCharFlags::Word;
423 /* ' */     t[39] |=   ScCharFlags::Word;
424 /* ( */     t[40] |=   ScCharFlags::Word;
425 /* ) */     t[41] |=   ScCharFlags::Word;
426 /* * */     t[42] |=   ScCharFlags::Word;
427 /* + */     t[43] |=   ScCharFlags::Word;
428 #if 0 /* this really needs to be locale specific. */
429 /* , */     t[44]  =   ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
430 #else
431 /* , */     t[44] |=   ScCharFlags::Word;
432 #endif
433 /* - */     t[45] |=   ScCharFlags::Word;
434 
435 /* ; */     t[59] |=   ScCharFlags::Word;
436 /* < */     t[60] |=   ScCharFlags::Word;
437 /* = */     t[61] |=   ScCharFlags::Word;
438 /* > */     t[62] |=   ScCharFlags::Word;
439 /* ? */     // question really is not permitted in sheet name
440 /* @ */     t[64] |=   ScCharFlags::Word;
441 /* [ */     t[91] |=   ScCharFlags::Word;
442 /* ] */     t[93] |=   ScCharFlags::Word;
443 /* { */     t[123]|=   ScCharFlags::Word;
444 /* | */     t[124]|=   ScCharFlags::Word;
445 /* } */     t[125]|=   ScCharFlags::Word;
446 /* ~ */     t[126]|=   ScCharFlags::Word;
447     }
448 }
449 
450 static bool lcl_isValidQuotedText( const OUString& rFormula, sal_Int32 nSrcPos, ParseResult& rRes )
451 {
452     // Tokens that start at ' can have anything in them until a final '
453     // but '' marks an escaped '
454     // We've earlier guaranteed that a string containing '' will be
455     // surrounded by '
456     if (nSrcPos < rFormula.getLength() && rFormula[nSrcPos] == '\'')
457     {
458         sal_Int32 nPos = nSrcPos+1;
459         while (nPos < rFormula.getLength())
460         {
461             if (rFormula[nPos] == '\'')
462             {
463                 if ( (nPos+1 == rFormula.getLength()) || (rFormula[nPos+1] != '\'') )
464                 {
465                     rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
466                     rRes.EndPos = nPos+1;
467                     return true;
468                 }
469                 ++nPos;
470             }
471             ++nPos;
472         }
473     }
474 
475     return false;
476 }
477 
478 static bool lcl_parseExternalName(
479         const OUString& rSymbol,
480         OUString& rFile,
481         OUString& rName,
482         const sal_Unicode cSep,
483         const ScDocument* pDoc,
484         const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
485 {
486     /* TODO: future versions will have to support sheet-local names too, thus
487      * return a possible sheet name as well. */
488     const sal_Unicode* const pStart = rSymbol.getStr();
489     const sal_Unicode* p = pStart;
490     sal_Int32 nLen = rSymbol.getLength();
491     OUString aTmpFile;
492     OUStringBuffer aTmpName;
493     sal_Int32 i = 0;
494     bool bInName = false;
495     if (cSep == '!')
496     {
497         // For XL use existing parser that resolves bracketed and quoted and
498         // indexed external document names.
499         ScRange aRange;
500         OUString aStartTabName, aEndTabName;
501         ScRefFlags nFlags = ScRefFlags::ZERO;
502         p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
503                 aEndTabName, nFlags, true, pExternalLinks );
504         if (!p || p == pStart)
505             return false;
506         i = sal_Int32(p - pStart);
507     }
508     for ( ; i < nLen; ++i, ++p)
509     {
510         sal_Unicode c = *p;
511         if (i == 0)
512         {
513             if (c == '.' || c == cSep)
514                 return false;
515 
516             if (c == '\'')
517             {
518                 // Move to the next char and loop until the second single
519                 // quote.
520                 sal_Unicode cPrev = c;
521                 ++i; ++p;
522                 for (sal_Int32 j = i; j < nLen; ++j, ++p)
523                 {
524                     c = *p;
525                     if (c == '\'')
526                     {
527                         if (j == i)
528                         {
529                             // empty quote e.g. (=''!Name)
530                             return false;
531                         }
532 
533                         if (cPrev == '\'')
534                         {
535                             // two consecutive quotes equal a single quote in
536                             // the file name.
537                             aTmpFile += OUStringChar(c);
538                             cPrev = 'a';
539                         }
540                         else
541                             cPrev = c;
542 
543                         continue;
544                     }
545 
546                     if (cPrev == '\'' && j != i)
547                     {
548                         // this is not a quote but the previous one is.  This
549                         // ends the parsing of the quoted segment.  At this
550                         // point, the current char must equal the separator
551                         // char.
552 
553                         i = j;
554                         bInName = true;
555                         aTmpName.append(c); // Keep the separator as part of the name.
556                         break;
557                     }
558                     aTmpFile += OUStringChar(c);
559                     cPrev = c;
560                 }
561 
562                 if (!bInName)
563                 {
564                     // premature ending of the quoted segment.
565                     return false;
566                 }
567 
568                 if (c != cSep)
569                 {
570                     // only the separator is allowed after the closing quote.
571                     return false;
572                 }
573 
574                 continue;
575             }
576         }
577 
578         if (bInName)
579         {
580             if (c == cSep)
581             {
582                 // A second separator ?  Not a valid external name.
583                 return false;
584             }
585             aTmpName.append(c);
586         }
587         else
588         {
589             if (c == cSep)
590             {
591                 bInName = true;
592                 aTmpName.append(c); // Keep the separator as part of the name.
593             }
594             else
595             {
596                 do
597                 {
598                     if (rtl::isAsciiAlphanumeric(c))
599                         // allowed.
600                         break;
601 
602                     if (c > 128)
603                         // non-ASCII character is allowed.
604                         break;
605 
606                     bool bValid = false;
607                     switch (c)
608                     {
609                         case '_':
610                         case '-':
611                         case '.':
612                             // these special characters are allowed.
613                             bValid = true;
614                             break;
615                     }
616                     if (bValid)
617                         break;
618 
619                     return false;
620                 }
621                 while (false);
622                 aTmpFile += OUStringChar(c);
623             }
624         }
625     }
626 
627     if (!bInName)
628     {
629         // No name found - most likely the symbol has no '!'s.
630         return false;
631     }
632 
633     sal_Int32 nNameLen = aTmpName.getLength();
634     if (nNameLen < 2)
635     {
636         // Name must be at least 2-char long (separator plus name).
637         return false;
638     }
639 
640     if (aTmpName[0] != cSep)
641     {
642         // 1st char of the name must equal the separator.
643         return false;
644     }
645 
646     if (aTmpName[nNameLen-1] == '!')
647     {
648         // Check against #REF!.
649         if (aTmpName.toString().equalsIgnoreAsciiCase("#REF!"))
650             return false;
651     }
652 
653     rFile = aTmpFile;
654     rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
655     return true;
656 }
657 
658 static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
659         const sal_Unicode cSep, bool bODF )
660 {
661     OUString aEscQuote("''");
662     OUString aFile(rFile.replaceAll("'", aEscQuote));
663     OUString aName(rName);
664     if (bODF)
665         aName = aName.replaceAll("'", aEscQuote);
666     OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
667     if (bODF)
668         aBuf.append( '[');
669     aBuf.append( "'" ).append( aFile ).append( "'" ).append( OUStringChar(cSep) );
670     if (bODF)
671         aBuf.append( "$$'" );
672     aBuf.append( aName);
673     if (bODF)
674         aBuf.append( "']" );
675     return aBuf.makeStringAndClear();
676 }
677 
678 static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
679                                 const vector<OUString>& rTabNames, const ScRange& rRef )
680 {
681     SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
682     if (nTabSpan > 0)
683     {
684         size_t nCount = rTabNames.size();
685         vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
686         vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
687         if (itr == rTabNames.end())
688         {
689             rTabName2 = ScResId(STR_NO_REF_TABLE);
690             return false;
691         }
692 
693         size_t nDist = ::std::distance(itrBeg, itr);
694         if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
695         {
696             rTabName2 = ScResId(STR_NO_REF_TABLE);
697             return false;
698         }
699 
700         rTabName2 = rTabNames[nDist+nTabSpan];
701     }
702     else
703         rTabName2 = rTabName1;
704 
705     return true;
706 }
707 
708 namespace {
709 
710 struct Convention_A1 : public ScCompiler::Convention
711 {
712     explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
713     static void MakeColStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol );
714     static void MakeRowStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow );
715 
716     ParseResult parseAnyToken( const OUString& rFormula,
717                                sal_Int32 nSrcPos,
718                                const CharClass* pCharClass,
719                                bool bGroupSeparator) const override
720     {
721         ParseResult aRet;
722         if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
723             return aRet;
724 
725         constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
726             KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
727         constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
728         // '?' allowed in range names because of Xcl :-/
729         static const OUString aAddAllowed("?#");
730         return pCharClass->parseAnyToken( rFormula,
731                 nSrcPos, nStartFlags, aAddAllowed,
732                 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
733                 aAddAllowed );
734     }
735 
736     virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override
737     {
738         return mpCharTable[static_cast<sal_uInt8>(c)];
739     }
740 };
741 
742 }
743 
744 void Convention_A1::MakeColStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol )
745 {
746     if ( !rLimits.ValidCol(nCol) )
747         rBuffer.append(ScResId(STR_NO_REF_TABLE));
748     else
749         ::ScColToAlpha( rBuffer, nCol);
750 }
751 
752 void Convention_A1::MakeRowStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow )
753 {
754     if ( !rLimits.ValidRow(nRow) )
755         rBuffer.append(ScResId(STR_NO_REF_TABLE));
756     else
757         rBuffer.append(sal_Int32(nRow + 1));
758 }
759 
760 namespace {
761 
762 struct ConventionOOO_A1 : public Convention_A1
763 {
764     ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
765     explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
766 
767     static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
768     {
769         if (static_cast<size_t>(nTab) >= rTabNames.size())
770             rBuf.append(ScResId(STR_NO_REF_TABLE));
771         else
772             rBuf.append(rTabNames[nTab]);
773         rBuf.append('.');
774     }
775 
776     enum SingletonDisplay
777     {
778         SINGLETON_NONE,
779         SINGLETON_COL,
780         SINGLETON_ROW
781     };
782 
783     static void MakeOneRefStrImpl(
784         ScSheetLimits& rLimits, OUStringBuffer& rBuffer,
785         const OUString& rErrRef, const std::vector<OUString>& rTabNames,
786         const ScSingleRefData& rRef, const ScAddress& rAbsRef,
787         bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
788     {
789         // For ODF override singleton so earlier releases still can read what
790         // we write now as of 2015-06-26.
791         /* TODO: we may want to change that in future in a few releases. */
792         if (bODF)
793             eSingletonDisplay = SINGLETON_NONE;
794 
795         if( rRef.IsFlag3D() || bForceTab )
796         {
797             if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
798             {
799                 if (!rRef.IsTabRel())
800                     rBuffer.append('$');
801                 rBuffer.append(rErrRef);
802                 rBuffer.append('.');
803             }
804             else
805             {
806                 if (!rRef.IsTabRel())
807                     rBuffer.append('$');
808                 MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
809             }
810         }
811         else if (bODF)
812             rBuffer.append('.');
813 
814         if (eSingletonDisplay != SINGLETON_ROW)
815         {
816             if (!rRef.IsColRel())
817                 rBuffer.append('$');
818             if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
819                 rBuffer.append(rErrRef);
820             else
821                 MakeColStr(rLimits, rBuffer, rAbsRef.Col());
822         }
823 
824         if (eSingletonDisplay != SINGLETON_COL)
825         {
826             if (!rRef.IsRowRel())
827                 rBuffer.append('$');
828             if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
829                 rBuffer.append(rErrRef);
830             else
831                 MakeRowStr(rLimits, rBuffer, rAbsRef.Row());
832         }
833     }
834 
835     static SingletonDisplay getSingletonDisplay( ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2,
836             const ScComplexRefData& rRef, bool bFromRangeName )
837     {
838         // If any part is error, display as such.
839         if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
840             !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
841             return SINGLETON_NONE;
842 
843         // A:A or $A:$A or A:$A or $A:A
844         if (rRef.IsEntireCol())
845             return SINGLETON_COL;
846 
847         // Same if not in named expression and both rows of entire columns are
848         // relative references.
849         if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow &&
850                 rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel())
851             return SINGLETON_COL;
852 
853         // 1:1 or $1:$1 or 1:$1 or $1:1
854         if (rRef.IsEntireRow())
855             return SINGLETON_ROW;
856 
857         // Same if not in named expression and both columns of entire rows are
858         // relative references.
859         if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol &&
860                 rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel())
861             return SINGLETON_ROW;
862 
863         return SINGLETON_NONE;
864     }
865 
866     virtual void makeRefStr(
867                      ScSheetLimits& rLimits,
868                      OUStringBuffer&   rBuffer,
869                      formula::FormulaGrammar::Grammar /*eGram*/,
870                      const ScAddress& rPos,
871                      const OUString& rErrRef, const std::vector<OUString>& rTabNames,
872                      const ScComplexRefData& rRef,
873                      bool bSingleRef,
874                      bool bFromRangeName ) const override
875     {
876         // In case absolute/relative positions weren't separately available:
877         // transform relative to absolute!
878         ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
879         if( !bSingleRef )
880             aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
881 
882         SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
883             getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
884         MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton);
885         if (!bSingleRef)
886         {
887             rBuffer.append(':');
888             MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
889                     eSingleton);
890         }
891     }
892 
893     virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
894     {
895         switch (eSymType)
896         {
897             case ScCompiler::Convention::ABS_SHEET_PREFIX:
898                 return '$';
899             case ScCompiler::Convention::SHEET_SEPARATOR:
900                 return '.';
901         }
902 
903         return u'\0';
904     }
905 
906     virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
907             const ScDocument* pDoc,
908             const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
909     {
910         return lcl_parseExternalName(rSymbol, rFile, rName, '#', pDoc, pExternalLinks);
911     }
912 
913     virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
914             const OUString& rName ) const override
915     {
916         return lcl_makeExternalNameStr( rFile, rName, '#', false);
917     }
918 
919     static bool makeExternalSingleRefStr(
920         ScSheetLimits& rLimits,
921         OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
922         const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
923     {
924         ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
925         if (bDisplayTabName)
926         {
927             OUString aFile;
928             if (bEncodeUrl)
929                 aFile = rFileName;
930             else
931                 aFile = INetURLObject::decode(rFileName, INetURLObject::DecodeMechanism::Unambiguous);
932 
933             rBuffer.append("'").append(aFile.replaceAll("'", "''")).append("'#");
934 
935             if (!rRef.IsTabRel())
936                 rBuffer.append('$');
937             ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
938 
939             rBuffer.append('.');
940         }
941 
942         if (!rRef.IsColRel())
943             rBuffer.append('$');
944         MakeColStr( rLimits, rBuffer, aAbsRef.Col());
945         if (!rRef.IsRowRel())
946             rBuffer.append('$');
947         MakeRowStr( rLimits, rBuffer, aAbsRef.Row());
948 
949         return true;
950     }
951 
952     static void makeExternalRefStrImpl(
953         ScSheetLimits& rLimits,
954         OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
955         const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
956     {
957         if (bODF)
958             rBuffer.append( '[');
959 
960         bool bEncodeUrl = bODF;
961         makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
962         if (bODF)
963             rBuffer.append( ']');
964     }
965 
966     virtual void makeExternalRefStr(
967         ScSheetLimits& rLimits,
968         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
969         const OUString& rTabName, const ScSingleRefData& rRef ) const override
970     {
971         makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false);
972     }
973 
974     static void makeExternalRefStrImpl(
975         ScSheetLimits& rLimits,
976         OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
977         const std::vector<OUString>& rTabNames, const OUString& rTabName,
978         const ScComplexRefData& rRef, bool bODF )
979     {
980         ScRange aAbsRange = rRef.toAbs(rLimits, rPos);
981 
982         if (bODF)
983             rBuffer.append( '[');
984         // Ensure that there's always a closing bracket, no premature returns.
985         bool bEncodeUrl = bODF;
986 
987         do
988         {
989             if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
990                 break;
991 
992             rBuffer.append(':');
993 
994             OUString aLastTabName;
995             bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
996             if (bDisplayTabName)
997             {
998                 // Get the name of the last table.
999                 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
1000                 {
1001                     OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1002                     // aLastTabName contains #REF!, proceed.
1003                 }
1004             }
1005             else if (bODF)
1006                 rBuffer.append( '.');      // need at least the sheet separator in ODF
1007             makeExternalSingleRefStr(rLimits,
1008                 rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
1009         } while (false);
1010 
1011         if (bODF)
1012             rBuffer.append( ']');
1013     }
1014 
1015     virtual void makeExternalRefStr(
1016         ScSheetLimits& rLimits,
1017         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1018         const std::vector<OUString>& rTabNames, const OUString& rTabName,
1019         const ScComplexRefData& rRef ) const override
1020     {
1021         makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
1022     }
1023 };
1024 
1025 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
1026 {
1027     ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
1028 
1029     virtual void makeRefStr(
1030                      ScSheetLimits& rLimits,
1031                      OUStringBuffer&   rBuffer,
1032                      formula::FormulaGrammar::Grammar eGram,
1033                      const ScAddress& rPos,
1034                      const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1035                      const ScComplexRefData& rRef,
1036                      bool bSingleRef,
1037                      bool bFromRangeName ) const override
1038     {
1039         rBuffer.append('[');
1040         // In case absolute/relative positions weren't separately available:
1041         // transform relative to absolute!
1042         ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1043         if( !bSingleRef )
1044             aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
1045 
1046         if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) ||
1047                     (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2)))))
1048         {
1049             rBuffer.append(rErrRef);
1050             // For ODFF write [#REF!], but not for PODF so apps reading ODF
1051             // 1.0/1.1 may have a better chance if they implemented the old
1052             // form.
1053         }
1054         else
1055         {
1056             SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
1057                 getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
1058             MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton);
1059             if (!bSingleRef)
1060             {
1061                 rBuffer.append(':');
1062                 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
1063                         eSingleton);
1064             }
1065         }
1066         rBuffer.append(']');
1067     }
1068 
1069     virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1070             const OUString& rName ) const override
1071     {
1072         return lcl_makeExternalNameStr( rFile, rName, '#', true);
1073     }
1074 
1075     virtual void makeExternalRefStr(
1076         ScSheetLimits& rLimits,
1077         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1078         const OUString& rTabName, const ScSingleRefData& rRef ) const override
1079     {
1080         makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true);
1081     }
1082 
1083     virtual void makeExternalRefStr(
1084         ScSheetLimits& rLimits,
1085         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1086         const std::vector<OUString>& rTabNames,
1087         const OUString& rTabName, const ScComplexRefData& rRef ) const override
1088     {
1089         makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
1090     }
1091 };
1092 
1093 struct ConventionXL
1094 {
1095     virtual ~ConventionXL()
1096     {
1097     }
1098 
1099     static void GetTab(
1100         ScSheetLimits& rLimits,
1101         const ScAddress& rPos, const std::vector<OUString>& rTabNames,
1102         const ScSingleRefData& rRef, OUString& rTabName )
1103     {
1104         ScAddress aAbs = rRef.toAbs(rLimits, rPos);
1105         if (rRef.IsTabDeleted() || static_cast<size_t>(aAbs.Tab()) >= rTabNames.size())
1106         {
1107             rTabName = ScResId( STR_NO_REF_TABLE );
1108             return;
1109         }
1110         rTabName = rTabNames[aAbs.Tab()];
1111     }
1112 
1113     static void MakeTabStr( ScSheetLimits& rLimits, OUStringBuffer& rBuf,
1114                             const ScAddress& rPos,
1115                             const std::vector<OUString>& rTabNames,
1116                             const ScComplexRefData& rRef,
1117                             bool bSingleRef )
1118     {
1119         if( rRef.Ref1.IsFlag3D() )
1120         {
1121             OUString aStartTabName, aEndTabName;
1122 
1123             GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName);
1124 
1125             if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1126             {
1127                 GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName);
1128             }
1129 
1130             rBuf.append( aStartTabName );
1131             if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1132             {
1133                 rBuf.append( ':' );
1134                 rBuf.append( aEndTabName );
1135             }
1136 
1137             rBuf.append( '!' );
1138         }
1139     }
1140 
1141     static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1142     {
1143         switch (eSymType)
1144         {
1145             case ScCompiler::Convention::ABS_SHEET_PREFIX:
1146                 return u'\0';
1147             case ScCompiler::Convention::SHEET_SEPARATOR:
1148                 return '!';
1149         }
1150         return u'\0';
1151     }
1152 
1153     static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1154             const ScDocument* pDoc,
1155             const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1156     {
1157         return lcl_parseExternalName( rSymbol, rFile, rName, '!', pDoc, pExternalLinks);
1158     }
1159 
1160     static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
1161     {
1162         return lcl_makeExternalNameStr( rFile, rName, '!', false);
1163     }
1164 
1165     static void makeExternalDocStr( OUStringBuffer& rBuffer, const OUString& rFullName )
1166     {
1167         // Format that is easier to deal with inside OOo, because we use file
1168         // URL, and all characters are allowed.  Check if it makes sense to do
1169         // it the way Gnumeric does it.  Gnumeric doesn't use the URL form
1170         // and allows relative file path.
1171         //
1172         //   ['file:///path/to/source/filename.xls']
1173 
1174         rBuffer.append('[');
1175         rBuffer.append('\'');
1176         OUString aFullName = INetURLObject::decode(rFullName, INetURLObject::DecodeMechanism::Unambiguous);
1177 
1178         const sal_Unicode* pBuf = aFullName.getStr();
1179         sal_Int32 nLen = aFullName.getLength();
1180         for (sal_Int32 i = 0; i < nLen; ++i)
1181         {
1182             const sal_Unicode c = pBuf[i];
1183             if (c == '\'')
1184                 rBuffer.append(c);
1185             rBuffer.append(c);
1186         }
1187         rBuffer.append('\'');
1188         rBuffer.append(']');
1189     }
1190 
1191     static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
1192                                           const vector<OUString>& rTabNames,
1193                                           const ScRange& rRef )
1194     {
1195         OUString aLastTabName;
1196         if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1197         {
1198             ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1199             return;
1200         }
1201 
1202         ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1203         if (rTabName != aLastTabName)
1204         {
1205             rBuf.append(':');
1206             ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1207         }
1208     }
1209 
1210     virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const
1211     {
1212         sal_Int32 nLen = rFormula.getLength();
1213         const sal_Unicode* p = rFormula.getStr();
1214         sal_Unicode cPrev = 0;
1215         for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1216         {
1217             sal_Unicode c = p[i];
1218             if (i == rSrcPos)
1219             {
1220                 // first character must be '['.
1221                 if (c != '[')
1222                     return;
1223             }
1224             else if (i == rSrcPos + 1)
1225             {
1226                 // second character must be a single quote.
1227                 if (c != '\'')
1228                     return;
1229             }
1230             else if (c == '\'')
1231             {
1232                 if (cPrev == '\'')
1233                     // two successive single quote is treated as a single
1234                     // valid character.
1235                     c = 'a';
1236             }
1237             else if (c == ']')
1238             {
1239                 if (cPrev == '\'')
1240                 {
1241                     // valid source document path found.  Increment the
1242                     // current position to skip the source path.
1243                     rSrcPos = i + 1;
1244                     if (rSrcPos >= nLen)
1245                         rSrcPos = nLen - 1;
1246                     return;
1247                 }
1248                 else
1249                     return;
1250             }
1251             else
1252             {
1253                 // any other character
1254                 if (i > rSrcPos + 2 && cPrev == '\'')
1255                     // unless it's the 3rd character, a normal character
1256                     // following immediately a single quote is invalid.
1257                     return;
1258             }
1259             cPrev = c;
1260         }
1261     }
1262 };
1263 
1264 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1265 {
1266     ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1267     explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1268 
1269     static void makeSingleCellStr( ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
1270     {
1271         if (!rRef.IsColRel())
1272             rBuf.append('$');
1273         MakeColStr(rLimits, rBuf, rAbs.Col());
1274         if (!rRef.IsRowRel())
1275             rBuf.append('$');
1276         MakeRowStr(rLimits, rBuf, rAbs.Row());
1277     }
1278 
1279     virtual void makeRefStr(
1280                      ScSheetLimits& rLimits,
1281                      OUStringBuffer&   rBuf,
1282                      formula::FormulaGrammar::Grammar /*eGram*/,
1283                      const ScAddress& rPos,
1284                      const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1285                      const ScComplexRefData& rRef,
1286                      bool bSingleRef,
1287                      bool /*bFromRangeName*/ ) const override
1288     {
1289         ScComplexRefData aRef( rRef );
1290 
1291         // Play fast and loose with invalid refs.  There is not much point in producing
1292         // Foo!A1:#REF! versus #REF! at this point
1293         ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1294 
1295         MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1296 
1297         if (!rLimits.ValidAddress(aAbs1))
1298         {
1299             rBuf.append(rErrRef);
1300             return;
1301         }
1302 
1303         if( !bSingleRef )
1304         {
1305             aAbs2 = aRef.Ref2.toAbs(rLimits, rPos);
1306             if (!rLimits.ValidAddress(aAbs2))
1307             {
1308                 rBuf.append(rErrRef);
1309                 return;
1310             }
1311 
1312             if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol)
1313             {
1314                 if (!aRef.Ref1.IsRowRel())
1315                     rBuf.append( '$' );
1316                 MakeRowStr(rLimits, rBuf, aAbs1.Row());
1317                 rBuf.append( ':' );
1318                 if (!aRef.Ref2.IsRowRel())
1319                     rBuf.append( '$' );
1320                 MakeRowStr(rLimits, rBuf, aAbs2.Row());
1321                 return;
1322             }
1323 
1324             if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow)
1325             {
1326                 if (!aRef.Ref1.IsColRel())
1327                     rBuf.append( '$' );
1328                 MakeColStr(rLimits, rBuf, aAbs1.Col());
1329                 rBuf.append( ':' );
1330                 if (!aRef.Ref2.IsColRel())
1331                     rBuf.append( '$' );
1332                 MakeColStr(rLimits, rBuf, aAbs2.Col());
1333                 return;
1334             }
1335         }
1336 
1337         makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1);
1338         if (!bSingleRef)
1339         {
1340             rBuf.append( ':' );
1341             makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2);
1342         }
1343     }
1344 
1345     virtual ParseResult parseAnyToken( const OUString& rFormula,
1346                                        sal_Int32 nSrcPos,
1347                                        const CharClass* pCharClass,
1348                                        bool bGroupSeparator) const override
1349     {
1350         parseExternalDocName(rFormula, nSrcPos);
1351 
1352         ParseResult aRet;
1353         if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1354             return aRet;
1355 
1356         constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1357             KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1358         constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1359         // '?' allowed in range names
1360         static const OUString aAddAllowed("?!");
1361         return pCharClass->parseAnyToken( rFormula,
1362                 nSrcPos, nStartFlags, aAddAllowed,
1363                 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1364                 aAddAllowed );
1365     }
1366 
1367     virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1368     {
1369         return ConventionXL::getSpecialSymbol(eSymType);
1370     }
1371 
1372     virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1373             const ScDocument* pDoc,
1374             const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1375     {
1376         return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1377     }
1378 
1379     virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1380             const OUString& rName ) const override
1381     {
1382         return ConventionXL::makeExternalNameStr(rFile, rName);
1383     }
1384 
1385     virtual void makeExternalRefStr(
1386         ScSheetLimits& rLimits,
1387         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1388         const OUString& rTabName, const ScSingleRefData& rRef ) const override
1389     {
1390         // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1391         // This is a little different from the format Excel uses, as Excel
1392         // puts [] only around the file name.  But we need to enclose the
1393         // whole file path with [] because the file name can contain any
1394         // characters.
1395 
1396         ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1397         ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1398         rBuffer.append('!');
1399 
1400         makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1401     }
1402 
1403     virtual void makeExternalRefStr(
1404         ScSheetLimits& rLimits,
1405         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1406         const std::vector<OUString>& rTabNames, const OUString& rTabName,
1407         const ScComplexRefData& rRef ) const override
1408     {
1409         ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1410 
1411         ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1412         ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1413         rBuffer.append('!');
1414 
1415         makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1416         if (aAbsRef.aStart != aAbsRef.aEnd)
1417         {
1418             rBuffer.append(':');
1419             makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1420         }
1421     }
1422 };
1423 
1424 struct ConventionXL_OOX : public ConventionXL_A1
1425 {
1426     ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1427 
1428     virtual void makeRefStr( ScSheetLimits& rLimits,
1429                      OUStringBuffer&   rBuf,
1430                      formula::FormulaGrammar::Grammar eGram,
1431                      const ScAddress& rPos,
1432                      const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1433                      const ScComplexRefData& rRef,
1434                      bool bSingleRef,
1435                      bool bFromRangeName ) const override
1436     {
1437         // In OOXML relative references in named expressions are relative to
1438         // column 0 and row 0. Relative sheet references don't exist.
1439         ScAddress aPos( rPos );
1440         if (bFromRangeName)
1441         {
1442             // XXX NOTE: by decrementing the reference position we may end up
1443             // with resolved references with negative values. There's no proper
1444             // way to solve that or wrap them around without sheet dimensions
1445             // that are stored along. That, or blindly assume fixed dimensions
1446             // here and in import.
1447             /* TODO: maybe do that blind fixed dimensions wrap? */
1448             aPos.SetCol(0);
1449             aPos.SetRow(0);
1450         }
1451 
1452         if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted()))
1453         {
1454             // For OOXML write plain "#REF!" instead of detailed sheet/col/row
1455             // information.
1456             rBuf.append(rErrRef);
1457             return;
1458         }
1459 
1460         ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
1461     }
1462 
1463     virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
1464             const OUString& rName ) const override
1465     {
1466         // [N]!DefinedName is a workbook global name.
1467         return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );
1468 
1469         /* TODO: add support for sheet local names, would be
1470          * [N]'Sheet Name'!DefinedName
1471          * Similar to makeExternalRefStr() but with DefinedName instead of
1472          * CellStr. */
1473     }
1474 
1475     virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const override
1476     {
1477         sal_Int32 nLen = rFormula.getLength();
1478         const sal_Unicode* p = rFormula.getStr();
1479         for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1480         {
1481             sal_Unicode c = p[i];
1482             if (i == rSrcPos)
1483             {
1484                 // first character must be '['.
1485                 if (c != '[')
1486                     return;
1487             }
1488             else if (c == ']')
1489             {
1490                 rSrcPos = i + 1;
1491                 return;
1492             }
1493         }
1494     }
1495 
1496     virtual void makeExternalRefStr(
1497         ScSheetLimits& rLimits,
1498         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1499         const OUString& rTabName, const ScSingleRefData& rRef ) const override
1500     {
1501         // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
1502         // Where N is a 1-based positive integer number of a file name in OOXML
1503         // xl/externalLinks/externalLinkN.xml
1504 
1505         OUString aQuotedTab( rTabName);
1506         ScCompiler::CheckTabQuotes( aQuotedTab);
1507         if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'')
1508         {
1509             rBuffer.append('\'');
1510             ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1511             rBuffer.append( aQuotedTab.copy(1));
1512         }
1513         else
1514         {
1515             ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1516             rBuffer.append( aQuotedTab);
1517         }
1518         rBuffer.append('!');
1519 
1520         makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1521     }
1522 
1523     virtual void makeExternalRefStr(
1524         ScSheetLimits& rLimits,
1525         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1526         const std::vector<OUString>& rTabNames, const OUString& rTabName,
1527         const ScComplexRefData& rRef ) const override
1528     {
1529         // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
1530         // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
1531         // simpler to produce and more logical form with independently quoted
1532         // sheet names as well. The [N] having to be within the quoted sheet
1533         // name is ugly enough...
1534 
1535         ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1536 
1537         OUStringBuffer aBuf;
1538         ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef);
1539         if (!aBuf.isEmpty() && aBuf[0] == '\'')
1540         {
1541             rBuffer.append('\'');
1542             ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1543             rBuffer.append( aBuf.copy(1));
1544         }
1545         else
1546         {
1547             ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1548             rBuffer.append( aBuf);
1549         }
1550         rBuffer.append('!');
1551 
1552         makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1553         if (aAbsRef.aStart != aAbsRef.aEnd)
1554         {
1555             rBuffer.append(':');
1556             makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1557         }
1558     }
1559 
1560     static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
1561     {
1562         rBuffer.append('[').append( OUString::number( nFileId+1)).append(']');
1563     }
1564 };
1565 
1566 }
1567 
1568 static void
1569 r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1570 {
1571     rBuf.append( 'C' );
1572     if( rRef.IsColRel() )
1573     {
1574         SCCOL nCol = rRef.Col();
1575         if (nCol != 0)
1576             rBuf.append("[").append(OUString::number(nCol)).append("]");
1577     }
1578     else
1579         rBuf.append( OUString::number( rAbsRef.Col() + 1 ) );
1580 }
1581 static void
1582 r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1583 {
1584     rBuf.append( 'R' );
1585     if( rRef.IsRowRel() )
1586     {
1587         if (rRef.Row() != 0)
1588         {
1589             rBuf.append("[").append( OUString::number(rRef.Row()) ).append("]");
1590         }
1591     }
1592     else
1593         rBuf.append( OUString::number( rAbsRef.Row() + 1 ) );
1594 }
1595 
1596 namespace {
1597 
1598 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1599 {
1600     ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1601 
1602     virtual void makeRefStr( ScSheetLimits& rLimits,
1603                      OUStringBuffer&   rBuf,
1604                      formula::FormulaGrammar::Grammar /*eGram*/,
1605                      const ScAddress& rPos,
1606                      const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1607                      const ScComplexRefData& rRef,
1608                      bool bSingleRef,
1609                      bool /*bFromRangeName*/ ) const override
1610     {
1611         ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1612         ScComplexRefData aRef( rRef );
1613 
1614         MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1615 
1616         // Play fast and loose with invalid refs.  There is not much point in producing
1617         // Foo!A1:#REF! versus #REF! at this point
1618         if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row()))
1619         {
1620             rBuf.append(rErrRef);
1621             return;
1622         }
1623 
1624         if( !bSingleRef )
1625         {
1626             if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1627             {
1628                 rBuf.append(rErrRef);
1629                 return;
1630             }
1631 
1632             if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1633             {
1634                 r1c1_add_row(rBuf,  rRef.Ref1, aAbsRef.aStart);
1635                 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
1636                     rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
1637                 {
1638                     rBuf.append( ':' );
1639                     r1c1_add_row(rBuf,  rRef.Ref2, aAbsRef.aEnd);
1640                 }
1641                 return;
1642 
1643             }
1644 
1645             if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1646             {
1647                 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1648                 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
1649                     rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1650                 {
1651                     rBuf.append( ':' );
1652                     r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1653                 }
1654                 return;
1655             }
1656         }
1657 
1658         r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1659         r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1660         if (!bSingleRef)
1661         {
1662             rBuf.append( ':' );
1663             r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1664             r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1665         }
1666     }
1667 
1668     ParseResult parseAnyToken( const OUString& rFormula,
1669                                sal_Int32 nSrcPos,
1670                                const CharClass* pCharClass,
1671                                bool bGroupSeparator) const override
1672     {
1673         parseExternalDocName(rFormula, nSrcPos);
1674 
1675         ParseResult aRet;
1676         if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1677             return aRet;
1678 
1679         constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1680             KParseTokens::ASC_UNDERSCORE ;
1681         constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1682         // '?' allowed in range names
1683         static const OUString aAddAllowed("?-[]!");
1684 
1685         return pCharClass->parseAnyToken( rFormula,
1686                 nSrcPos, nStartFlags, aAddAllowed,
1687                 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1688                 aAddAllowed );
1689     }
1690 
1691     virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1692     {
1693         return ConventionXL::getSpecialSymbol(eSymType);
1694     }
1695 
1696     virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1697             const ScDocument* pDoc,
1698             const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1699     {
1700         return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1701     }
1702 
1703     virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1704             const OUString& rName ) const override
1705     {
1706         return ConventionXL::makeExternalNameStr(rFile, rName);
1707     }
1708 
1709     virtual void makeExternalRefStr(
1710         ScSheetLimits& rLimits,
1711         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1712         const OUString& rTabName, const ScSingleRefData& rRef ) const override
1713     {
1714         // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1715         // This is a little different from the format Excel uses, as Excel
1716         // puts [] only around the file name.  But we need to enclose the
1717         // whole file path with [] because the file name can contain any
1718         // characters.
1719 
1720         ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
1721         ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1722         ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1723         rBuffer.append('!');
1724 
1725         r1c1_add_row(rBuffer, rRef, aAbsRef);
1726         r1c1_add_col(rBuffer, rRef, aAbsRef);
1727     }
1728 
1729     virtual void makeExternalRefStr(
1730         ScSheetLimits& rLimits,
1731         OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1732         const std::vector<OUString>& rTabNames, const OUString& rTabName,
1733         const ScComplexRefData& rRef ) const override
1734     {
1735         ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1736 
1737         ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1738         ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1739         rBuffer.append('!');
1740 
1741         if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1742         {
1743             rBuffer.append(ScResId(STR_NO_REF_TABLE));
1744             return;
1745         }
1746 
1747         if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1748         {
1749             r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1750             if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1751             {
1752                 rBuffer.append(':');
1753                 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1754             }
1755             return;
1756         }
1757 
1758         if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1759         {
1760             r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1761             if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1762             {
1763                 rBuffer.append(':');
1764                 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1765             }
1766             return;
1767         }
1768 
1769         r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1770         r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1771         rBuffer.append(':');
1772         r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1773         r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1774     }
1775 
1776     virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const override
1777     {
1778         ScCharFlags nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
1779         if (c == '-' && cLast == '[')
1780             // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1781             nFlags |= ScCharFlags::Ident;
1782         return nFlags;
1783     }
1784 };
1785 
1786 }
1787 
1788 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr,
1789                         bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1790     : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1791     pDoc(rCxt.getDoc()),
1792     aPos(rPos),
1793     mpFormatter(pContext? pContext->GetFormatTable() : pDoc->GetFormatTable()),
1794     mpInterpreterContext(pContext),
1795     mnCurrentSheetTab(-1),
1796     mnCurrentSheetEndPos(0),
1797     pCharClass(ScGlobal::pCharClass),
1798     mnPredetectedReference(0),
1799     mnRangeOpPosInSymbol(-1),
1800     pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1801     meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1802     mbCloseBrackets(true),
1803     mbRewind(false),
1804     maTabNames(rCxt.getTabNames())
1805 {
1806     SetGrammar(rCxt.getGrammar());
1807 }
1808 
1809 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos, ScTokenArray& rArr,
1810                         formula::FormulaGrammar::Grammar eGrammar,
1811                         bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1812     : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1813         pDoc( pDocument ),
1814         aPos( rPos ),
1815         mpFormatter(pContext ? pContext->GetFormatTable() : pDoc->GetFormatTable()),
1816         mpInterpreterContext(pContext),
1817         mnCurrentSheetTab(-1),
1818         mnCurrentSheetEndPos(0),
1819         nSrcPos(0),
1820         pCharClass( ScGlobal::pCharClass ),
1821         mnPredetectedReference(0),
1822         mnRangeOpPosInSymbol(-1),
1823         pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1824         meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1825         mbCloseBrackets( true ),
1826         mbRewind( false )
1827 {
1828     SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ?
1829                 pDocument->GetGrammar() :
1830                 eGrammar );
1831 }
1832 
1833 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
1834                         bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1835     : FormulaCompiler(bComputeII, bMatrixFlag),
1836     pDoc(rCxt.getDoc()),
1837     aPos(rPos),
1838     mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr),
1839     mpInterpreterContext(pContext),
1840     mnCurrentSheetTab(-1),
1841     mnCurrentSheetEndPos(0),
1842     pCharClass(ScGlobal::pCharClass),
1843     mnPredetectedReference(0),
1844     mnRangeOpPosInSymbol(-1),
1845     pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1846     meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1847     mbCloseBrackets(true),
1848     mbRewind(false),
1849     maTabNames(rCxt.getTabNames())
1850 {
1851     SetGrammar(rCxt.getGrammar());
1852 }
1853 
1854 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,
1855                         formula::FormulaGrammar::Grammar eGrammar,
1856                         bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1857         : FormulaCompiler(bComputeII, bMatrixFlag),
1858         pDoc( pDocument ),
1859         aPos( rPos ),
1860         mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr),
1861         mpInterpreterContext(pContext),
1862         mnCurrentSheetTab(-1),
1863         mnCurrentSheetEndPos(0),
1864         nSrcPos(0),
1865         pCharClass( ScGlobal::pCharClass ),
1866         mnPredetectedReference(0),
1867         mnRangeOpPosInSymbol(-1),
1868         pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1869         meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1870         mbCloseBrackets( true ),
1871         mbRewind( false )
1872 {
1873     SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ?
1874                 (pDocument ? pDocument->GetGrammar() : formula::FormulaGrammar::GRAM_DEFAULT) :
1875                 eGrammar );
1876 }
1877 
1878 ScCompiler::~ScCompiler()
1879 {
1880 }
1881 
1882 void ScCompiler::CheckTabQuotes( OUString& rString,
1883                                  const FormulaGrammar::AddressConvention eConv )
1884 {
1885     sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1886     sal_Int32 nContFlags = nStartFlags;
1887     ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
1888         KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_OUSTRING, nContFlags, EMPTY_OUSTRING);
1889     bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
1890 
1891     switch ( eConv )
1892     {
1893         default :
1894         case FormulaGrammar::CONV_UNSPECIFIED :
1895             break;
1896         case FormulaGrammar::CONV_OOO :
1897         case FormulaGrammar::CONV_XL_A1 :
1898         case FormulaGrammar::CONV_XL_R1C1 :
1899         case FormulaGrammar::CONV_XL_OOX :
1900         case FormulaGrammar::CONV_ODF :
1901             if( bNeedsQuote )
1902             {
1903                 const OUString one_quote('\'');
1904                 const OUString two_quote("''");
1905                 // escape embedded quotes
1906                 rString = rString.replaceAll( one_quote, two_quote );
1907             }
1908             break;
1909     }
1910 
1911     if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1912     {
1913         // Prevent any possible confusion resulting from pure numeric sheet names.
1914         bNeedsQuote = true;
1915     }
1916 
1917     if( bNeedsQuote )
1918     {
1919         rString = "'" + rString + "'";
1920     }
1921 }
1922 
1923 sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
1924 {
1925     if (rString[0] != '\'')
1926         return -1;
1927     sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
1928     // it must be 'Doc'#
1929     if (nPos != -1 && rString[nPos-1] != '\'')
1930         nPos = -1;
1931     return nPos;
1932 }
1933 
1934 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
1935 {
1936     const Convention* p = GetRefConvention(eConv);
1937     if (p)
1938         SetRefConvention(p);
1939 }
1940 
1941 const ScCompiler::Convention* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv )
1942 {
1943 
1944     switch (eConv)
1945     {
1946         case FormulaGrammar::CONV_OOO:
1947         {
1948             static const ConventionOOO_A1 ConvOOO_A1;
1949             return &ConvOOO_A1;
1950         }
1951         case FormulaGrammar::CONV_ODF:
1952         {
1953             static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
1954             return &ConvOOO_A1_ODF;
1955         }
1956         case FormulaGrammar::CONV_XL_A1:
1957         {
1958             static const ConventionXL_A1 ConvXL_A1;
1959             return &ConvXL_A1;
1960         }
1961         case FormulaGrammar::CONV_XL_R1C1:
1962         {
1963             static const ConventionXL_R1C1 ConvXL_R1C1;
1964             return &ConvXL_R1C1;
1965         }
1966         case FormulaGrammar::CONV_XL_OOX:
1967         {
1968             static const ConventionXL_OOX ConvXL_OOX;
1969             return &ConvXL_OOX;
1970         }
1971         case FormulaGrammar::CONV_UNSPECIFIED:
1972         default:
1973             ;
1974     }
1975 
1976     return nullptr;
1977 }
1978 
1979 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
1980 {
1981     pConv = pConvP;
1982     meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
1983     OSL_ENSURE( FormulaGrammar::isSupported( meGrammar),
1984             "ScCompiler::SetRefConvention: unsupported grammar resulting");
1985 }
1986 
1987 void ScCompiler::SetError(FormulaError nError)
1988 {
1989     if( pArr->GetCodeError() == FormulaError::NONE)
1990         pArr->SetCodeError( nError);
1991 }
1992 
1993 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
1994 {
1995     const sal_Unicode* const pStop = pDst + nMax;
1996     while ( pDst < pStop )
1997     {
1998         *pDst++ = *pSrc++;
1999     }
2000     *pDst = 0;
2001     return pDst;
2002 }
2003 
2004 // p1 MUST contain at least n characters, or terminate with NIL.
2005 // p2 MUST pass upper case letters, if any.
2006 // n  MUST not be greater than length of p2
2007 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n )
2008 {
2009     for (size_t i=0; i<n; ++i)
2010     {
2011         if (!p1[i])
2012             return false;
2013         if (p1[i] != p2[i])
2014         {
2015             if (p1[i] < 'a' || 'z' < p1[i])
2016                 return false;   // not a lower case letter
2017             if (p2[i] < 'A' || 'Z' < p2[i])
2018                 return false;   // not a letter to match
2019             if (p1[i] != p2[i] + 0x20)
2020                 return false;   // lower case doesn't match either
2021         }
2022     }
2023     return true;
2024 }
2025 
2026 // NextSymbol
2027 
2028 // Parses the formula into separate symbols for further processing.
2029 // XXX NOTE: this is a rough sketch of the original idea, there are other
2030 // states that were added and didn't make it into this table and things are
2031 // more complicated. Use the source, Luke.
2032 
2033 // initial state = GetChar
2034 
2035 // old state     | read character    | action                | new state
2036 //---------------+-------------------+-----------------------+---------------
2037 // GetChar       | ;()+-*/^=&        | Symbol=char           | Stop
2038 //               | <>                | Symbol=char           | GetBool
2039 //               | $ letter          | Symbol=char           | GetWord
2040 //               | number            | Symbol=char           | GetValue
2041 //               | "                 | none                  | GetString
2042 //               | other             | none                  | GetChar
2043 //---------------+-------------------+-----------------------+---------------
2044 // GetBool       | =>                | Symbol=Symbol+char    | Stop
2045 //               | other             | Dec(CharPos)          | Stop
2046 //---------------+-------------------+-----------------------+---------------
2047 // GetWord       | SepSymbol         | Dec(CharPos)          | Stop
2048 //               | ()+-*/^=<>&~      |                       |
2049 //               | space             | Dec(CharPos)          | Stop
2050 //               | $_:.              |                       |
2051 //               | letter, number    | Symbol=Symbol+char    | GetWord
2052 //               | other             | error                 | Stop
2053 //---------------+-------------------+-----------------------+---------------
2054 // GetValue      | ;()*/^=<>&        |                       |
2055 //               | space             | Dec(CharPos)          | Stop
2056 //               | number E+-%,.     | Symbol=Symbol+char    | GetValue
2057 //               | other             | error                 | Stop
2058 //---------------+-------------------+-----------------------+---------------
2059 // GetString     | "                 | none                  | Stop
2060 //               | other             | Symbol=Symbol+char    | GetString
2061 //---------------+-------------------+-----------------------+---------------
2062 
2063 sal_Int32 ScCompiler::NextSymbol(bool bInArray)
2064 {
2065     cSymbol[MAXSTRLEN] = 0;       // end
2066     sal_Unicode* pSym = cSymbol;
2067     const sal_Unicode* const pStart = aFormula.getStr();
2068     const sal_Unicode* pSrc = pStart + nSrcPos;
2069     bool bi18n = false;
2070     sal_Unicode c = *pSrc;
2071     sal_Unicode cLast = 0;
2072     bool bQuote = false;
2073     mnRangeOpPosInSymbol = -1;
2074     ScanState eState = ssGetChar;
2075     sal_Int32 nSpaces = 0;
2076     sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
2077     sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
2078     sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
2079     sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : ScGlobal::pLocaleData->getNumDecimalSep()[0]);
2080     sal_Unicode cDecSepAlt = (mxSymbols->isEnglish() ? 0 : ScGlobal::pLocaleData->getNumDecimalSepAlt().toChar());
2081 
2082     // special symbols specific to address convention used
2083     sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
2084     sal_Unicode cSheetSep    = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
2085 
2086     int nDecSeps = 0;
2087     bool bAutoIntersection = false;
2088     int nRefInName = 0;
2089     bool bErrorConstantHadSlash = false;
2090     mnPredetectedReference = 0;
2091     // try to parse simple tokens before calling i18n parser
2092     while ((c != 0) && (eState != ssStop) )
2093     {
2094         pSrc++;
2095         ScCharFlags nMask = GetCharTableFlags( c, cLast );
2096 
2097         // The parameter separator and the array column and row separators end
2098         // things unconditionally if not in string or reference.
2099         if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
2100         {
2101             switch (eState)
2102             {
2103                 // these are to be continued
2104                 case ssGetString:
2105                 case ssSkipString:
2106                 case ssGetReference:
2107                 case ssSkipReference:
2108                     break;
2109                 default:
2110                     if (eState == ssGetChar)
2111                         *pSym++ = c;
2112                     else
2113                         pSrc--;
2114                     eState = ssStop;
2115             }
2116         }
2117 Label_MaskStateMachine:
2118         switch (eState)
2119         {
2120             case ssGetChar :
2121             {
2122                 // Order is important!
2123                 if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
2124                 {
2125                     *pSym++ = c;
2126                     eState = ssGetTableRefColumn;
2127                 }
2128                 else if( nMask & ScCharFlags::OdfLabelOp )
2129                 {
2130                     // '!!' automatic intersection
2131                     if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp)
2132                     {
2133                         /* TODO: For now the UI "space operator" is used, this
2134                          * could be enhanced using a specialized OpCode to get
2135                          * rid of the space ambiguity, which would need some
2136                          * places to be adapted though. And we would still need
2137                          * to support the ambiguous space operator for UI
2138                          * purposes anyway. However, we then could check for
2139                          * invalid usage of '!!', which currently isn't
2140                          * possible. */
2141                         if (!bAutoIntersection)
2142                         {
2143                             ++pSrc;
2144                             nSpaces += 2;   // must match the character count
2145                             bAutoIntersection = true;
2146                         }
2147                         else
2148                         {
2149                             pSrc--;
2150                             eState = ssStop;
2151                         }
2152                     }
2153                     else
2154                     {
2155                         nMask &= ~ScCharFlags::OdfLabelOp;
2156                         goto Label_MaskStateMachine;
2157                     }
2158                 }
2159                 else if( nMask & ScCharFlags::OdfNameMarker )
2160                 {
2161                     // '$$' defined name marker
2162                     if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfNameMarker)
2163                     {
2164                         // both eaten, not added to pSym
2165                         ++pSrc;
2166                     }
2167                     else
2168                     {
2169                         nMask &= ~ScCharFlags::OdfNameMarker;
2170                         goto Label_MaskStateMachine;
2171                     }
2172                 }
2173                 else if( nMask & ScCharFlags::Char )
2174                 {
2175                     // '[' is a special case in Excel syntax, it can start an
2176                     // external reference, ID in OOXML like [1]Sheet1!A1 or
2177                     // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2178                     // [filename]Sheet!R1C1 that needs to be scanned
2179                     // entirely, or can be ocTableRefOpen, of which the first
2180                     // transforms an ocDBArea into an ocTableRef.
2181                     if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar)
2182                             && eLastOp != ocDBArea && maTableRefs.empty())
2183                     {
2184                         nMask &= ~ScCharFlags::Char;
2185                         goto Label_MaskStateMachine;
2186                     }
2187                     else
2188                     {
2189                         *pSym++ = c;
2190                         eState = ssStop;
2191                     }
2192                 }
2193                 else if( nMask & ScCharFlags::OdfLBracket )
2194                 {
2195                     // eaten, not added to pSym
2196                     eState = ssGetReference;
2197                     mnPredetectedReference = 1;
2198                 }
2199                 else if( nMask & ScCharFlags::CharBool )
2200                 {
2201                     *pSym++ = c;
2202                     eState = ssGetBool;
2203                 }
2204                 else if( nMask & ScCharFlags::CharValue )
2205                 {
2206                     *pSym++ = c;
2207                     eState = ssGetValue;
2208                 }
2209                 else if( nMask & ScCharFlags::CharString )
2210                 {
2211                     *pSym++ = c;
2212                     eState = ssGetString;
2213                 }
2214                 else if( nMask & ScCharFlags::CharErrConst )
2215                 {
2216                     *pSym++ = c;
2217                     if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
2218                         eState = ssGetTableRefItem;
2219                     else
2220                         eState = ssGetErrorConstant;
2221                 }
2222                 else if( nMask & ScCharFlags::CharDontCare )
2223                 {
2224                     nSpaces++;
2225                 }
2226                 else if( nMask & ScCharFlags::CharIdent )
2227                 {   // try to get a simple ASCII identifier before calling
2228                     // i18n, to gain performance during import
2229                     *pSym++ = c;
2230                     eState = ssGetIdent;
2231                 }
2232                 else
2233                 {
2234                     bi18n = true;
2235                     eState = ssStop;
2236                 }
2237             }
2238             break;
2239             case ssGetIdent:
2240             {
2241                 if ( nMask & ScCharFlags::Ident )
2242                 {   // This catches also $Sheet1.A$1, for example.
2243                     if( pSym == &cSymbol[ MAXSTRLEN ] )
2244                     {
2245                         SetError(FormulaError::StringOverflow);
2246                         eState = ssStop;
2247                     }
2248                     else
2249                         *pSym++ = c;
2250                 }
2251                 else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4))
2252                 {
2253                     // Completely ugly means to catch broken
2254                     // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2255                     // references that were written in ODF named ranges
2256                     // (without embracing [] hence no predetected reference)
2257                     // and to OOXML and handle them as one symbol.
2258                     // Also catches these in UI, so we can process them
2259                     // further.
2260                     int i = 0;
2261                     for ( ; i<5; ++i)
2262                     {
2263                         if( pSym == &cSymbol[ MAXSTRLEN ] )
2264                         {
2265                             SetError(FormulaError::StringOverflow);
2266                             eState = ssStop;
2267                             break;  // for
2268                         }
2269                         else
2270                         {
2271                             *pSym++ = c;
2272                             c = *pSrc++;
2273                         }
2274                     }
2275                     if (i == 5)
2276                         c = *((--pSrc)-1);  // position last/next character correctly
2277                 }
2278                 else if (c == ':' && mnRangeOpPosInSymbol < 0)
2279                 {
2280                     // One range operator may form Sheet1.A:A, which we need to
2281                     // pass as one entity to IsReference().
2282                     if( pSym == &cSymbol[ MAXSTRLEN ] )
2283                     {
2284                         SetError(FormulaError::StringOverflow);
2285                         eState = ssStop;
2286                     }
2287                     else
2288                     {
2289                         mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2290                         *pSym++ = c;
2291                     }
2292                 }
2293                 else if ( 128 <= c || '\'' == c )
2294                 {   // High values need reparsing with i18n,
2295                     // single quoted $'sheet' names too (otherwise we'd had to
2296                     // implement everything twice).
2297                     bi18n = true;
2298                     eState = ssStop;
2299                 }
2300                 else
2301                 {
2302                     pSrc--;
2303                     eState = ssStop;
2304                 }
2305             }
2306             break;
2307             case ssGetBool :
2308             {
2309                 if( nMask & ScCharFlags::Bool )
2310                 {
2311                     *pSym++ = c;
2312                     eState = ssStop;
2313                 }
2314                 else
2315                 {
2316                     pSrc--;
2317                     eState = ssStop;
2318                 }
2319             }
2320             break;
2321             case ssGetValue :
2322             {
2323                 if( pSym == &cSymbol[ MAXSTRLEN ] )
2324                 {
2325                     SetError(FormulaError::StringOverflow);
2326                     eState = ssStop;
2327                 }
2328                 else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt))
2329                 {
2330                     if (++nDecSeps > 1)
2331                     {
2332                         // reparse with i18n, may be numeric sheet name as well
2333                         bi18n = true;
2334                         eState = ssStop;
2335                     }
2336                     else
2337                         *pSym++ = c;
2338                 }
2339                 else if( nMask & ScCharFlags::Value )
2340                     *pSym++ = c;
2341                 else if( nMask & ScCharFlags::ValueSep )
2342                 {
2343                     pSrc--;
2344                     eState = ssStop;
2345                 }
2346                 else if (c == 'E' || c == 'e')
2347                 {
2348                     if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp)
2349                         *pSym++ = c;
2350                     else
2351                     {
2352                         // reparse with i18n
2353                         bi18n = true;
2354                         eState = ssStop;
2355                     }
2356                 }
2357                 else if( nMask & ScCharFlags::ValueSign )
2358                 {
2359                     if (((cLast == 'E') || (cLast == 'e')) &&
2360                             (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueValue))
2361                     {
2362                         *pSym++ = c;
2363                     }
2364                     else
2365                     {
2366                         pSrc--;
2367                         eState = ssStop;
2368                     }
2369                 }
2370                 else
2371                 {
2372                     // reparse with i18n
2373                     bi18n = true;
2374                     eState = ssStop;
2375                 }
2376             }
2377             break;
2378             case ssGetString :
2379             {
2380                 if( nMask & ScCharFlags::StringSep )
2381                 {
2382                     if ( !bQuote )
2383                     {
2384                         if ( *pSrc == '"' )
2385                             bQuote = true;      // "" => literal "
2386                         else
2387                             eState = ssStop;
2388                     }
2389                     else
2390                         bQuote = false;
2391                 }
2392                 if ( !bQuote )
2393                 {
2394                     if( pSym == &cSymbol[ MAXSTRLEN ] )
2395                     {
2396                         SetError(FormulaError::StringOverflow);
2397                         eState = ssSkipString;
2398                     }
2399                     else
2400                         *pSym++ = c;
2401                 }
2402             }
2403             break;
2404             case ssSkipString:
2405                 if( nMask & ScCharFlags::StringSep )
2406                     eState = ssStop;
2407                 break;
2408             case ssGetErrorConstant:
2409                 {
2410                     // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2411                     // BUT, in UI these may have been translated! So don't
2412                     // check for ASCII alnum. Note that this construct can't be
2413                     // parsed with i18n.
2414                     /* TODO: be strict when reading ODFF, check for ASCII alnum
2415                      * and proper continuation after '/'. However, even with
2416                      * the lax parsing only the error constants we have defined
2417                      * as opcode symbols will be recognized and others result
2418                      * in ocBad, so the result is actually conformant. */
2419                     bool bAdd = true;
2420                     if ('?' == c)
2421                         eState = ssStop;
2422                     else if ('!' == c)
2423                     {
2424                         // Check if this is #REF! that starts an invalid reference.
2425                         // Note we have an implicit '!' here at the end.
2426                         if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) &&
2427                                 (GetCharTableFlags( *pSrc, c) & ScCharFlags::Ident))
2428                             eState = ssGetIdent;
2429                         else
2430                             eState = ssStop;
2431                     }
2432                     else if ('/' == c)
2433                     {
2434                         if (!bErrorConstantHadSlash)
2435                             bErrorConstantHadSlash = true;
2436                         else
2437                         {
2438                             bAdd = false;
2439                             eState = ssStop;
2440                         }
2441                     }
2442                     else if ((nMask & ScCharFlags::WordSep) ||
2443                             (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2444                     {
2445                         bAdd = false;
2446                         eState = ssStop;
2447                     }
2448                     if (!bAdd)
2449                         --pSrc;
2450                     else
2451                     {
2452                         if (pSym == &cSymbol[ MAXSTRLEN ])
2453                         {
2454                             SetError( FormulaError::StringOverflow);
2455                             eState = ssStop;
2456                         }
2457                         else
2458                             *pSym++ = c;
2459                     }
2460                 }
2461                 break;
2462             case ssGetTableRefItem:
2463                 {
2464                     // Scan whatever up to the next ']' closer.
2465                     if (c != ']')
2466                     {
2467                         if( pSym == &cSymbol[ MAXSTRLEN ] )
2468                         {
2469                             SetError( FormulaError::StringOverflow);
2470                             eState = ssStop;
2471                         }
2472                         else
2473                             *pSym++ = c;
2474                     }
2475                     else
2476                     {
2477                         --pSrc;
2478                         eState = ssStop;
2479                     }
2480                 }
2481                 break;
2482             case ssGetTableRefColumn:
2483                 {
2484                     // Scan whatever up to the next unescaped ']' closer.
2485                     if (c != ']' || cLast == '\'')
2486                     {
2487                         if( pSym == &cSymbol[ MAXSTRLEN ] )
2488                         {
2489                             SetError( FormulaError::StringOverflow);
2490                             eState = ssStop;
2491                         }
2492                         else
2493                             *pSym++ = c;
2494                     }
2495                     else
2496                     {
2497                         --pSrc;
2498                         eState = ssStop;
2499                     }
2500                 }
2501                 break;
2502             case ssGetReference:
2503                 if( pSym == &cSymbol[ MAXSTRLEN ] )
2504                 {
2505                     SetError( FormulaError::StringOverflow);
2506                     eState = ssSkipReference;
2507                 }
2508                 [[fallthrough]];
2509             case ssSkipReference:
2510                 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2511                 // mandatory also if no sheet name. 'External'# is optional,
2512                 // sheet name is optional, quotes around sheet name are
2513                 // optional if no quote contained. [#REF!] is valid.
2514                 // 2nd usage: ['Sheet'.$$'DefinedName']
2515                 // 3rd usage: ['External'#$$'DefinedName']
2516                 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2517                 // Also for all these names quotes are optional if no quote
2518                 // contained.
2519                 {
2520 
2521                     // nRefInName: 0 := not in sheet name yet. 'External'
2522                     // is parsed as if it was a sheet name and nRefInName
2523                     // is reset when # is encountered immediately after closing
2524                     // quote. Same with 'DefinedName', nRefInName is cleared
2525                     // when : is encountered.
2526 
2527                     // Encountered leading $ before sheet name.
2528                     constexpr int kDollar    = (1 << 1);
2529                     // Encountered ' opening quote, which may be after $ or
2530                     // not.
2531                     constexpr int kOpen      = (1 << 2);
2532                     // Somewhere in name.
2533                     constexpr int kName      = (1 << 3);
2534                     // Encountered ' in name, will be cleared if double or
2535                     // transformed to kClose if not, in which case kOpen is
2536                     // cleared.
2537                     constexpr int kQuote     = (1 << 4);
2538                     // Past ' closing quote.
2539                     constexpr int kClose     = (1 << 5);
2540                     // Encountered # file/sheet separator.
2541                     constexpr int kFileSep   = (1 << 6);
2542                     // Past . sheet name separator.
2543                     constexpr int kPast      = (1 << 7);
2544                     // Marked name $$ follows sheet name separator, detected
2545                     // while we're still on the separator. Will be cleared when
2546                     // entering the name.
2547                     constexpr int kMarkAhead = (1 << 8);
2548                     // In marked defined name.
2549                     constexpr int kDefName   = (1 << 9);
2550                     // Encountered # of #REF!
2551                     constexpr int kRefErr    = (1 << 10);
2552 
2553                     bool bAddToSymbol = true;
2554                     if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen))
2555                     {
2556                         OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2557                                 "ScCompiler::NextSymbol: reference: "
2558                                 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2559                         // eaten, not added to pSym
2560                         bAddToSymbol = false;
2561                         eState = ssStop;
2562                     }
2563                     else if (cSheetSep == c && nRefInName == 0)
2564                     {
2565                         // eat it, no sheet name [.A1]
2566                         bAddToSymbol = false;
2567                         nRefInName |= kPast;
2568                         if ('$' == pSrc[0] && '$' == pSrc[1])
2569                             nRefInName |= kMarkAhead;
2570                     }
2571                     else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2572                     {
2573                         // Not in col/row yet.
2574 
2575                         if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2576                             nRefInName = 0;
2577                         else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2578                         {
2579                             nRefInName &= ~kMarkAhead;
2580                             if (!(nRefInName & kDefName))
2581                             {
2582                                 // eaten, not added to pSym (2 chars)
2583                                 bAddToSymbol = false;
2584                                 ++pSrc;
2585                                 nRefInName &= kPast;
2586                                 nRefInName |= kDefName;
2587                             }
2588                             else
2589                             {
2590                                 // ScAddress::Parse() will recognize this as
2591                                 // invalid later.
2592                                 if (eState != ssSkipReference)
2593                                 {
2594                                     *pSym++ = c;
2595                                     *pSym++ = *pSrc++;
2596                                 }
2597                                 bAddToSymbol = false;
2598                             }
2599                         }
2600                         else if (cSheetPrefix == c && nRefInName == 0)
2601                             nRefInName |= kDollar;
2602                         else if ('\'' == c)
2603                         {
2604                             // TODO: The conventions' parseExternalName()
2605                             // should handle quoted names, but as long as they
2606                             // don't remove non-embedded quotes here.
2607                             if (!(nRefInName & kName))
2608                             {
2609                                 nRefInName |= (kOpen | kName);
2610                                 bAddToSymbol = !(nRefInName & kDefName);
2611                             }
2612                             else if (!(nRefInName & kOpen))
2613                             {
2614                                 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2615                                         "a ''' without the name being enclosed in '...' violates ODF spec");
2616                             }
2617                             else if (nRefInName & kQuote)
2618                             {
2619                                 // escaped embedded quote
2620                                 nRefInName &= ~kQuote;
2621                             }
2622                             else
2623                             {
2624                                 switch (pSrc[0])
2625                                 {
2626                                     case '\'':
2627                                         // escapes embedded quote
2628                                         nRefInName |= kQuote;
2629                                         break;
2630                                     case SC_COMPILER_FILE_TAB_SEP:
2631                                         // sheet name should follow
2632                                         nRefInName |= kFileSep;
2633                                         [[fallthrough]];
2634                                     default:
2635                                         // quote not followed by quote => close
2636                                         nRefInName |= kClose;
2637                                         nRefInName &= ~kOpen;
2638                                 }
2639                                 bAddToSymbol = !(nRefInName & kDefName);
2640                             }
2641                         }
2642                         else if ('#' == c && nRefInName == 0)
2643                             nRefInName |= kRefErr;
2644                         else if (cSheetSep == c && !(nRefInName & kOpen))
2645                         {
2646                             // unquoted sheet name separator
2647                             nRefInName |= kPast;
2648                             if ('$' == pSrc[0] && '$' == pSrc[1])
2649                                 nRefInName |= kMarkAhead;
2650                         }
2651                         else if (':' == c && !(nRefInName & kOpen))
2652                         {
2653                             OSL_FAIL("ScCompiler::NextSymbol: reference: "
2654                                     "range operator ':' without prior sheet name separator '.' violates ODF spec");
2655                             nRefInName = 0;
2656                             ++mnPredetectedReference;
2657                         }
2658                         else if (!(nRefInName & kName))
2659                         {
2660                             // start unquoted name
2661                             nRefInName |= kName;
2662                         }
2663                     }
2664                     else if (':' == c)
2665                     {
2666                         // range operator
2667                         nRefInName = 0;
2668                         ++mnPredetectedReference;
2669                     }
2670                     if (bAddToSymbol && eState != ssSkipReference)
2671                         *pSym++ = c;    // everything is part of reference
2672                 }
2673                 break;
2674             case ssStop:
2675                 ;   // nothing, prevent warning
2676                 break;
2677         }
2678         cLast = c;
2679         c = *pSrc;
2680     }
2681     if ( bi18n )
2682     {
2683         const sal_Int32 nOldSrcPos = nSrcPos;
2684         nSrcPos = nSrcPos + nSpaces;
2685         // If group separator is not a possible operator and not one of any
2686         // separators then it may be parsed away in numbers. This is
2687         // specifically the case with NO-BREAK SPACE, which actually triggers
2688         // the bi18n case (which we don't want to include as yet another
2689         // special case above as it is rare enough and doesn't generally occur
2690         // in formulas).
2691         const sal_Unicode cGroupSep = ScGlobal::pLocaleData->getNumThousandSep()[0];
2692         const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep &&
2693                 cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep &&
2694                 cGroupSep != cDecSep && cGroupSep != cDecSepAlt &&
2695                 cGroupSep != cSheetPrefix && cGroupSep != cSheetSep);
2696         OUStringBuffer aSymbol;
2697         mnRangeOpPosInSymbol = -1;
2698         FormulaError nErr = FormulaError::NONE;
2699         do
2700         {
2701             bi18n = false;
2702             // special case  (e.g. $'sheetname' in OOO A1)
2703             if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2704                 aSymbol.append(pStart[nSrcPos++]);
2705 
2706             ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass, bGroupSeparator);
2707 
2708             if ( !aRes.TokenType )
2709             {
2710                 nErr = FormulaError::IllegalChar;
2711                 SetError( nErr );      // parsed chars as string
2712             }
2713             if ( aRes.EndPos <= nSrcPos )
2714             {
2715                 // Could not parse anything meaningful.
2716                 assert(!aRes.TokenType);
2717                 nErr = FormulaError::IllegalChar;
2718                 SetError( nErr );
2719                 // Caller has to act on an empty symbol for
2720                 // nSrcPos < aFormula.getLength()
2721                 nSrcPos = nOldSrcPos;
2722                 aSymbol.setLength(0);
2723             }
2724             else
2725             {
2726                 // When having parsed a second reference part, ensure that the
2727                 // i18n parser did not mistakingly parse a number that included
2728                 // a separator which happened to be meant as a parameter
2729                 // separator instead.
2730                 if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
2731                 {
2732                     for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
2733                     {
2734                         if (pStart[i] == cSep)
2735                             aRes.EndPos = i;    // also ends for
2736                     }
2737                 }
2738                 aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2739                 nSrcPos = aRes.EndPos;
2740                 c = pStart[nSrcPos];
2741                 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2742                 {   // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2743                     bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2744                 }
2745                 // One range operator restarts parsing for second reference.
2746                 if (c == ':' && mnRangeOpPosInSymbol < 0)
2747                 {
2748                     mnRangeOpPosInSymbol = aSymbol.getLength();
2749                     bi18n = true;
2750                 }
2751                 if ( bi18n )
2752                     aSymbol.append(pStart[nSrcPos++]);
2753             }
2754         } while ( bi18n && nErr == FormulaError::NONE );
2755         sal_Int32 nLen = aSymbol.getLength();
2756         if ( nLen > MAXSTRLEN )
2757         {
2758             SetError( FormulaError::StringOverflow );
2759             nLen = MAXSTRLEN;
2760         }
2761         if (mnRangeOpPosInSymbol >= nLen)
2762             mnRangeOpPosInSymbol = -1;
2763         lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2764         pSym = &cSymbol[nLen];
2765     }
2766     else
2767     {
2768         nSrcPos = pSrc - pStart;
2769         *pSym = 0;
2770     }
2771     if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2772     {
2773         // This is a trailing range operator, which is nonsense. Will be caught
2774         // in next round.
2775         mnRangeOpPosInSymbol = -1;
2776         *--pSym = 0;
2777         --nSrcPos;
2778     }
2779     if ( bAutoCorrect )
2780         aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol);
2781     if (bAutoIntersection && nSpaces > 1)
2782         --nSpaces;  // replace '!!' with only one space
2783     return nSpaces;
2784 }
2785 
2786 // Convert symbol to token
2787 
2788 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray )
2789 {
2790     OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
2791     bool bFound = (iLook != mxSymbols->getHashMap().end());
2792     if (bFound)
2793     {
2794         OpCode eOp = iLook->second;
2795         if (bInArray)
2796         {
2797             if (rName == mxSymbols->getSymbol(ocArrayColSep))
2798                 eOp = ocArrayColSep;
2799             else if (rName == mxSymbols->getSymbol(ocArrayRowSep))
2800                 eOp = ocArrayRowSep;
2801         }
2802         else if (eOp == ocArrayColSep || eOp == ocArrayRowSep)
2803         {
2804             if (rName == mxSymbols->getSymbol(ocSep))
2805                 eOp = ocSep;
2806             else if (rName == ";")
2807             {
2808                 switch (FormulaGrammar::extractFormulaLanguage( meGrammar))
2809                 {
2810                     // Only for languages/grammars that actually use ';'
2811                     // parameter separator.
2812                     case css::sheet::FormulaLanguage::NATIVE:
2813                     case css::sheet::FormulaLanguage::ENGLISH:
2814                     case css::sheet::FormulaLanguage::ODFF:
2815                     case css::sheet::FormulaLanguage::ODF_11:
2816                         eOp = ocSep;
2817                 }
2818             }
2819         }
2820         else if (eOp == ocCeil && mxSymbols->isOOXML())
2821         {
2822             // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2823             // unassigned for import.
2824             eOp = ocCeil_Math;
2825         }
2826         else if (eOp == ocFloor && mxSymbols->isOOXML())
2827         {
2828             // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2829             // unassigned for import.
2830             eOp = ocFloor_Math;
2831         }
2832         maRawToken.SetOpCode(eOp);
2833     }
2834     else if (mxSymbols->isODFF())
2835     {
2836         // ODFF names that are not written in the current mapping but to be
2837         // recognized. New names will be written in a future release, then
2838         // exchange (!) with the names in
2839         // formula/source/core/resource/core_resource.src to be able to still
2840         // read the old names as well.
2841         struct FunctionName
2842         {
2843             const char* pName;
2844             OpCode          eOp;
2845         };
2846         static const FunctionName aOdffAliases[] = {
2847             // Renamed old names, still accept them:
2848             { "B",              ocB },              // B -> BINOM.DIST.RANGE
2849             { "TDIST",          ocTDist },          // TDIST -> LEGACY.TDIST
2850             { "EASTERSUNDAY",   ocEasterSunday },   // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2851             { "ZGZ",            ocRRI },            // ZGZ -> RRI
2852             { "COLOR",          ocColor },          // COLOR -> ORG.LIBREOFFICE.COLOR
2853             { "GOALSEEK",       ocBackSolver },     // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2854             { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2855             { "COM.MICROSOFT.F.INV",  ocFInv_LT }   // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2856             // Renamed new names, prepare to read future names:
2857             //{ "ORG.OPENOFFICE.XXX", ocXXX }         // XXX -> ORG.OPENOFFICE.XXX
2858         };
2859         for (const FunctionName& rOdffAlias : aOdffAliases)
2860         {
2861             if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName))
2862             {
2863                 maRawToken.SetOpCode( rOdffAlias.eOp);
2864                 bFound = true;
2865                 break;  // for
2866             }
2867         }
2868     }
2869     else if (mxSymbols->isOOXML())
2870     {
2871         // OOXML names that are not written in the current mapping but to be
2872         // recognized as old versions wrote them.
2873         struct FunctionName
2874         {
2875             const char* pName;
2876             OpCode      eOp;
2877         };
2878         static const FunctionName aOoxmlAliases[] = {
2879             { "EFFECTIVE",      ocEffect },         // EFFECTIVE -> EFFECT
2880             { "ERRORTYPE",      ocErrorType },      // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
2881             { "MULTIRANGE",     ocMultiArea },      // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
2882             { "GOALSEEK",       ocBackSolver },     // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
2883             { "EASTERSUNDAY",   ocEasterSunday },   // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
2884             { "CURRENT",        ocCurrent },        // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
2885             { "STYLE",          ocStyle }           // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
2886         };
2887         for (const FunctionName& rOoxmlAlias : aOoxmlAliases)
2888         {
2889             if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName))
2890             {
2891                 maRawToken.SetOpCode( rOoxmlAlias.eOp);
2892                 bFound = true;
2893                 break;  // for
2894             }
2895         }
2896     }
2897     else if (mxSymbols->isPODF())
2898     {
2899         // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
2900         // We can't rename them in
2901         // formula/source/core/resource/core_resource.src but can add
2902         // additional names to be recognized here so they match the UI names if
2903         // those are renamed.
2904         struct FunctionName
2905         {
2906             const char* pName;
2907             OpCode      eOp;
2908         };
2909         static const FunctionName aPodfAliases[] = {
2910             { "EFFECT",  ocEffect }      // EFFECTIVE -> EFFECT
2911         };
2912         for (const FunctionName& rPodfAlias : aPodfAliases)
2913         {
2914             if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName))
2915             {
2916                 maRawToken.SetOpCode( rPodfAlias.eOp);
2917                 bFound = true;
2918                 break;  // for
2919             }
2920         }
2921     }
2922 
2923     if (!bFound)
2924     {
2925         OUString aIntName;
2926         if (mxSymbols->hasExternals())
2927         {
2928             // If symbols are set by filters get mapping to exact name.
2929             ExternalHashMap::const_iterator iExt(
2930                     mxSymbols->getExternalHashMap().find( rName));
2931             if (iExt != mxSymbols->getExternalHashMap().end())
2932             {
2933                 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2934                     aIntName = (*iExt).second;
2935             }
2936             if (aIntName.isEmpty())
2937             {
2938                 // If that isn't found we might continue with rName lookup as a
2939                 // last resort by just falling through to FindFunction(), but
2940                 // it shouldn't happen if the map was setup correctly. Don't
2941                 // waste time and bail out.
2942                 return false;
2943             }
2944         }
2945         if (aIntName.isEmpty())
2946         {
2947             // Old (deprecated) addins first for legacy.
2948             if (ScGlobal::GetLegacyFuncCollection()->findByName(cSymbol))
2949             {
2950                 maRawToken.SetExternal( cSymbol );
2951             }
2952             else
2953                 // bLocalFirst=false for (English) upper full original name
2954                 // (service.function)
2955                 aIntName = ScGlobal::GetAddInCollection()->FindFunction(
2956                         rName, !mxSymbols->isEnglish());
2957         }
2958         if (!aIntName.isEmpty())
2959         {
2960             maRawToken.SetExternal( aIntName );     // international name
2961             bFound = true;
2962         }
2963     }
2964     OpCode eOp;
2965     if (bFound && ((eOp = maRawToken.GetOpCode()) == ocSub || eOp == ocNegSub))
2966     {
2967         bool bShouldBeNegSub =
2968             (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
2969              (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
2970              eLastOp == ocArrayOpen ||
2971              eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
2972         if (bShouldBeNegSub && eOp == ocSub)
2973             maRawToken.NewOpCode( ocNegSub );
2974             //TODO: if ocNegSub had ForceArray we'd have to set it here
2975         else if (!bShouldBeNegSub && eOp == ocNegSub)
2976             maRawToken.NewOpCode( ocSub );
2977     }
2978     return bFound;
2979 }
2980 
2981 bool ScCompiler::IsOpCode2( const OUString& rName )
2982 {
2983     bool bFound = false;
2984     sal_uInt16 i;
2985 
2986     for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
2987         bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] );
2988 
2989     if (bFound)
2990     {
2991         maRawToken.SetOpCode( static_cast<OpCode>(--i) );
2992     }
2993     return bFound;
2994 }
2995 
2996 static bool lcl_ParenthesisFollows( const sal_Unicode* p )
2997 {
2998     while (*p == ' ')
2999         p++;
3000     return *p == '(';
3001 }
3002 
3003 bool ScCompiler::IsValue( const OUString& rSym )
3004 {
3005     const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar());
3006     if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML)
3007     {
3008         // Speedup things for ODFF, only well-formed numbers, not locale
3009         // dependent nor user input.
3010         rtl_math_ConversionStatus eStatus;
3011         sal_Int32 nParseEnd;
3012         double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd);
3013         if (nParseEnd != rSym.getLength())
3014         {
3015             // Not (only) a number.
3016 
3017             if (nParseEnd > 0)
3018                 return false;   // partially a number => no such thing
3019 
3020             if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3021                 return false;   // some function name, not a constant
3022 
3023             // Could be TRUE or FALSE constant.
3024             if (rSym.equalsIgnoreAsciiCase("TRUE"))
3025             {
3026                 maRawToken.SetDouble( 1.0 );
3027                 return true;
3028             }
3029             if (rSym.equalsIgnoreAsciiCase("FALSE"))
3030             {
3031                 maRawToken.SetDouble( 0.0 );
3032                 return true;
3033             }
3034             return false;
3035         }
3036         if (eStatus == rtl_math_ConversionStatus_OutOfRange)
3037             SetError( FormulaError::IllegalArgument );
3038         maRawToken.SetDouble( fVal );
3039         return true;
3040     }
3041 
3042     double fVal;
3043     sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
3044 
3045     if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
3046         return false;
3047 
3048     SvNumFormatType nType = mpFormatter->GetType(nIndex);
3049 
3050     // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3051     // Dates should never be entered directly and automatically converted
3052     // to serial, because the serial would be wrong if null-date changed.
3053     // Usually it wouldn't be accepted anyway because the date separator
3054     // clashed with other separators or operators.
3055     if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE))
3056         return false;
3057 
3058     if (nType == SvNumFormatType::LOGICAL)
3059     {
3060         if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3061             return false;   // Boolean function instead.
3062     }
3063 
3064     if( nType == SvNumFormatType::TEXT )
3065         // HACK: number too big!
3066         SetError( FormulaError::IllegalArgument );
3067     maRawToken.SetDouble( fVal );
3068     return true;
3069 }
3070 
3071 bool ScCompiler::IsString()
3072 {
3073     if ( cSymbol[0] != '"' )
3074         return false;
3075     const sal_Unicode* p = cSymbol+1;
3076     while ( *p )
3077         p++;
3078     sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
3079     if (!nLen || cSymbol[nLen] != '"')
3080         return false;
3081     svl::SharedString aSS = pDoc->GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1));
3082     maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
3083     return true;
3084 }
3085 
3086 bool ScCompiler::IsPredetectedErrRefReference( const OUString& rName, const OUString* pErrRef )
3087 {
3088     switch (mnPredetectedReference)
3089     {
3090         case 1:
3091             return IsSingleReference( rName, pErrRef);
3092         case 2:
3093             return IsDoubleReference( rName, pErrRef);
3094         default:
3095             return false;
3096     }
3097 }
3098 
3099 bool ScCompiler::IsPredetectedReference( const OUString& rName )
3100 {
3101     // Speedup documents with lots of broken references, e.g. sheet deleted.
3102     // It could also be a broken invalidated reference that contains #REF!
3103     // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3104     // between 2013 and 2016 until 5.1.4
3105     const OUString aErrRef("#REF!");    // not localized in ODFF
3106     sal_Int32 nPos = rName.indexOf( aErrRef);
3107     if (nPos != -1)
3108     {
3109         /* TODO: this may be enhanced by reusing scan information from
3110          * NextSymbol(), the positions of quotes and special characters found
3111          * there for $'sheet'.A1:... could be stored in a vector. We don't
3112          * fully rescan here whether found positions are within single quotes
3113          * for performance reasons. This code does not check for possible
3114          * occurrences of insane "valid" sheet names like
3115          * 'haha.#REF!1fooledyou' and will generate an error on such. */
3116         if (nPos == 0)
3117         {
3118             // Per ODFF the correct string for a reference error is just #REF!,
3119             // so pass it on.
3120             if (rName.getLength() == 5)
3121                 return IsErrorConstant( rName);
3122             // #REF!.AB42 or #REF!42 or #REF!#REF!
3123             return IsPredetectedErrRefReference( rName, &aErrRef);
3124         }
3125         sal_Unicode c = rName[nPos-1];      // before #REF!
3126         if ('$' == c)
3127         {
3128             if (nPos == 1)
3129             {
3130                 // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3131                 return IsPredetectedErrRefReference( rName, &aErrRef);
3132             }
3133             c = rName[nPos-2];              // before $#REF!
3134         }
3135         sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0;     // after #REF!
3136         switch (c)
3137         {
3138             case '.':
3139                 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
3140                 {
3141                     // sheet.#REF!42 or sheet.#REF!#REF!
3142                     return IsPredetectedErrRefReference( rName, &aErrRef);
3143                 }
3144                 break;
3145             case ':':
3146                 if (mnPredetectedReference > 1 &&
3147                         ('.' == c2 || '$' == c2 || '#' == c2 ||
3148                          ('0' <= c2 && c2 <= '9')))
3149                 {
3150                     // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3151                     return IsPredetectedErrRefReference( rName, &aErrRef);
3152                 }
3153                 break;
3154             default:
3155                 if (rtl::isAsciiAlpha(c) &&
3156                         ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
3157                 {
3158                     // AB#REF!: or AB#REF!
3159                     return IsPredetectedErrRefReference( rName, &aErrRef);
3160                 }
3161         }
3162     }
3163     switch (mnPredetectedReference)
3164     {
3165         case 1:
3166             return IsSingleReference( rName);
3167         case 2:
3168             return IsDoubleReference( rName);
3169     }
3170     return false;
3171 }
3172 
3173 bool ScCompiler::IsDoubleReference( const OUString& rName, const OUString* pErrRef )
3174 {
3175     ScRange aRange( aPos, aPos );
3176     const ScAddress::Details aDetails( pConv->meConv, aPos );
3177     ScAddress::ExternalInfo aExtInfo;
3178     ScRefFlags nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef );
3179     if( nFlags & ScRefFlags::VALID )
3180     {
3181         ScComplexRefData aRef;
3182         aRef.InitRange( aRange );
3183         aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3184         aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3185         aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3186         if ( !(nFlags & ScRefFlags::TAB_VALID) )
3187             aRef.Ref1.SetTabDeleted( true );        // #REF!
3188         aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3189         aRef.Ref2.SetColRel( (nFlags & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO );
3190         aRef.Ref2.SetRowRel( (nFlags & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO );
3191         aRef.Ref2.SetTabRel( (nFlags & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO );
3192         if ( !(nFlags & ScRefFlags::TAB2_VALID) )
3193             aRef.Ref2.SetTabDeleted( true );        // #REF!
3194         aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO );
3195         aRef.SetRange(pDoc->GetSheetLimits(), aRange, aPos);
3196         if (aExtInfo.mbExternal)
3197         {
3198             ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3199             const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3200             maRawToken.SetExternalDoubleRef(
3201                 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3202             maExternalFiles.push_back(aExtInfo.mnFileId);
3203         }
3204         else
3205         {
3206             maRawToken.SetDoubleReference(aRef);
3207         }
3208     }
3209 
3210     return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3211 }
3212 
3213 bool ScCompiler::IsSingleReference( const OUString& rName, const OUString* pErrRef )
3214 {
3215     mnCurrentSheetEndPos = 0;
3216     mnCurrentSheetTab = -1;
3217     ScAddress aAddr( aPos );
3218     const ScAddress::Details aDetails( pConv->meConv, aPos );
3219     ScAddress::ExternalInfo aExtInfo;
3220     ScRefFlags nFlags = aAddr.Parse( rName, pDoc, aDetails,
3221             &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef);
3222     // Something must be valid in order to recognize Sheet1.blah or blah.a1
3223     // as a (wrong) reference.
3224     if( nFlags & ( ScRefFlags::COL_VALID|ScRefFlags::ROW_VALID|ScRefFlags::TAB_VALID ) )
3225     {
3226         // Valid given tab and invalid col or row may indicate a sheet-local
3227         // named expression, bail out early and don't create a reference token.
3228         if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 &&
3229                 (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
3230         {
3231             if (aExtInfo.mbExternal)
3232             {
3233                 // External names are handled separately.
3234                 mnCurrentSheetEndPos = 0;
3235                 mnCurrentSheetTab = -1;
3236             }
3237             else
3238             {
3239                 mnCurrentSheetTab = aAddr.Tab();
3240             }
3241             return false;
3242         }
3243 
3244         ScSingleRefData aRef;
3245         aRef.InitAddress( aAddr );
3246         aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3247         aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3248         aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3249         aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3250         // the reference is really invalid
3251         if( !( nFlags & ScRefFlags::VALID ) )
3252         {
3253             if( !( nFlags & ScRefFlags::COL_VALID ) )
3254                 aRef.SetColDeleted(true);
3255             if( !( nFlags & ScRefFlags::ROW_VALID ) )
3256                 aRef.SetRowDeleted(true);
3257             if( !( nFlags & ScRefFlags::TAB_VALID ) )
3258                 aRef.SetTabDeleted(true);
3259             nFlags |= ScRefFlags::VALID;
3260         }
3261         aRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos);
3262 
3263         if (aExtInfo.mbExternal)
3264         {
3265             ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3266             const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3267             maRawToken.SetExternalSingleRef(
3268                 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3269             maExternalFiles.push_back(aExtInfo.mnFileId);
3270         }
3271         else
3272             maRawToken.SetSingleReference(aRef);
3273     }
3274 
3275     return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3276 }
3277 
3278 bool ScCompiler::IsReference( const OUString& rName, const OUString* pErrRef )
3279 {
3280     // Has to be called before IsValue
3281     sal_Unicode ch1 = rName[0];
3282     sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : ScGlobal::pLocaleData->getNumDecimalSep()[0] );
3283     if ( ch1 == cDecSep )
3284         return false;
3285     // Code further down checks only if cDecSep=='.' so simply obtaining the
3286     // alternative decimal separator if it's not is sufficient.
3287     if (cDecSep != '.')
3288     {
3289         cDecSep = ScGlobal::pLocaleData->getNumDecimalSepAlt().toChar();
3290         if ( ch1 == cDecSep )
3291             return false;
3292     }
3293     // Who was that imbecile introducing '.' as the sheet name separator!?!
3294     if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
3295     {
3296         // Numerical sheet name is valid.
3297         // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3298         // Don't create a #REF! of values. But also do not bail out on
3299         // something like 3:3, meaning entire row 3.
3300         do
3301         {
3302             const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
3303             if ( nPos == -1 )
3304             {
3305                 if (ScGlobal::FindUnquoted( rName, ':') != -1)
3306                     break;      // may be 3:3, continue as usual
3307                 return false;
3308             }
3309             sal_Unicode const * const pTabSep = rName.getStr() + nPos;
3310             sal_Unicode ch2 = pTabSep[1];   // maybe a column identifier
3311             if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
3312                 return false;
3313             if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e')   // E + - digit
3314                     && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) )
3315             {
3316                 // If it is an 1.E2 expression check if "1" is an existent sheet
3317                 // name. If so, a desired value 1.E2 would have to be entered as
3318                 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3319                 // require numerical sheet names always being entered quoted, which
3320                 // is not desirable (too many 1999, 2000, 2001 sheets in use).
3321                 // Furthermore, XML files created with versions prior to SRC640e
3322                 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3323                 // and would produce wrong formulas if the conditions here are met.
3324                 // If you can live with these restrictions you may remove the
3325                 // check and return an unconditional FALSE.
3326                 OUString aTabName( rName.copy( 0, nPos ) );
3327                 SCTAB nTab;
3328                 if ( !pDoc->GetTable( aTabName, nTab ) )
3329                     return false;
3330                 // If sheet "1" exists and the expression is 1.E+2 continue as
3331                 // usual, the ScRange/ScAddress parser will take care of it.
3332             }
3333         } while(false);
3334     }
3335 
3336     if (IsSingleReference( rName, pErrRef))
3337         return true;
3338 
3339     // Though the range operator is handled explicitly, when encountering
3340     // something like Sheet1.A:A we will have to treat it as one entity if it
3341     // doesn't pass as single cell reference.
3342     if (mnRangeOpPosInSymbol > 0)   // ":foo" would be nonsense
3343     {
3344         if (IsDoubleReference( rName, pErrRef))
3345             return true;
3346         // Now try with a symbol up to the range operator, rewind source
3347         // position.
3348         assert(mnRangeOpPosInSymbol < MAXSTRLEN);   // We should have caught the maldoers.
3349         if (mnRangeOpPosInSymbol >= MAXSTRLEN)      // TODO: this check and return
3350             return false;                           // can be removed when sure.
3351         sal_Int32 nLen = mnRangeOpPosInSymbol;
3352         while (cSymbol[++nLen])
3353             ;
3354         cSymbol[mnRangeOpPosInSymbol] = 0;
3355         nSrcPos -= (nLen - mnRangeOpPosInSymbol);
3356         mnRangeOpPosInSymbol = -1;
3357         mbRewind = true;
3358         return true;    // end all checks
3359     }
3360     else
3361     {
3362         switch (pConv->meConv)
3363         {
3364             case FormulaGrammar::CONV_XL_A1:
3365             case FormulaGrammar::CONV_XL_OOX:
3366                 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3367                 // sickness, mnRangeOpPosInSymbol did not catch the range
3368                 // operator as it is within a quoted name.
3369                 if (rName[0] != '\'')
3370                     return false;   // Document name has to be single quoted.
3371                 [[fallthrough]];
3372             case FormulaGrammar::CONV_XL_R1C1:
3373                 // C2 or C[1] are valid entire column references.
3374                 if (IsDoubleReference( rName, pErrRef))
3375                     return true;
3376                 break;
3377             default:
3378                 ;   // nothing
3379         }
3380     }
3381     return false;
3382 }
3383 
3384 bool ScCompiler::IsMacro( const OUString& rName )
3385 {
3386 #if !HAVE_FEATURE_SCRIPTING
3387     (void) rName;
3388 
3389     return false;
3390 #else
3391 
3392     // Calling SfxObjectShell::GetBasic() may result in all sort of things
3393     // including obtaining the model and deep down in
3394     // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3395     // formulas are compiled from a threaded import may result in a deadlock.
3396     // Check first if we actually could acquire it and if not bail out.
3397     /* FIXME: yes, but how ... */
3398     vcl::SolarMutexTryAndBuyGuard g;
3399     if (!g.isAcquired())
3400     {
3401         SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic");
3402         return false;   // bad luck
3403     }
3404 
3405     OUString aName( rName);
3406     StarBASIC* pObj = nullptr;
3407     SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
3408 
3409     try
3410     {
3411         if( pDocSh )//XXX
3412             pObj = pDocSh->GetBasic();
3413         else
3414             pObj = SfxApplication::GetBasic();
3415     }
3416     catch (...)
3417     {
3418         return false;
3419     }
3420 
3421     if (!pObj)
3422         return false;
3423 
3424     // ODFF recommends to store user-defined functions prefixed with "USER.",
3425     // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3426     // function name so a function "USER.FOO" could not exist, and macro check
3427     // is assigned the lowest priority in function name check.
3428     if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
3429         aName = aName.copy(5);
3430 
3431     SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method ));
3432     if( !pMeth )
3433     {
3434         return false;
3435     }
3436     // It really should be a BASIC function!
3437     if( pMeth->GetType() == SbxVOID
3438      || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
3439      || dynamic_cast<const SbMethod*>( pMeth) ==  nullptr )
3440     {
3441         return false;
3442     }
3443     maRawToken.SetExternal( aName );
3444     maRawToken.eOp = ocMacro;
3445     return true;
3446 #endif
3447 }
3448 
3449 bool ScCompiler::IsNamedRange( const OUString& rUpperName )
3450 {
3451     // IsNamedRange is called only from NextNewToken, with an upper-case string
3452 
3453     // try local names first
3454     sal_Int16 nSheet = aPos.Tab();
3455     ScRangeName* pRangeName = pDoc->GetRangeName(nSheet);
3456     const ScRangeData* pData = nullptr;
3457     if (pRangeName)
3458         pData = pRangeName->findByUpperName(rUpperName);
3459     if (!pData)
3460     {
3461         pRangeName = pDoc->GetRangeName();
3462         if (pRangeName)
3463             pData = pRangeName->findByUpperName(rUpperName);
3464         if (pData)
3465             nSheet = -1;
3466     }
3467 
3468     if (pData)
3469     {
3470         maRawToken.SetName( nSheet, pData->GetIndex());
3471         return true;
3472     }
3473 
3474     // Sheet-local name with sheet specified.
3475     if (mnCurrentSheetEndPos > 0 && mnCurrentSheetTab >= 0)
3476     {
3477         OUString aName( rUpperName.copy( mnCurrentSheetEndPos));
3478         pRangeName = pDoc->GetRangeName( mnCurrentSheetTab);
3479         if (pRangeName)
3480         {
3481             pData = pRangeName->findByUpperName(aName);
3482             if (pData)
3483             {
3484                 maRawToken.SetName( mnCurrentSheetTab, pData->GetIndex());
3485                 return true;
3486             }
3487         }
3488     }
3489 
3490     return false;
3491 }
3492 
3493 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
3494 {
3495     /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3496      * correctly parses external named references in OOo, as required per RFE
3497      * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3498      * spec first. Until then don't pretend to support external names that
3499      * wouldn't survive a save and reload cycle, return false instead. */
3500 
3501     rbInvalidExternalNameRange = false;
3502 
3503     if (!pConv)
3504         return false;
3505 
3506     OUString aFile, aName;
3507     if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
3508         return false;
3509 
3510     if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
3511         return false;
3512 
3513     ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3514     OUString aTmp = aFile;
3515     pRefMgr->convertToAbsName(aTmp);
3516     aFile = aTmp;
3517     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
3518     if (!pRefMgr->isValidRangeName(nFileId, aName))
3519     {
3520         rbInvalidExternalNameRange = true;
3521         // range name doesn't exist in the source document.
3522         return false;
3523     }
3524 
3525     const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
3526     maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp);
3527     maExternalFiles.push_back(nFileId);
3528     return true;
3529 }
3530 
3531 bool ScCompiler::IsDBRange( const OUString& rName )
3532 {
3533     ScDBCollection::NamedDBs& rDBs = pDoc->GetDBCollection()->getNamedDBs();
3534     const ScDBData* p = rDBs.findByUpperName(rName);
3535     if (!p)
3536         return false;
3537 
3538     maRawToken.SetName( -1, p->GetIndex()); // DB range is always global.
3539     maRawToken.eOp = ocDBArea;
3540     return true;
3541 }
3542 
3543 bool ScCompiler::IsColRowName( const OUString& rName )
3544 {
3545     bool bInList = false;
3546     bool bFound = false;
3547     ScSingleRefData aRef;
3548     OUString aName( rName );
3549     DeQuote( aName );
3550     SCTAB nThisTab = aPos.Tab();
3551     for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
3552     {   // first check ranges on this sheet, in case of duplicated names
3553         for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3554         {
3555             ScRangePairList* pRL;
3556             if ( !jRow )
3557                 pRL = pDoc->GetColNameRanges();
3558             else
3559                 pRL = pDoc->GetRowNameRanges();
3560             for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
3561             {
3562                 const ScRangePair & rR = (*pRL)[iPair];
3563                 const ScRange& rNameRange = rR.GetRange(0);
3564                 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
3565                         nThisTab <= rNameRange.aEnd.Tab()) )
3566                     continue;   // for
3567                 ScCellIterator aIter( pDoc, rNameRange );
3568                 for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
3569                 {
3570                     // Don't crash if cell (via CompileNameFormula) encounters
3571                     // a formula cell without code and
3572                     // HasStringData/Interpret/Compile is executed and all that
3573                     // recursively...
3574                     // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3575                     CellType eType = aIter.getType();
3576                     bool bOk = false;
3577                     if (eType == CELLTYPE_FORMULA)
3578                     {
3579                         ScFormulaCell* pFC = aIter.getFormulaCell();
3580                         bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3581                     }
3582                     else
3583                         bOk = true;
3584 
3585                     if (bOk && aIter.hasString())
3586                     {
3587                         OUString aStr = aIter.getString();
3588                         if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3589                         {
3590                             aRef.InitFlags();
3591                             if ( !jRow )
3592                                 aRef.SetColRel( true );     // ColName
3593                             else
3594                                 aRef.SetRowRel( true );     // RowName
3595                             aRef.SetAddress(pDoc->GetSheetLimits(), aIter.GetPos(), aPos);
3596                             bInList = bFound = true;
3597                         }
3598                     }
3599                 }
3600             }
3601         }
3602     }
3603     if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
3604     {   // search in current sheet
3605         long nDistance = 0, nMax = 0;
3606         long nMyCol = static_cast<long>(aPos.Col());
3607         long nMyRow = static_cast<long>(aPos.Row());
3608         bool bTwo = false;
3609         ScAddress aOne( 0, 0, aPos.Tab() );
3610         ScAddress aTwo( pDoc->MaxCol(), pDoc->MaxRow(), aPos.Tab() );
3611 
3612         ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
3613         if ( pNameCache )
3614         {
3615             //  use GetNameOccurrences to collect all positions of aName on the sheet
3616             //  (only once), similar to the outer part of the loop in the "else" branch.
3617 
3618             const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
3619 
3620             //  Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3621             //  The order of addresses in the vector is the same as from ScCellIterator.
3622 
3623             for ( const ScAddress& aAddress : rAddresses )
3624             {
3625                 if ( bFound )
3626                 {   // stop if everything else is further away
3627                     if ( nMax < static_cast<long>(aAddress.Col()) )
3628                         break;      // aIter
3629                 }
3630                 if ( aAddress != aPos )
3631                 {
3632                     // same treatment as in isEqual case below
3633 
3634                     SCCOL nCol = aAddress.Col();
3635                     SCROW nRow = aAddress.Row();
3636                     long nC = nMyCol - nCol;
3637                     long nR = nMyRow - nRow;
3638                     if ( bFound )
3639                     {
3640                         long nD = nC * nC + nR * nR;
3641                         if ( nD < nDistance )
3642                         {
3643                             if ( nC < 0 || nR < 0 )
3644                             {   // right or below
3645                                 bTwo = true;
3646                                 aTwo.Set( nCol, nRow, aAddress.Tab() );
3647                                 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3648                                 nDistance = nD;
3649                             }
3650                             else if ( !(nRow < aOne.Row() && nMyRow >= static_cast<long>(aOne.Row())) )
3651                             {
3652                                 // upper left, only if not further up than the
3653                                 // current entry and nMyRow is below (CellIter
3654                                 // runs column-wise)
3655                                 bTwo = false;
3656                                 aOne.Set( nCol, nRow, aAddress.Tab() );
3657                                 nMax = std::max( nMyCol + nC, nMyRow + nR );
3658                                 nDistance = nD;
3659                             }
3660                         }
3661                     }
3662                     else
3663                     {
3664                         aOne.Set( nCol, nRow, aAddress.Tab() );
3665                         nDistance = nC * nC + nR * nR;
3666                         nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3667 
3668                     }
3669                     bFound = true;
3670                 }
3671             }
3672         }
3673         else
3674         {
3675             ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
3676             for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3677             {
3678                 if ( bFound )
3679                 {   // stop if everything else is further away
3680                     if ( nMax < static_cast<long>(aIter.GetPos().Col()) )
3681                         break;      // aIter
3682                 }
3683                 CellType eType = aIter.getType();
3684                 bool bOk = false;
3685                 if (eType == CELLTYPE_FORMULA)
3686                 {
3687                     ScFormulaCell* pFC = aIter.getFormulaCell();
3688                     bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3689                 }
3690                 else
3691                     bOk = true;
3692 
3693                 if (bOk && aIter.hasString())
3694                 {
3695                     OUString aStr = aIter.getString();
3696                     if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3697                     {
3698                         SCCOL nCol = aIter.GetPos().Col();
3699                         SCROW nRow = aIter.GetPos().Row();
3700                         long nC = nMyCol - nCol;
3701                         long nR = nMyRow - nRow;
3702                         if ( bFound )
3703                         {
3704                             long nD = nC * nC + nR * nR;
3705                             if ( nD < nDistance )
3706                             {
3707                                 if ( nC < 0 || nR < 0 )
3708                                 {   // right or below
3709                                     bTwo = true;
3710                                     aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3711                                     nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3712                                     nDistance = nD;
3713                                 }
3714                                 else if ( !(nRow < aOne.Row() && nMyRow >= static_cast<long>(aOne.Row())) )
3715                                 {
3716                                     // upper left, only if not further up than the
3717                                     // current entry and nMyRow is below (CellIter
3718                                     // runs column-wise)
3719                                     bTwo = false;
3720                                     aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3721                                     nMax = std::max( nMyCol + nC, nMyRow + nR );
3722                                     nDistance = nD;
3723                                 }
3724                             }
3725                         }
3726                         else
3727                         {
3728                             aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3729                             nDistance = nC * nC + nR * nR;
3730                             nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3731                         }
3732                         bFound = true;
3733                     }
3734                 }
3735             }
3736         }
3737 
3738         if ( bFound )
3739         {
3740             ScAddress aAdr;
3741             if ( bTwo )
3742             {
3743                 if ( nMyCol >= static_cast<long>(aOne.Col()) && nMyRow >= static_cast<long>(aOne.Row()) )
3744                     aAdr = aOne;        // upper left takes precedence
3745                 else
3746                 {
3747                     if ( nMyCol < static_cast<long>(aOne.Col()) )
3748                     {   // two to the right
3749                         if ( nMyRow >= static_cast<long>(aTwo.Row()) )
3750                             aAdr = aTwo;        // directly right
3751                         else
3752                             aAdr = aOne;
3753                     }
3754                     else
3755                     {   // two below or below and right, take the nearest
3756                         long nC1 = nMyCol - aOne.Col();
3757                         long nR1 = nMyRow - aOne.Row();
3758                         long nC2 = nMyCol - aTwo.Col();
3759                         long nR2 = nMyRow - aTwo.Row();
3760                         if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3761                             aAdr = aOne;
3762                         else
3763                             aAdr = aTwo;
3764                     }
3765                 }
3766             }
3767             else
3768                 aAdr = aOne;
3769             aRef.InitAddress( aAdr );
3770             if ( (aAdr.Row() != pDoc->MaxRow() && pDoc->HasStringData(
3771                     aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3772               || (aAdr.Row() != 0 && pDoc->HasStringData(
3773                     aAdr.Col(), aAdr.Row() - 1, aAdr.Tab())))
3774                 aRef.SetRowRel( true );     // RowName
3775             else
3776                 aRef.SetColRel( true );     // ColName
3777             aRef.SetAddress(pDoc->GetSheetLimits(), aAdr, aPos);
3778         }
3779     }
3780     if ( bFound )
3781     {
3782         maRawToken.SetSingleReference( aRef );
3783         maRawToken.eOp = ocColRowName;
3784         return true;
3785     }
3786     else
3787         return false;
3788 }
3789 
3790 bool ScCompiler::IsBoolean( const OUString& rName )
3791 {
3792     OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) );
3793     if( iLook != mxSymbols->getHashMap().end() &&
3794         ((*iLook).second == ocTrue ||
3795          (*iLook).second == ocFalse) )
3796     {
3797         maRawToken.SetOpCode( (*iLook).second );
3798         return true;
3799     }
3800     else
3801         return false;
3802 }
3803 
3804 bool ScCompiler::IsErrorConstant( const OUString& rName ) const
3805 {
3806     FormulaError nError = GetErrorConstant( rName);
3807     if (nError != FormulaError::NONE)
3808     {
3809         maRawToken.SetErrorConstant( nError);
3810         return true;
3811     }
3812     else
3813         return false;
3814 }
3815 
3816 bool ScCompiler::IsTableRefItem( const OUString& rName ) const
3817 {
3818     bool bItem = false;
3819     OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
3820     if (iLook != mxSymbols->getHashMap().end())
3821     {
3822         // Only called when there actually is a current TableRef, hence
3823         // accessing maTableRefs.back() is safe.
3824         ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3825         assert(p);  // not a ScTableRefToken can't be
3826 
3827         switch ((*iLook).second)
3828         {
3829             case ocTableRefItemAll:
3830                 bItem = true;
3831                 p->AddItem( ScTableRefToken::ALL);
3832                 break;
3833             case ocTableRefItemHeaders:
3834                 bItem = true;
3835                 p->AddItem( ScTableRefToken::HEADERS);
3836                 break;
3837             case ocTableRefItemData:
3838                 bItem = true;
3839                 p->AddItem( ScTableRefToken::DATA);
3840                 break;
3841             case ocTableRefItemTotals:
3842                 bItem = true;
3843                 p->AddItem( ScTableRefToken::TOTALS);
3844                 break;
3845             case ocTableRefItemThisRow:
3846                 bItem = true;
3847                 p->AddItem( ScTableRefToken::THIS_ROW);
3848                 break;
3849             default:
3850                 ;
3851         }
3852         if (bItem)
3853             maRawToken.SetOpCode( (*iLook).second );
3854     }
3855     return bItem;
3856 }
3857 
3858 namespace {
3859 OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
3860 {
3861     // '#', '[', ']' and '\'' are escaped with '\''
3862 
3863     if (rStr.indexOf( '\'' ) < 0)
3864         return rStr;
3865 
3866     const sal_Int32 n = rStr.getLength();
3867     OUStringBuffer aBuf( n );
3868     const sal_Unicode* p = rStr.getStr();
3869     const sal_Unicode* const pStop = p + n;
3870     bool bEscaped = false;
3871     for ( ; p < pStop; ++p)
3872     {
3873         const sal_Unicode c = *p;
3874         if (bEscaped)
3875         {
3876             aBuf.append( c );
3877             bEscaped = false;
3878         }
3879         else if (c == '\'')
3880             bEscaped = true;    // unescaped escaping '\''
3881         else
3882             aBuf.append( c );
3883     }
3884     return aBuf.makeStringAndClear();
3885 }
3886 }
3887 
3888 bool ScCompiler::IsTableRefColumn( const OUString& rName ) const
3889 {
3890     // Only called when there actually is a current TableRef, hence
3891     // accessing maTableRefs.back() is safe.
3892     ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3893     assert(p);  // not a ScTableRefToken can't be
3894 
3895     ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( p->GetIndex());
3896     if (!pDBData)
3897         return false;
3898 
3899     OUString aName( unescapeTableRefColumnSpecifier( rName));
3900 
3901     ScRange aRange;
3902     pDBData->GetArea( aRange);
3903     aRange.aEnd.SetTab( aRange.aStart.Tab());
3904     aRange.aEnd.SetRow( aRange.aStart.Row());
3905 
3906     // Prefer the stored internal table column name, which is also needed for
3907     // named expressions during document load time when cell content isn't
3908     // available yet. Also, avoiding a possible calculation step in case the
3909     // header cell is a formula cell is "a good thing".
3910     sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
3911     if (nOffset >= 0)
3912     {
3913         // This is sneaky... we always use the top row of the database range,
3914         // regardless of whether it is a header row or not. Code evaluating
3915         // this reference must take that into account and may have to act
3916         // differently if it is a header-less table. Which are two places,
3917         // HandleTableRef() (no change necessary there) and
3918         // CreateStringFromSingleRef() (must not fallback to cell lookup).
3919         ScSingleRefData aRef;
3920         ScAddress aAdr( aRange.aStart);
3921         aAdr.IncCol( nOffset);
3922         aRef.InitAddress( aAdr);
3923         maRawToken.SetSingleReference( aRef );
3924         return true;
3925     }
3926 
3927     if (pDBData->HasHeader())
3928     {
3929         // Quite similar to IsColRowName() but limited to one row of headers.
3930         ScCellIterator aIter( pDoc, aRange);
3931         for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3932         {
3933             CellType eType = aIter.getType();
3934             bool bOk = false;
3935             if (eType == CELLTYPE_FORMULA)
3936             {
3937                 ScFormulaCell* pFC = aIter.getFormulaCell();
3938                 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3939             }
3940             else
3941                 bOk = true;
3942 
3943             if (bOk && aIter.hasString())
3944             {
3945                 OUString aStr = aIter.getString();
3946                 if (ScGlobal::GetpTransliteration()->isEqual( aStr, aName))
3947                 {
3948                     // If this is successful and the internal column name
3949                     // lookup was not, it may be worth a warning.
3950                     SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
3951 
3952                     /* XXX NOTE: we could init the column as relative so copying a
3953                      * formula across columns would point to the relative column,
3954                      * but do it absolute because:
3955                      * a) it makes the reference work in named expressions without
3956                      * having to distinguish
3957                      * b) Excel does it the same. */
3958                     ScSingleRefData aRef;
3959                     aRef.InitAddress( aIter.GetPos());
3960                     maRawToken.SetSingleReference( aRef );
3961                     return true;
3962                 }
3963             }
3964         }
3965     }
3966 
3967     return false;
3968 }
3969 
3970 void ScCompiler::SetAutoCorrection( bool bVal )
3971 {
3972     assert(mbJumpCommandReorder);
3973     bAutoCorrect = bVal;
3974     mbStopOnError = !bVal;
3975 }
3976 
3977 void ScCompiler::AutoCorrectParsedSymbol()
3978 {
3979     sal_Int32 nPos = aCorrectedSymbol.getLength();
3980     if ( nPos )
3981     {
3982         nPos--;
3983         const sal_Unicode cQuote = '\"';
3984         const sal_Unicode cx = 'x';
3985         const sal_Unicode cX = 'X';
3986         sal_Unicode c1 = aCorrectedSymbol[0];
3987         sal_Unicode c2 = aCorrectedSymbol[nPos];
3988         sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
3989         if ( c1 == cQuote && c2 != cQuote  )
3990         {   // "...
3991             // What's not a word doesn't belong to it.
3992             // Don't be pedantic: c < 128 should be sufficient here.
3993             while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
3994                     ((GetCharTableFlags(aCorrectedSymbol[nPos], aCorrectedSymbol[nPos-1]) &
3995                     (ScCharFlags::Word | ScCharFlags::CharDontCare)) == ScCharFlags::NONE)) )
3996                 nPos--;
3997             if ( nPos == MAXSTRLEN - 1 )
3998                 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) );   // '"' the MAXSTRLENth character
3999             else
4000                 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) );
4001             bCorrected = true;
4002         }
4003         else if ( c1 != cQuote && c2 == cQuote )
4004         {   // ..."
4005             aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol;
4006             bCorrected = true;
4007         }
4008         else if ( nPos == 0 && (c1 == cx || c1 == cX) )
4009         {   // x => *
4010             aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
4011             bCorrected = true;
4012         }
4013         else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue)
4014                && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) )
4015         {
4016             if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx
4017             {   // x => *
4018                 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4019                 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c));
4020                 bCorrected = true;
4021             }
4022             if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX
4023             {   // X => *
4024                 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4025                 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c));
4026                 bCorrected = true;
4027             }
4028         }
4029         else
4030         {
4031             OUString aSymbol( aCorrectedSymbol );
4032             OUString aDoc;
4033             sal_Int32 nPosition;
4034             if ( aSymbol[0] == '\''
4035               && ((nPosition = aSymbol.indexOf( "'#" )) != -1) )
4036             {   // Split off 'Doc'#, may be d:\... or whatever
4037                 aDoc = aSymbol.copy(0, nPosition + 2);
4038                 aSymbol = aSymbol.copy(nPosition + 2);
4039             }
4040             sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
4041             bool bColons;
4042             if ( nRefs > 2 )
4043             {   // duplicated or too many ':'? B:2::C10 => B2:C10
4044                 bColons = true;
4045                 sal_Int32 nIndex = 0;
4046                 OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
4047                 sal_Int32 nLen1 = aTmp1.getLength();
4048                 OUStringBuffer aSym;
4049                 OUString aTmp2;
4050                 bool bLastAlp, bNextNum;
4051                 bLastAlp = bNextNum = true;
4052                 sal_Int32 nStrip = 0;
4053                 sal_Int32 nCount = nRefs;
4054                 for ( sal_Int32 j=1; j<nCount; j++ )
4055                 {
4056                     aTmp2 = aSymbol.getToken( 0, ':', nIndex );
4057                     sal_Int32 nLen2 = aTmp2.getLength();
4058                     if ( nLen1 || nLen2 )
4059                     {
4060                         if ( nLen1 )
4061                         {
4062                             aSym.append(aTmp1);
4063                             bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
4064                         }
4065                         if ( nLen2 )
4066                         {
4067                             bNextNum = CharClass::isAsciiNumeric( aTmp2 );
4068                             if ( bLastAlp == bNextNum && nStrip < 1 )
4069                             {
4070                                 // Must be alternating number/string, only
4071                                 // strip within a reference.
4072                                 nRefs--;
4073                                 nStrip++;
4074                             }
4075                             else
4076                             {
4077                                 if ( !aSym.isEmpty() && aSym[aSym.getLength()-1] != ':')
4078                                     aSym.append(":");
4079                                 nStrip = 0;
4080                             }
4081                             bLastAlp = !bNextNum;
4082                         }
4083                         else
4084                         {   // ::
4085                             nRefs--;
4086                             if ( nLen1 )
4087                             {   // B10::C10 ? append ':' on next round
4088                                 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
4089                                     nStrip++;
4090                             }
4091                         }
4092                         aTmp1 = aTmp2;
4093                         nLen1 = nLen2;
4094                     }
4095                     else
4096                         nRefs--;
4097                 }
4098                 aSymbol = aSym.makeStringAndClear() + aTmp1;
4099             }
4100             else
4101                 bColons = false;
4102             if ( nRefs && nRefs <= 2 )
4103             {   // reference twisted? 4A => A4 etc.
4104                 OUString aTab[2], aRef[2];
4105                 const ScAddress::Details aDetails( pConv->meConv, aPos );
4106                 if ( nRefs == 2 )
4107                 {
4108                     sal_Int32 nIdx{ 0 };
4109                     aRef[0] = aSymbol.getToken( 0, ':', nIdx );
4110                     aRef[1] = aSymbol.getToken( 0, ':', nIdx );
4111                 }
4112                 else
4113                     aRef[0] = aSymbol;
4114 
4115                 bool bChanged = false;
4116                 bool bOk = true;
4117                 ScRefFlags nMask = ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID;
4118                 for ( int j=0; j<nRefs; j++ )
4119                 {
4120                     sal_Int32 nTmp = 0;
4121                     sal_Int32 nDotPos = -1;
4122                     while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
4123                         nDotPos = nTmp++;      // the last one counts
4124                     if ( nDotPos != -1 )
4125                     {
4126                         aTab[j] = aRef[j].copy( 0, nDotPos + 1 );  // with '.'
4127                         aRef[j] = aRef[j].copy( nDotPos + 1 );
4128                     }
4129                     OUString aOld( aRef[j] );
4130                     OUStringBuffer aStr2;
4131                     const sal_Unicode* p = aRef[j].getStr();
4132                     while ( *p && rtl::isAsciiDigit( *p ) )
4133                         aStr2.append(*p++);
4134                     aRef[j] = OUString( p );
4135                     aRef[j] += aStr2.makeStringAndClear();
4136                     if ( bColons || aRef[j] != aOld )
4137                     {
4138                         bChanged = true;
4139                         ScAddress aAdr;
4140                         bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
4141                     }
4142                 }
4143                 if ( bChanged && bOk )
4144                 {
4145                     aCorrectedSymbol = aDoc;
4146                     aCorrectedSymbol += aTab[0];
4147                     aCorrectedSymbol += aRef[0];
4148                     if ( nRefs == 2 )
4149                     {
4150                         aCorrectedSymbol += ":";
4151                         aCorrectedSymbol += aTab[1];
4152                         aCorrectedSymbol += aRef[1];
4153                     }
4154                     bCorrected = true;
4155                 }
4156             }
4157         }
4158     }
4159 }
4160 
4161 static bool lcl_UpperAsciiOrI18n( OUString& rUpper, const OUString& rOrg, FormulaGrammar::Grammar eGrammar )
4162 {
4163     if (FormulaGrammar::isODFF( eGrammar ))
4164     {
4165         // ODFF has a defined set of English function names, avoid i18n
4166         // overhead.
4167         rUpper = rOrg.toAsciiUpperCase();
4168         return true;
4169     }
4170     else
4171     {
4172         rUpper = ScGlobal::pCharClass->uppercase(rOrg);
4173         return false;
4174     }
4175 }
4176 
4177 bool ScCompiler::NextNewToken( bool bInArray )
4178 {
4179     bool bAllowBooleans = bInArray;
4180     sal_Int32 nSpaces = NextSymbol(bInArray);
4181 
4182     if (!cSymbol[0])
4183     {
4184         if (nSrcPos < aFormula.getLength())
4185         {
4186             // Nothing could be parsed, remainder as bad string.
4187             // NextSymbol() must had set an error for this.
4188             assert( pArr->GetCodeError() != FormulaError::NONE);
4189             const OUString aBad( aFormula.copy( nSrcPos));
4190             svl::SharedString aSS = pDoc->GetSharedStringPool().intern( aBad);
4191             maRawToken.SetString( aSS.getData(), aSS.getDataIgnoreCase());
4192             maRawToken.NewOpCode( ocBad);
4193             nSrcPos = aFormula.getLength();
4194             // Add bad string as last token.
4195             return true;
4196         }
4197         return false;
4198     }
4199 
4200     if( nSpaces )
4201     {
4202         ScRawToken aToken;
4203         aToken.SetOpCode( ocSpaces );
4204         aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(nSpaces, 255) );
4205         if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
4206         {
4207             SetError(FormulaError::CodeOverflow);
4208             return false;
4209         }
4210     }
4211 
4212     // Short cut for references when reading ODF to speedup things.
4213     if (mnPredetectedReference)
4214     {
4215         OUString aStr( cSymbol);
4216         bool bInvalidExternalNameRange;
4217         if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange ))
4218         {
4219             svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aStr);
4220             maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
4221             maRawToken.NewOpCode( ocBad );
4222         }
4223         return true;
4224     }
4225 
4226     if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
4227             !bAutoCorrect )
4228     {   // special case to speed up broken [$]#REF documents
4229         /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4230          * be processed as usual. That would need some special treatment,
4231          * also in NextSymbol() because of possible combinations of
4232          * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4233          * handled by IsPredetectedReference(), this case here remains for
4234          * manual/API input. */
4235         OUString aBad( aFormula.copy( nSrcPos-1 ) );
4236         eLastOp = pArr->AddBad( aBad )->GetOpCode();
4237         return false;
4238     }
4239 
4240     if( IsString() )
4241         return true;
4242 
4243     bool bMayBeFuncName;
4244     bool bAsciiNonAlnum;    // operators, separators, ...
4245     if ( cSymbol[0] < 128 )
4246     {
4247         bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
4248         if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing())
4249         {
4250             SvtMiscOptions aOpt;
4251             bMayBeFuncName = aOpt.IsExperimentalMode();
4252         }
4253 
4254         bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
4255     }
4256     else
4257     {
4258         OUString aTmpStr( cSymbol[0] );
4259         bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
4260         bAsciiNonAlnum = false;
4261     }
4262     if (bAsciiNonAlnum && cSymbol[1] == 0)
4263     {
4264         // Shortcut for operators and separators that need no further checks or upper.
4265         if (IsOpCode( OUString( cSymbol), bInArray ))
4266             return true;
4267     }
4268     if ( bMayBeFuncName )
4269     {
4270         // a function name must be followed by a parenthesis
4271         const sal_Unicode* p = aFormula.getStr() + nSrcPos;
4272         while( *p == ' ' )
4273             p++;
4274         bMayBeFuncName = ( *p == '(' );
4275     }
4276 
4277     // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
4278     // IsReference().
4279 
4280     OUString aUpper;
4281 
4282     do
4283     {
4284         const OUString aOrg( cSymbol );
4285 
4286         // Check for TableRef column specifier first, it may be anything.
4287         if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
4288         {
4289             if (IsTableRefColumn( aOrg ))
4290                 return true;
4291             // Do not attempt to resolve as any other name.
4292             aUpper = aOrg;  // for ocBad
4293             break;          // do; create ocBad token or set error.
4294         }
4295 
4296         mbRewind = false;
4297         aUpper.clear();
4298         bool bAsciiUpper = false;
4299 
4300         if (bAsciiNonAlnum)
4301         {
4302             bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
4303             if (cSymbol[0] == '#')
4304             {
4305                 // Check for TableRef item specifiers first.
4306                 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
4307                 {
4308                     if (IsTableRefItem( aUpper ))
4309                         return true;
4310                 }
4311 
4312                 // This can be either an error constant ...
4313                 if (IsErrorConstant( aUpper))
4314                     return true;
4315 
4316                 // ... or some invalidated reference starting with #REF!
4317                 // which is handled after the do loop.
4318 
4319                 break;  // do; create ocBad token or set error.
4320             }
4321             if (IsOpCode( aUpper, bInArray ))
4322                 return true;
4323         }
4324 
4325         if (bMayBeFuncName)
4326         {
4327             if (aUpper.isEmpty())
4328                 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
4329             if (IsOpCode( aUpper, bInArray ))
4330                 return true;
4331         }
4332 
4333         // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4334         // referred => IsReference() before IsValue().
4335         // Preserve case of file names in external references.
4336         if (IsReference( aOrg ))
4337         {
4338             if (mbRewind)   // Range operator, but no direct reference.
4339                 continue;   // do; up to range operator.
4340             // If a syntactically correct reference was recognized but invalid
4341             // e.g. because of non-existing sheet name => entire reference
4342             // ocBad to preserve input instead of #REF!.A1
4343             if (!maRawToken.IsValidReference(pDoc))
4344             {
4345                 aUpper = aOrg;          // ensure for ocBad
4346                 break;                  // do; create ocBad token or set error.
4347             }
4348             return true;
4349         }
4350 
4351         if (aUpper.isEmpty())
4352             bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
4353 
4354         // IsBoolean() before IsValue() to catch inline bools without the kludge
4355         //    for inline arrays.
4356         if (bAllowBooleans && IsBoolean( aUpper ))
4357             return true;
4358 
4359         if (IsValue( aUpper ))
4360             return true;
4361 
4362         // User defined names and such do need i18n upper also in ODF.
4363         if (bAsciiUpper)
4364             aUpper = ScGlobal::pCharClass->uppercase( aOrg );
4365 
4366         if (IsNamedRange( aUpper ))
4367             return true;
4368 
4369         // Compiling a named expression during collecting them in import shall
4370         // not match arbitrary names that otherwise if all named expressions
4371         // were present would be recognized as named expression. Such name will
4372         // flag an error below and will be recompiled in a second step later
4373         // with ScRangeData::CompileUnresolvedXML()
4374         if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_NO_BREAK && pDoc->IsImportingXML())
4375             break;  // while
4376 
4377         // Preserve case of file names in external references.
4378         bool bInvalidExternalNameRange;
4379         if (IsExternalNamedRange( aOrg, bInvalidExternalNameRange ))
4380             return true;
4381         // Preserve case of file names in external references even when range
4382         // is not valid and previous check failed tdf#89330
4383         if (bInvalidExternalNameRange)
4384         {
4385             // add ocBad but do not lowercase
4386             svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aOrg);
4387             maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
4388             maRawToken.NewOpCode( ocBad );
4389             return true;
4390         }
4391         if (IsDBRange( aUpper ))
4392             return true;
4393         // If followed by '(' (with or without space inbetween) it can not be a
4394         // column/row label. Prevent arbitrary content detection.
4395         if (!bMayBeFuncName && IsColRowName( aUpper ))
4396             return true;
4397         if (bMayBeFuncName && IsMacro( aUpper ))
4398             return true;
4399         if (bMayBeFuncName && IsOpCode2( aUpper ))
4400             return true;
4401 
4402     } while (mbRewind);
4403 
4404     // Last chance: it could be a broken invalidated reference that contains
4405     // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4406     // and 2016 until 5.1.4
4407     OUString aErrRef( mxSymbols->getSymbol( ocErrRef));
4408     if (aUpper.indexOf( aErrRef) >= 0 && IsReference( aUpper, &aErrRef))
4409         return true;
4410 
4411     if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE )
4412     {
4413         // set an error
4414         SetError( FormulaError::NoName );
4415         if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_BREAK)
4416             return false;   // end compilation
4417     }
4418 
4419     // Provide single token information and continue. Do not set an error, that
4420     // would prematurely end compilation. Simple unknown names are handled by
4421     // the interpreter.
4422     aUpper = ScGlobal::pCharClass->lowercase( aUpper );
4423     svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aUpper);
4424     maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
4425     maRawToken.NewOpCode( ocBad );
4426     if ( bAutoCorrect )
4427         AutoCorrectParsedSymbol();
4428     return true;
4429 }
4430 
4431 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
4432 {
4433     bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
4434     sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
4435     OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4436     if( pArr->GetLen() == nExpectedCount )
4437     {
4438         FormulaToken** ppTokens = pArr->GetArray();
4439         // string tokens expected, GetString() will assert if token type is wrong
4440         rFormula = ppTokens[0]->GetString().getString();
4441         if( bExternal )
4442             rFormulaNmsp = ppTokens[1]->GetString().getString();
4443     }
4444 }
4445 
4446 namespace {
4447 
4448 class ExternalFileInserter
4449 {
4450     ScAddress const maPos;
4451     ScExternalRefManager& mrRefMgr;
4452 public:
4453     ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
4454         maPos(rPos), mrRefMgr(rRefMgr) {}
4455 
4456     void operator() (sal_uInt16 nFileId) const
4457     {
4458         mrRefMgr.insertRefCell(nFileId, maPos);
4459     }
4460 };
4461 
4462 }
4463 
4464 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula )
4465 {
4466     OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4467     if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
4468         SetGrammar( FormulaGrammar::GRAM_PODF );
4469 
4470     ScTokenArray aArr(pDoc);
4471     pArr = &aArr;
4472     maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
4473     aFormula = comphelper::string::strip(rFormula, ' ');
4474 
4475     nSrcPos = 0;
4476     bCorrected = false;
4477     if ( bAutoCorrect )
4478     {
4479         aCorrectedFormula.clear();
4480         aCorrectedSymbol.clear();
4481     }
4482     sal_uInt8 nForced = 0;   // ==formula forces recalc even if cell is not visible
4483     if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4484     {
4485         nSrcPos++;
4486         nForced++;
4487         if ( bAutoCorrect )
4488             aCorrectedFormula += "=";
4489     }
4490     if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4491     {
4492         nSrcPos++;
4493         nForced++;
4494         if ( bAutoCorrect )
4495             aCorrectedFormula += "=";
4496     }
4497     struct FunctionStack
4498     {
4499         OpCode  eOp;
4500         short   nSep;
4501     };
4502     // FunctionStack only used if PODF or OOXML!
4503     bool bPODF = FormulaGrammar::isPODF( meGrammar);
4504     bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
4505     bool bUseFunctionStack = (bPODF || bOOXML);
4506     const size_t nAlloc = 512;
4507     FunctionStack aFuncs[ nAlloc ];
4508     FunctionStack* pFunctionStack = (bUseFunctionStack && static_cast<size_t>(rFormula.getLength()) > nAlloc ?
4509          new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
4510     pFunctionStack[0].eOp = ocNone;
4511     pFunctionStack[0].nSep = 0;
4512     size_t nFunction = 0;
4513     short nBrackets = 0;
4514     bool bInArray = false;
4515     eLastOp = ocOpen;
4516     while( NextNewToken( bInArray ) )
4517     {
4518         const OpCode eOp = maRawToken.GetOpCode();
4519         if (eOp == ocSkip)
4520             continue;
4521 
4522         switch (eOp)
4523         {
4524             case ocOpen:
4525             {
4526                 ++nBrackets;
4527                 if (bUseFunctionStack)
4528                 {
4529                     ++nFunction;
4530                     pFunctionStack[ nFunction ].eOp = eLastOp;
4531                     pFunctionStack[ nFunction ].nSep = 0;
4532                 }
4533             }
4534             break;
4535             case ocClose:
4536             {
4537                 if( !nBrackets )
4538                 {
4539                     SetError( FormulaError::PairExpected );
4540                     if ( bAutoCorrect )
4541                     {
4542                         bCorrected = true;
4543                         aCorrectedSymbol.clear();
4544                     }
4545                 }
4546                 else
4547                     nBrackets--;
4548                 if (bUseFunctionStack && nFunction)
4549                     --nFunction;
4550             }
4551             break;
4552             case ocSep:
4553             {
4554                 if (bUseFunctionStack)
4555                     ++pFunctionStack[ nFunction ].nSep;
4556             }
4557             break;
4558             case ocArrayOpen:
4559             {
4560                 if( bInArray )
4561                     SetError( FormulaError::NestedArray );
4562                 else
4563                     bInArray = true;
4564                 // Don't count following column separator as parameter separator.
4565                 if (bUseFunctionStack)
4566                 {
4567                     ++nFunction;
4568                     pFunctionStack[ nFunction ].eOp = eOp;
4569                     pFunctionStack[ nFunction ].nSep = 0;
4570                 }
4571             }
4572             break;
4573             case ocArrayClose:
4574             {
4575                 if( bInArray )
4576                 {
4577                     bInArray = false;
4578                 }
4579                 else
4580                 {
4581                     SetError( FormulaError::PairExpected );
4582                     if ( bAutoCorrect )
4583                     {
4584                         bCorrected = true;
4585                         aCorrectedSymbol.clear();
4586                     }
4587                 }
4588                 if (bUseFunctionStack && nFunction)
4589                     --nFunction;
4590             }
4591             break;
4592             case ocTableRefOpen:
4593             {
4594                 // Don't count following item separator as parameter separator.
4595                 if (bUseFunctionStack)
4596                 {
4597                     ++nFunction;
4598                     pFunctionStack[ nFunction ].eOp = eOp;
4599                     pFunctionStack[ nFunction ].nSep = 0;
4600                 }
4601             }
4602             break;
4603             case ocTableRefClose:
4604             {
4605                 if (bUseFunctionStack && nFunction)
4606                     --nFunction;
4607             }
4608             break;
4609             case ocColRowName:
4610             case ocColRowNameAuto:
4611                 // The current implementation of column / row labels doesn't
4612                 // function correctly in grouped cells.
4613                 aArr.SetShareable(false);
4614             break;
4615             default:
4616             break;
4617         }
4618         if (!(eLastOp == ocOpen && eOp == ocClose) &&
4619                 (eLastOp == ocOpen ||
4620                  eLastOp == ocSep ||
4621                  eLastOp == ocArrayRowSep ||
4622                  eLastOp == ocArrayColSep ||
4623                  eLastOp == ocArrayOpen) &&
4624                 (eOp == ocSep ||
4625                  eOp == ocClose ||
4626                  eOp == ocArrayRowSep ||
4627                  eOp == ocArrayColSep ||
4628                  eOp == ocArrayClose))
4629         {
4630             // TODO: should we check for known functions with optional empty
4631             // args so the correction dialog can do better?
4632             if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
4633             {
4634                 SetError(FormulaError::CodeOverflow); break;
4635             }
4636         }
4637         if (bOOXML)
4638         {
4639             // Append a parameter for WEEKNUM, all 1.0
4640             // Function is already closed, parameter count is nSep+1
4641             size_t nFunc = nFunction + 1;
4642             if (eOp == ocClose &&
4643                     (pFunctionStack[ nFunc ].eOp == ocWeek &&   // 2nd week start
4644                      pFunctionStack[ nFunc ].nSep == 0))
4645             {
4646                 if (    !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4647                         !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4648                 {
4649                     SetError(FormulaError::CodeOverflow); break;
4650                 }
4651             }
4652         }
4653         else if (bPODF)
4654         {
4655             /* TODO: for now this is the only PODF adapter. If there were more,
4656              * factor this out. */
4657             // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4658             if (eOp == ocSep &&
4659                     pFunctionStack[ nFunction ].eOp == ocAddress &&
4660                     pFunctionStack[ nFunction ].nSep == 3)
4661             {
4662                 if (    !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4663                         !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4664                 {
4665                     SetError(FormulaError::CodeOverflow); break;
4666                 }
4667                 ++pFunctionStack[ nFunction ].nSep;
4668             }
4669         }
4670         FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(pDoc->GetSheetLimits()));
4671         if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose)
4672         {
4673             // Nested inline array or non-value/non-string in array. The
4674             // original tokens are still in the ScTokenArray and not merged
4675             // into an ScMatrixToken. Set error but keep on tokenizing.
4676             SetError( FormulaError::BadArrayContent);
4677         }
4678         else if (!pNewToken)
4679         {
4680             SetError(FormulaError::CodeOverflow);
4681             break;
4682         }
4683         else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
4684         {
4685             static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
4686         }
4687         else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
4688         {
4689             sal_uInt16 nIdx = pArr->GetLen() - 1;
4690             const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
4691             if (pPrev && pPrev->GetOpCode() == ocDBArea)
4692             {
4693                 FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
4694                 maTableRefs.emplace_back( pTableRefToken);
4695                 // pPrev may be dead hereafter.
4696                 static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken,
4697                         FormulaTokenArray::ReplaceMode::CODE_ONLY);
4698             }
4699         }
4700         switch (eOp)
4701         {
4702             case ocTableRefOpen:
4703                 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4704                 if (maTableRefs.empty())
4705                     SetError(FormulaError::Pair);
4706                 else
4707                     ++maTableRefs.back().mnLevel;
4708                 break;
4709             case ocTableRefClose:
4710                 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4711                 if (maTableRefs.empty())
4712                     SetError(FormulaError::Pair);
4713                 else
4714                 {
4715                     if (--maTableRefs.back().mnLevel == 0)
4716                         maTableRefs.pop_back();
4717                 }
4718                 break;
4719             default:
4720                 break;
4721         }
4722         eLastOp = maRawToken.GetOpCode();
4723         if ( bAutoCorrect )
4724             aCorrectedFormula += aCorrectedSymbol;
4725     }
4726     if ( mbCloseBrackets )
4727     {
4728         if( bInArray )
4729         {
4730             FormulaByteToken aToken( ocArrayClose );
4731             if( !pArr->AddToken( aToken ) )
4732             {
4733                 SetError(FormulaError::CodeOverflow);
4734             }
4735             else if ( bAutoCorrect )
4736                 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
4737         }
4738 
4739         if (nBrackets)
4740         {
4741             FormulaToken aToken( svSep, ocClose );
4742             while( nBrackets-- )
4743             {
4744                 if( !pArr->AddToken( aToken ) )
4745                 {
4746                     SetError(FormulaError::CodeOverflow);
4747                     break;  // while
4748                 }
4749                 if ( bAutoCorrect )
4750                     aCorrectedFormula += mxSymbols->getSymbol(ocClose);
4751             }
4752         }
4753     }
4754     if ( nForced >= 2 )
4755         pArr->SetRecalcModeForced();
4756 
4757     if (pFunctionStack != &aFuncs[0])
4758         delete [] pFunctionStack;
4759 
4760     // remember pArr, in case a subsequent CompileTokenArray() is executed.
4761     std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr ));
4762     pNew->GenHash();
4763     // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4764     pArr = pNew.get();
4765     maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
4766 
4767     if (!maExternalFiles.empty())
4768     {
4769         // Remove duplicates, and register all external files found in this cell.
4770         std::sort(maExternalFiles.begin(), maExternalFiles.end());
4771         std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
4772         std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *pDoc->GetExternalRefManager()));
4773         maExternalFiles.erase(itEnd, maExternalFiles.end());
4774     }
4775 
4776     return pNew;
4777 }
4778 
4779 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
4780 {
4781     OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
4782         "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
4783     if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
4784     {
4785         ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
4786         uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
4787         table::CellAddress aReferencePos;
4788         ScUnoConversion::FillApiAddress( aReferencePos, aPos );
4789         uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
4790         ScTokenArray aTokenArray(pDoc);
4791         if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
4792         {
4793             // remember pArr, in case a subsequent CompileTokenArray() is executed.
4794             std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray ));
4795             // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4796             pArr = pNew.get();
4797             maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
4798             return pNew;
4799         }
4800     }
4801     catch( uno::Exception& )
4802     {
4803     }
4804     // no success - fallback to some internal grammar and hope the best
4805     return CompileString( rFormula );
4806 }
4807 
4808 ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const
4809 {
4810     return pDoc->FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
4811 }
4812 
4813 bool ScCompiler::HandleRange()
4814 {
4815     ScTokenArray* pNew;
4816     const ScRangeData* pRangeData = GetRangeData( *mpToken);
4817     if (pRangeData)
4818     {
4819         FormulaError nErr = pRangeData->GetErrCode();
4820         if( nErr != FormulaError::NONE )
4821             SetError( nErr );
4822         else if (mbJumpCommandReorder)
4823         {
4824             // put named formula into parentheses.
4825             // But only if there aren't any yet, parenthetical
4826             // ocSep doesn't work, e.g. SUM((...;...))
4827             // or if not directly between ocSep/parenthesis,
4828             // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
4829             // in short: if it isn't a self-contained expression.
4830             FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces();
4831             FormulaToken* p2 = maArrIterator.PeekNextNoSpaces();
4832             OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep);
4833             OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep);
4834             bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
4835             bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
4836             bool bAddPair = !(bBorder1 && bBorder2);
4837             if ( bAddPair )
4838             {
4839                 pNew = new ScTokenArray(pDoc);
4840                 pNew->AddOpCode( ocClose );
4841                 PushTokenArray( pNew, true );
4842             }
4843             pNew = pRangeData->GetCode()->Clone().release();
4844             pNew->SetFromRangeName( true );
4845             PushTokenArray( pNew, true );
4846             if( pRangeData->HasReferences() )
4847             {
4848                 // Relative sheet references in sheet-local named expressions
4849                 // shall still point to the same sheet as if used on the
4850                 // original sheet, not shifted to the current position where
4851                 // they are used.
4852                 SCTAB nSheetTab = mpToken->GetSheet();
4853                 if (nSheetTab >= 0 && nSheetTab != aPos.Tab())
4854                     AdjustSheetLocalNameRelReferences( nSheetTab - aPos.Tab());
4855 
4856                 SetRelNameReference();
4857                 MoveRelWrap();
4858             }
4859             maArrIterator.Reset();
4860             if ( bAddPair )
4861             {
4862                 pNew = new ScTokenArray(pDoc);
4863                 pNew->AddOpCode( ocOpen );
4864                 PushTokenArray( pNew, true );
4865             }
4866             return GetToken();
4867         }
4868     }
4869     else
4870     {
4871         // No ScRangeData for an already compiled token can happen in BIFF .xls
4872         // import if the original range is not present in the document.
4873         pNew = new ScTokenArray(pDoc);
4874         pNew->Add( new FormulaErrorToken( FormulaError::NoName));
4875         PushTokenArray( pNew, true );
4876         return GetToken();
4877     }
4878     return true;
4879 }
4880 
4881 bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
4882 {
4883     // Handle external range names.
4884     switch (_aToken.GetType())
4885     {
4886         case svExternalSingleRef:
4887         case svExternalDoubleRef:
4888             break;
4889         case svExternalName:
4890         {
4891             ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4892             const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
4893             if (!pFile)
4894             {
4895                 SetError(FormulaError::NoName);
4896                 return true;
4897             }
4898 
4899             OUString aName = _aToken.GetString().getString();
4900             ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
4901                 _aToken.GetIndex(), aName, &aPos);
4902 
4903             if (!xNew)
4904             {
4905                 SetError(FormulaError::NoName);
4906                 return true;
4907             }
4908 
4909             ScTokenArray* pNew = xNew->Clone().release();
4910             PushTokenArray( pNew, true);
4911             if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr)
4912             {
4913                 SetRelNameReference();
4914                 MoveRelWrap();
4915             }
4916             maArrIterator.Reset();
4917             return GetToken();
4918         }
4919         default:
4920             OSL_FAIL("Wrong type for external reference!");
4921             return false;
4922     }
4923     return true;
4924 }
4925 
4926 void ScCompiler::AdjustSheetLocalNameRelReferences( SCTAB nDelta )
4927 {
4928     for ( auto t: pArr->References() )
4929     {
4930         ScSingleRefData& rRef1 = *t->GetSingleRef();
4931         if (rRef1.IsTabRel())
4932             rRef1.IncTab( nDelta);
4933         if ( t->GetType() == svDoubleRef )
4934         {
4935             ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
4936             if (rRef2.IsTabRel())
4937                 rRef2.IncTab( nDelta);
4938         }
4939     }
4940 }
4941 
4942 // reference of named range with relative references
4943 
4944 void ScCompiler::SetRelNameReference()
4945 {
4946     for ( auto t: pArr->References() )
4947     {
4948         ScSingleRefData& rRef1 = *t->GetSingleRef();
4949         if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
4950             rRef1.SetRelName( true );
4951         if ( t->GetType() == svDoubleRef )
4952         {
4953             ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
4954             if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
4955                 rRef2.SetRelName( true );
4956         }
4957     }
4958 }
4959 
4960 // Wrap-adjust relative references of a RangeName to current position,
4961 // don't call for other token arrays!
4962 void ScCompiler::MoveRelWrap()
4963 {
4964     for ( auto t: pArr->References() )
4965     {
4966         if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4967             ScRefUpdate::MoveRelWrap( pDoc, aPos, pDoc->MaxCol(), pDoc->MaxRow(), SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
4968         else
4969             ScRefUpdate::MoveRelWrap( pDoc, aPos, pDoc->MaxCol(), pDoc->MaxRow(), *t->GetDoubleRef() );
4970     }
4971 }
4972 
4973 // Wrap-adjust relative references of a RangeName to current position,
4974 // don't call for other token arrays!
4975 void ScCompiler::MoveRelWrap( const ScTokenArray& rArr, const ScDocument* pDoc, const ScAddress& rPos,
4976                               SCCOL nMaxCol, SCROW nMaxRow )
4977 {
4978     for ( auto t: rArr.References() )
4979     {
4980         if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4981             ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
4982         else
4983             ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
4984     }
4985 }
4986 
4987 bool ScCompiler::IsCharFlagAllConventions(
4988     OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags )
4989 {
4990     sal_Unicode c = rStr[ nPos ];
4991     sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
4992     if (c < 128)
4993     {
4994         for ( int nConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
4995                 ++nConv < formula::FormulaGrammar::CONV_LAST; )
4996         {
4997             if (pConventions[nConv] &&
4998                     ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
4999                 return false;
5000             // convention not known => assume valid
5001         }
5002         return true;
5003     }
5004     else
5005         return ScGlobal::pCharClass->isLetterNumeric( rStr, nPos );
5006 }
5007 
5008 void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
5009 {
5010     const FormulaToken* t = pTokenP;
5011     sal_uInt16 nFileId = t->GetIndex();
5012     ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
5013     const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
5014     if (!pFileName)
5015         return;
5016 
5017     switch (t->GetType())
5018     {
5019         case svExternalName:
5020             rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString()));
5021         break;
5022         case svExternalSingleRef:
5023             pConv->makeExternalRefStr(pDoc->GetSheetLimits(),
5024                    rBuffer, GetPos(), nFileId, *pFileName, t->GetString().getString(),
5025                    *t->GetSingleRef());
5026         break;
5027         case svExternalDoubleRef:
5028         {
5029             vector<OUString> aTabNames;
5030             pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
5031             // No sheet names is a valid case if external sheets were not
5032             // cached in this document and external document is not reachable,
5033             // else not and worth to be investigated.
5034             SAL_WARN_IF( aTabNames.empty(), "sc.core", "wrecked cache of external document? '" <<
5035                     *pFileName << "' '" << t->GetString().getString() << "'");
5036 
5037             pConv->makeExternalRefStr(
5038                 pDoc->GetSheetLimits(), rBuffer, GetPos(), nFileId, *pFileName, aTabNames, t->GetString().getString(),
5039                 *t->GetDoubleRef());
5040         }
5041         break;
5042         default:
5043             // warning, not error, otherwise we may end up with a never
5044             // ending message box loop if this was the cursor cell to be redrawn.
5045             OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
5046     }
5047 }
5048 
5049 void ScCompiler::CreateStringFromMatrix( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
5050 {
5051     const ScMatrix* pMatrix = pTokenP->GetMatrix();
5052     SCSIZE nC, nMaxC, nR, nMaxR;
5053 
5054     pMatrix->GetDimensions( nMaxC, nMaxR);
5055 
5056     rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
5057     for( nR = 0 ; nR < nMaxR ; nR++)
5058     {
5059         if( nR > 0)
5060         {
5061             rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
5062         }
5063 
5064         for( nC = 0 ; nC < nMaxC ; nC++)
5065         {
5066             if( nC > 0)
5067             {
5068                 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
5069             }
5070 
5071             if( pMatrix->IsValue( nC, nR ) )
5072             {
5073                 if (pMatrix->IsBoolean(nC, nR))
5074                     AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
5075                 else
5076                 {
5077                     FormulaError nErr = pMatrix->GetError(nC, nR);
5078                     if (nErr != FormulaError::NONE)
5079                         rBuffer.append(ScGlobal::GetErrorString(nErr));
5080                     else
5081                         AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
5082                 }
5083             }
5084             else if( pMatrix->IsEmpty( nC, nR ) )
5085                 ;
5086             else if( pMatrix->IsStringOrEmpty( nC, nR ) )
5087                 AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() );
5088         }
5089     }
5090     rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
5091 }
5092 
5093 namespace {
5094 void escapeTableRefColumnSpecifier( OUString& rStr )
5095 {
5096     const sal_Int32 n = rStr.getLength();
5097     OUStringBuffer aBuf( n * 2 );
5098     const sal_Unicode* p = rStr.getStr();
5099     const sal_Unicode* const pStop = p + n;
5100     for ( ; p < pStop; ++p)
5101     {
5102         const sal_Unicode c = *p;
5103         switch (c)
5104         {
5105             case '\'':
5106             case '[':
5107             case '#':
5108             case ']':
5109                 aBuf.append( '\'' );
5110                 break;
5111             default:
5112                 ;   // nothing
5113         }
5114         aBuf.append( c );
5115     }
5116     rStr = aBuf.makeStringAndClear();
5117 }
5118 }
5119 
5120 void ScCompiler::CreateStringFromSingleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5121 {
5122     const FormulaToken* p;
5123     OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
5124     const OpCode eOp = _pTokenP->GetOpCode();
5125     const ScSingleRefData& rRef = *_pTokenP->GetSingleRef();
5126     ScComplexRefData aRef;
5127     aRef.Ref1 = aRef.Ref2 = rRef;
5128     if ( eOp == ocColRowName )
5129     {
5130         ScAddress aAbs = rRef.toAbs(pDoc, aPos);
5131         if (pDoc->HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab()))
5132         {
5133             OUString aStr = pDoc->GetString(aAbs, mpInterpreterContext);
5134             EnQuote( aStr );
5135             rBuffer.append(aStr);
5136         }
5137         else
5138         {
5139             rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
5140             pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
5141                               GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
5142         }
5143     }
5144     else if (pArr && (p = maArrIterator.PeekPrevNoSpaces()) && p->GetOpCode() == ocTableRefOpen)
5145     {
5146         OUString aStr;
5147         ScAddress aAbs = rRef.toAbs(pDoc, aPos);
5148         const ScDBData* pData = pDoc->GetDBAtCursor( aAbs.Col(), aAbs.Row(), aAbs.Tab(), ScDBDataPortion::AREA);
5149         SAL_WARN_IF( !pData, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " <<
5150                 aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc));
5151         if (pData)
5152             aStr = pData->GetTableColumnName( aAbs.Col());
5153         if (aStr.isEmpty())
5154         {
5155             if (pData && pData->HasHeader())
5156             {
5157                 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " <<
5158                         aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc));
5159                 aStr = pDoc->GetString(aAbs, mpInterpreterContext);
5160             }
5161             else
5162             {
5163                 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " <<
5164                         aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc));
5165                 aStr = aErrRef;
5166             }
5167         }
5168         escapeTableRefColumnSpecifier( aStr);
5169         rBuffer.append(aStr);
5170     }
5171     else
5172         pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
5173                           GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
5174 }
5175 
5176 void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5177 {
5178     OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
5179     pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef, GetSetupTabNames(),
5180                       *_pTokenP->GetDoubleRef(), false, (pArr && pArr->IsFromRangeName()));
5181 }
5182 
5183 void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5184 {
5185     const OpCode eOp = _pTokenP->GetOpCode();
5186     OUStringBuffer aBuffer;
5187     switch ( eOp )
5188     {
5189         case ocName:
5190         {
5191             const ScRangeData* pData = GetRangeData( *_pTokenP);
5192             if (pData)
5193             {
5194                 SCTAB nTab = _pTokenP->GetSheet();
5195                 if (nTab >= 0 && nTab != aPos.Tab())
5196                 {
5197                     // Sheet-local on other sheet.
5198                     OUString aName;
5199                     if (pDoc->GetName( nTab, aName))
5200                     {
5201                         ScCompiler::CheckTabQuotes( aName, pConv->meConv);
5202                         aBuffer.append( aName);
5203                     }
5204                     else
5205                         aBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
5206                     aBuffer.append( pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
5207                 }
5208                 aBuffer.append(pData->GetName());
5209             }
5210         }
5211         break;
5212         case ocDBArea:
5213         {
5214             const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
5215             if (pDBData)
5216                 aBuffer.append(pDBData->GetName());
5217         }
5218         break;
5219         case ocTableRef:
5220         {
5221             if (NeedsTableRefTransformation())
5222             {
5223                 // Write the resulting reference if TableRef is not supported.
5224                 const ScTableRefToken* pTR = dynamic_cast<const ScTableRefToken*>(_pTokenP);
5225                 if (!pTR)
5226                     AppendErrorConstant( aBuffer, FormulaError::NoCode);
5227                 else
5228                 {
5229                     const FormulaToken* pRef = pTR->GetAreaRefRPN();
5230                     if (!pRef)
5231                         AppendErrorConstant( aBuffer, FormulaError::NoCode);
5232                     else
5233                     {
5234                         switch (pRef->GetType())
5235                         {
5236                             case svSingleRef:
5237                                 CreateStringFromSingleRef( aBuffer, pRef);
5238                                 break;
5239                             case svDoubleRef:
5240                                 CreateStringFromDoubleRef( aBuffer, pRef);
5241                                 break;
5242                             case svError:
5243                                 AppendErrorConstant( aBuffer, pRef->GetError());
5244                                 break;
5245                             default:
5246                                 AppendErrorConstant( aBuffer, FormulaError::NoCode);
5247                         }
5248                     }
5249                 }
5250             }
5251             else
5252             {
5253                 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
5254                 if (pDBData)
5255                     aBuffer.append(pDBData->GetName());
5256             }
5257         }
5258         break;
5259         default:
5260             ;   // nothing
5261     }
5262     if ( !aBuffer.isEmpty() )
5263         rBuffer.append(aBuffer.makeStringAndClear());
5264     else
5265         rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
5266 }
5267 
5268 void ScCompiler::LocalizeString( OUString& rName ) const
5269 {
5270     ScGlobal::GetAddInCollection()->LocalizeString( rName );
5271 }
5272 
5273 // Put quotes around string if non-alphanumeric characters are contained,
5274 // quote characters contained within are escaped by '\\'.
5275 bool ScCompiler::EnQuote( OUString& rStr )
5276 {
5277     sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.getLength() );
5278     if ( !CharClass::isNumericType( nType )
5279             && CharClass::isAlphaNumericType( nType ) )
5280         return false;
5281 
5282     sal_Int32 nPos = 0;
5283     while ( (nPos = rStr.indexOf( '\'', nPos)) != -1 )
5284     {
5285         rStr = rStr.replaceAt( nPos, 0, "\\" );
5286         nPos += 2;
5287     }
5288     rStr = "'" + rStr + "'";
5289     return true;
5290 }
5291 
5292 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
5293 {
5294     return pConv->getSpecialSymbol(eType);
5295 }
5296 
5297 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 )
5298 {
5299     return extendRangeReference( pDoc->GetSheetLimits(), rTok1, rTok2, aPos, true/*bReuseDoubleRef*/ );
5300 }
5301 
5302 void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
5303 {
5304     // All known AddIn functions.
5305     sheet::FormulaOpCodeMapEntry aEntry;
5306     aEntry.Token.OpCode = ocExternal;
5307 
5308     ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
5309     const long nCount = pColl->GetFuncCount();
5310     for (long i=0; i < nCount; ++i)
5311     {
5312         const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
5313         if (pFuncData)
5314         {
5315             if ( _bIsEnglish )
5316             {
5317                 OUString aName;
5318                 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
5319                     aEntry.Name = aName;
5320                 else
5321                     aEntry.Name = pFuncData->GetUpperName();
5322             }
5323             else
5324                 aEntry.Name = pFuncData->GetUpperLocal();
5325             aEntry.Token.Data <<= pFuncData->GetOriginalName();
5326             _rVec.push_back( aEntry);
5327         }
5328     }
5329     // FIXME: what about those old non-UNO AddIns?
5330 }
5331 
5332 bool ScCompiler::HandleColRowName()
5333 {
5334     ScSingleRefData& rRef = *mpToken->GetSingleRef();
5335     const ScAddress aAbs = rRef.toAbs(pDoc, aPos);
5336     if (!pDoc->ValidAddress(aAbs))
5337     {
5338         SetError( FormulaError::NoRef );
5339         return true;
5340     }
5341     SCCOL nCol = aAbs.Col();
5342     SCROW nRow = aAbs.Row();
5343     SCTAB nTab = aAbs.Tab();
5344     bool bColName = rRef.IsColRel();
5345     SCCOL nMyCol = aPos.Col();
5346     SCROW nMyRow = aPos.Row();
5347     bool bInList = false;
5348     bool bValidName = false;
5349     ScRangePairList* pRL = (bColName ?
5350         pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
5351     ScRange aRange;
5352     for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5353     {
5354         const ScRangePair & rR = (*pRL)[i];
5355         if ( rR.GetRange(0).In( aAbs ) )
5356         {
5357             bInList = bValidName = true;
5358             aRange = rR.GetRange(1);
5359             if ( bColName )
5360             {
5361                 aRange.aStart.SetCol( nCol );
5362                 aRange.aEnd.SetCol( nCol );
5363             }
5364             else
5365             {
5366                 aRange.aStart.SetRow( nRow );
5367                 aRange.aEnd.SetRow( nRow );
5368             }
5369             break;  // for
5370         }
5371     }
5372     if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
5373     {   // automagically or created by copying and NamePos isn't in list
5374         ScRefCellValue aCell(*pDoc, aAbs);
5375         bool bString = aCell.hasString();
5376         if (!bString && aCell.isEmpty())
5377             bString = true;     // empty cell is ok
5378         if ( bString )
5379         {   // corresponds with ScInterpreter::ScColRowNameAuto()
5380             bValidName = true;
5381             if ( bColName )
5382             {   // ColName
5383                 SCROW nStartRow = nRow + 1;
5384                 if ( nStartRow > pDoc->MaxRow() )
5385                     nStartRow = pDoc->MaxRow();
5386                 SCROW nMaxRow = pDoc->MaxRow();
5387                 if ( nMyCol == nCol )
5388                 {   // formula cell in same column
5389                     if ( nMyRow == nStartRow )
5390                     {   // take remainder under name cell
5391                         nStartRow++;
5392                         if ( nStartRow > pDoc->MaxRow() )
5393                             nStartRow = pDoc->MaxRow();
5394                     }
5395                     else if ( nMyRow > nStartRow )
5396                     {   // from name cell down to formula cell
5397                         nMaxRow = nMyRow - 1;
5398                     }
5399                 }
5400                 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5401                 {   // next defined ColNameRange below limits row
5402                     const ScRangePair & rR = (*pRL)[i];
5403                     const ScRange& rRange = rR.GetRange(1);
5404                     if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
5405                     {   // identical column range
5406                         SCROW nTmp = rRange.aStart.Row();
5407                         if ( nStartRow < nTmp && nTmp <= nMaxRow )
5408                             nMaxRow = nTmp - 1;
5409                     }
5410                 }
5411                 aRange.aStart.Set( nCol, nStartRow, nTab );
5412                 aRange.aEnd.Set( nCol, nMaxRow, nTab );
5413             }
5414             else
5415             {   // RowName
5416                 SCCOL nStartCol = nCol + 1;
5417                 if ( nStartCol > pDoc->MaxCol() )
5418                     nStartCol = pDoc->MaxCol();
5419                 SCCOL nMaxCol = pDoc->MaxCol();
5420                 if ( nMyRow == nRow )
5421                 {   // formula cell in same row
5422                     if ( nMyCol == nStartCol )
5423                     {   // take remainder right from name cell
5424                         nStartCol++;
5425                         if ( nStartCol > pDoc->MaxCol() )
5426                             nStartCol = pDoc->MaxCol();
5427                     }
5428                     else if ( nMyCol > nStartCol )
5429                     {   // from name cell right to formula cell
5430                         nMaxCol = nMyCol - 1;
5431                     }
5432                 }
5433                 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5434                 {   // next defined RowNameRange to the right limits column
5435                     const ScRangePair & rR = (*pRL)[i];
5436                     const ScRange& rRange = rR.GetRange(1);
5437                     if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
5438                     {   // identical row range
5439                         SCCOL nTmp = rRange.aStart.Col();
5440                         if ( nStartCol < nTmp && nTmp <= nMaxCol )
5441                             nMaxCol = nTmp - 1;
5442                     }
5443                 }
5444                 aRange.aStart.Set( nStartCol, nRow, nTab );
5445                 aRange.aEnd.Set( nMaxCol, nRow, nTab );
5446             }
5447         }
5448     }
5449     if ( bValidName )
5450     {
5451         // And now the magic to distinguish between a range and a single
5452         // cell thereof, which is picked position-dependent of the formula
5453         // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5454         // SingleRef matching the column/row of the formula cell is
5455         // generated. A ocColRowName or ocIntersect as a neighbor results
5456         // in a range. Special case: if label is valid for a single cell, a
5457         // position independent SingleRef is generated.
5458         bool bSingle = (aRange.aStart == aRange.aEnd);
5459         bool bFound;
5460         if ( bSingle )
5461             bFound = true;
5462         else
5463         {
5464             FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces();
5465             FormulaToken* p2 = maArrIterator.PeekNextNoSpaces();
5466             // begin/end of a formula => single
5467             OpCode eOp1 = p1 ? p1->GetOpCode() : ocAdd;
5468             OpCode eOp2 = p2 ? p2->GetOpCode() : ocAdd;
5469             if ( eOp1 != ocColRowName && eOp1 != ocIntersect
5470                 && eOp2 != ocColRowName && eOp2 != ocIntersect )
5471             {
5472                 if (    (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
5473                         (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
5474                     bSingle = true;
5475             }
5476             if ( bSingle )
5477             {   // column and/or row must match range
5478                 if ( bColName )
5479                 {
5480                     bFound = (aRange.aStart.Row() <= nMyRow
5481                         && nMyRow <= aRange.aEnd.Row());
5482                     if ( bFound )
5483                         aRange.aStart.SetRow( nMyRow );
5484                 }
5485                 else
5486                 {
5487                     bFound = (aRange.aStart.Col() <= nMyCol
5488                         && nMyCol <= aRange.aEnd.Col());
5489                     if ( bFound )
5490                         aRange.aStart.SetCol( nMyCol );
5491                 }
5492             }
5493             else
5494                 bFound = true;
5495         }
5496         if ( !bFound )
5497             SetError(FormulaError::NoRef);
5498         else if (mbJumpCommandReorder)
5499         {
5500             ScTokenArray* pNew = new ScTokenArray(pDoc);
5501             if ( bSingle )
5502             {
5503                 ScSingleRefData aRefData;
5504                 aRefData.InitAddress( aRange.aStart );
5505                 if ( bColName )
5506                     aRefData.SetColRel( true );
5507                 else
5508                     aRefData.SetRowRel( true );
5509                 aRefData.SetAddress(pDoc->GetSheetLimits(), aRange.aStart, aPos);
5510                 pNew->AddSingleReference( aRefData );
5511             }
5512             else
5513             {
5514                 ScComplexRefData aRefData;
5515                 aRefData.InitRange( aRange );
5516                 if ( bColName )
5517                 {
5518                     aRefData.Ref1.SetColRel( true );
5519                     aRefData.Ref2.SetColRel( true );
5520                 }
5521                 else
5522                 {
5523                     aRefData.Ref1.SetRowRel( true );
5524                     aRefData.Ref2.SetRowRel( true );
5525                 }
5526                 aRefData.SetRange(pDoc->GetSheetLimits(), aRange, aPos);
5527                 if ( bInList )
5528                     pNew->AddDoubleReference( aRefData );
5529                 else
5530                 {   // automagically
5531                     pNew->Add( new ScDoubleRefToken( pDoc->GetSheetLimits(), aRefData, ocColRowNameAuto ) );
5532                 }
5533             }
5534             PushTokenArray( pNew, true );
5535             return GetToken();
5536         }
5537     }
5538     else
5539         SetError(FormulaError::NoName);
5540     return true;
5541 }
5542 
5543 bool ScCompiler::HandleDbData()
5544 {
5545     ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex());
5546     if ( !pDBData )
5547         SetError(FormulaError::NoName);
5548     else if (mbJumpCommandReorder)
5549     {
5550         ScComplexRefData aRefData;
5551         aRefData.InitFlags();
5552         ScRange aRange;
5553         pDBData->GetArea(aRange);
5554         aRange.aEnd.SetTab(aRange.aStart.Tab());
5555         aRefData.SetRange(pDoc->GetSheetLimits(), aRange, aPos);
5556         ScTokenArray* pNew = new ScTokenArray(pDoc);
5557         pNew->AddDoubleReference( aRefData );
5558         PushTokenArray( pNew, true );
5559         return GetToken();
5560     }
5561     return true;
5562 }
5563 
5564 bool ScCompiler::GetTokenIfOpCode( OpCode eOp )
5565 {
5566     const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces();
5567     if (p && p->GetOpCode() == eOp)
5568         return GetToken();
5569     return false;
5570 }
5571 
5572 
5573 /* Documentation on MS-Excel Table structured references:
5574  * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
5575  * * as of Excel 2013
5576  * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
5577  * * look for structure-reference
5578  */
5579 
5580 bool ScCompiler::HandleTableRef()
5581 {
5582     ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(mpToken.get());
5583     if (!pTR)
5584     {
5585         SetError(FormulaError::UnknownToken);
5586         return true;
5587     }
5588 
5589     ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( pTR->GetIndex());
5590     if ( !pDBData )
5591         SetError(FormulaError::NoName);
5592     else if (mbJumpCommandReorder)
5593     {
5594         ScRange aDBRange;
5595         pDBData->GetArea(aDBRange);
5596         aDBRange.aEnd.SetTab(aDBRange.aStart.Tab());
5597         ScRange aRange( aDBRange);
5598         FormulaError nError = FormulaError::NONE;
5599         bool bForwardToClose = false;
5600         ScTableRefToken::Item eItem = pTR->GetItem();
5601         switch (eItem)
5602         {
5603             case ScTableRefToken::TABLE:
5604                 {
5605                     // The table name without items references the table data,
5606                     // without headers or totals.
5607                     if (pDBData->HasHeader())
5608                         aRange.aStart.IncRow();
5609                     if (pDBData->HasTotals())
5610                         aRange.aEnd.IncRow(-1);
5611                     if (aRange.aEnd.Row() < aRange.aStart.Row())
5612                         nError = FormulaError::NoRef;
5613                     bForwardToClose = true;
5614                 }
5615                 break;
5616             case ScTableRefToken::ALL:
5617                 {
5618                     bForwardToClose = true;
5619                 }
5620                 break;
5621             case ScTableRefToken::HEADERS:
5622                 {
5623                     if (pDBData->HasHeader())
5624                         aRange.aEnd.SetRow( aRange.aStart.Row());
5625                     else
5626                         nError = FormulaError::NoRef;
5627                     bForwardToClose = true;
5628                 }
5629                 break;
5630             case ScTableRefToken::DATA:
5631                 {
5632                     if (pDBData->HasHeader())
5633                         aRange.aStart.IncRow();
5634                 }
5635                 [[fallthrough]];
5636             case ScTableRefToken::HEADERS_DATA:
5637                 {
5638                     if (pDBData->HasTotals())
5639                         aRange.aEnd.IncRow(-1);
5640                     if (aRange.aEnd.Row() < aRange.aStart.Row())
5641                         nError = FormulaError::NoRef;
5642                     bForwardToClose = true;
5643                 }
5644                 break;
5645             case ScTableRefToken::TOTALS:
5646                 {
5647                     if (pDBData->HasTotals())
5648                         aRange.aStart.SetRow( aRange.aEnd.Row());
5649                     else
5650                         nError = FormulaError::NoRef;
5651                     bForwardToClose = true;
5652                 }
5653                 break;
5654             case ScTableRefToken::DATA_TOTALS:
5655                 {
5656                     if (pDBData->HasHeader())
5657                         aRange.aStart.IncRow();
5658                     if (aRange.aEnd.Row() < aRange.aStart.Row())
5659                         nError = FormulaError::NoRef;
5660                     bForwardToClose = true;
5661                 }
5662                 break;
5663             case ScTableRefToken::THIS_ROW:
5664                 {
5665                     if (aRange.aStart.Row() <= aPos.Row() && aPos.Row() <= aRange.aEnd.Row())
5666                     {
5667                         aRange.aStart.SetRow( aPos.Row());
5668                         aRange.aEnd.SetRow( aPos.Row());
5669                     }
5670                     else
5671                     {
5672                         nError = FormulaError::NoValue;
5673                         // For *some* relative row reference in named
5674                         // expressions' thisrow special handling below.
5675                         aRange.aEnd.SetRow( aRange.aStart.Row());
5676                     }
5677                     bForwardToClose = true;
5678                 }
5679                 break;
5680         }
5681         bool bColumnRange = false;
5682         bool bCol1Rel = false;
5683         bool bCol1RelName = false;
5684         int nLevel = 0;
5685         if (bForwardToClose && GetTokenIfOpCode( ocTableRefOpen))
5686         {
5687             ++nLevel;
5688             enum
5689             {
5690                 sOpen,
5691                 sItem,
5692                 sClose,
5693                 sSep,
5694                 sLast,
5695                 sStop
5696             } eState = sOpen;
5697             do
5698             {
5699                 const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces();
5700                 if (!p)
5701                     eState = sStop;
5702                 else
5703                 {
5704                     switch (p->GetOpCode())
5705                     {
5706                         case ocTableRefOpen:
5707                             eState = ((eState == sOpen || eState == sSep) ? sOpen : sStop);
5708                             if (++nLevel > 2)
5709                             {
5710                                 SetError( FormulaError::Pair);
5711                                 eState = sStop;
5712                             }
5713                             break;
5714                         case ocTableRefItemAll:
5715                         case ocTableRefItemHeaders:
5716                         case ocTableRefItemData:
5717                         case ocTableRefItemTotals:
5718                         case ocTableRefItemThisRow:
5719                             eState = ((eState == sOpen) ? sItem : sStop);
5720                             break;
5721                         case ocTableRefClose:
5722                             eState = ((eState == sItem || eState == sClose) ? sClose : sStop);
5723                             if (eState != sStop && --nLevel == 0)
5724                                 eState = sLast;
5725                             break;
5726                         case ocSep:
5727                             eState = ((eState == sClose) ? sSep : sStop);
5728                             break;
5729                         case ocPush:
5730                             if (eState == sOpen && p->GetType() == svSingleRef)
5731                             {
5732                                 bColumnRange = true;
5733                                 bCol1Rel = p->GetSingleRef()->IsColRel();
5734                                 bCol1RelName = p->GetSingleRef()->IsRelName();
5735                                 eState = sLast;
5736                             }
5737                             else
5738                             {
5739                                 eState = sStop;
5740                             }
5741                             break;
5742                         case ocBad:
5743                             eState = sLast;
5744                             if (nError == FormulaError::NONE)
5745                                 nError = FormulaError::NoName;
5746                             break;
5747                         default:
5748                             eState = sStop;
5749                     }
5750                     if (eState != sStop)
5751                         GetToken();
5752                     if (eState == sLast)
5753                         eState = sStop;
5754                 }
5755             } while (eState != sStop);
5756         }
5757         ScTokenArray* pNew = new ScTokenArray(pDoc);
5758         if (nError == FormulaError::NONE || nError == FormulaError::NoValue)
5759         {
5760             bool bCol2Rel = false;
5761             bool bCol2RelName = false;
5762             // The FormulaError::NoValue case generates a thisrow reference that can be
5763             // used to save named expressions in A1 syntax notation.
5764             if (bColumnRange)
5765             {
5766                 // Limit range to specified columns.
5767                 ScRange aColRange( ScAddress::INITIALIZE_INVALID );
5768                 switch (mpToken->GetType())
5769                 {
5770                     case svSingleRef:
5771                         {
5772                             aColRange.aStart = aColRange.aEnd = mpToken->GetSingleRef()->toAbs(pDoc, aPos);
5773                             if (    GetTokenIfOpCode( ocTableRefClose) && (nLevel--) &&
5774                                     GetTokenIfOpCode( ocRange) &&
5775                                     GetTokenIfOpCode( ocTableRefOpen) && (++nLevel) &&
5776                                     GetTokenIfOpCode( ocPush))
5777                             {
5778                                 if (mpToken->GetType() != svSingleRef)
5779                                     aColRange = ScRange( ScAddress::INITIALIZE_INVALID);
5780                                 else
5781                                 {
5782                                     aColRange.aEnd = mpToken->GetSingleRef()->toAbs(pDoc, aPos);
5783                                     aColRange.PutInOrder();
5784                                     bCol2Rel = mpToken->GetSingleRef()->IsColRel();
5785                                     bCol2RelName = mpToken->GetSingleRef()->IsRelName();
5786                                 }
5787                             }
5788                         }
5789                         break;
5790                     default:
5791                         ;   // nothing
5792                 }
5793                 // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses
5794                 if (aColRange.aStart.Row() != aDBRange.aStart.Row() || aColRange.aEnd.Row() != aDBRange.aStart.Row())
5795                     aRange = ScRange( ScAddress::INITIALIZE_INVALID);
5796                 else
5797                 {
5798                     aColRange.aEnd.SetRow( aRange.aEnd.Row());
5799                     aRange = aRange.Intersection( aColRange);
5800                 }
5801             }
5802             if (aRange.IsValid())
5803             {
5804                 if (aRange.aStart == aRange.aEnd)
5805                 {
5806                     ScSingleRefData aRefData;
5807                     aRefData.InitFlags();
5808                     aRefData.SetColRel( bCol1Rel);
5809                     if (eItem == ScTableRefToken::THIS_ROW)
5810                     {
5811                         aRefData.SetRowRel( true);
5812                         if (!bCol1RelName)
5813                             bCol1RelName = pArr->IsFromRangeName();
5814                     }
5815                     aRefData.SetRelName( bCol1RelName);
5816                     aRefData.SetFlag3D( true);
5817                     if (nError != FormulaError::NONE)
5818                     {
5819                         aRefData.SetAddress( pDoc->GetSheetLimits(), aRange.aStart, aRange.aStart);
5820                         pTR->SetAreaRefRPN( new ScSingleRefToken(pDoc->GetSheetLimits(), aRefData));   // set reference at TableRef
5821                         pNew->Add( new FormulaErrorToken( nError));             // set error in RPN
5822                     }
5823                     else
5824                     {
5825                         aRefData.SetAddress( pDoc->GetSheetLimits(), aRange.aStart, aPos);
5826                         pTR->SetAreaRefRPN( pNew->AddSingleReference( aRefData));
5827                     }
5828                 }
5829                 else
5830                 {
5831                     ScComplexRefData aRefData;
5832                     aRefData.InitFlags();
5833                     aRefData.Ref1.SetColRel( bCol1Rel);
5834                     aRefData.Ref2.SetColRel( bCol2Rel);
5835                     bool bRelName = bCol1RelName || bCol2RelName;
5836                     if (eItem == ScTableRefToken::THIS_ROW)
5837                     {
5838                         aRefData.Ref1.SetRowRel( true);
5839                         aRefData.Ref2.SetRowRel( true);
5840                         if (!bRelName)
5841                             bRelName = pArr->IsFromRangeName();
5842                     }
5843                     aRefData.Ref1.SetRelName( bRelName);
5844                     aRefData.Ref2.SetRelName( bRelName);
5845                     aRefData.Ref1.SetFlag3D( true);
5846                     if (nError != FormulaError::NONE)
5847                     {
5848                         aRefData.SetRange( pDoc->GetSheetLimits(), aRange, aRange.aStart);
5849                         pTR->SetAreaRefRPN( new ScDoubleRefToken(pDoc->GetSheetLimits(), aRefData));   // set reference at TableRef
5850                         pNew->Add( new FormulaErrorToken( nError));             // set error in RPN
5851                     }
5852                     else
5853                     {
5854                         aRefData.SetRange( pDoc->GetSheetLimits(), aRange, aPos);
5855                         pTR->SetAreaRefRPN( pNew->AddDoubleReference( aRefData));
5856                     }
5857                 }
5858             }
5859             else
5860             {
5861                 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( FormulaError::NoRef)));
5862             }
5863         }
5864         else
5865         {
5866             pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( nError)));
5867         }
5868         while (nLevel-- > 0)
5869         {
5870             if (!GetTokenIfOpCode( ocTableRefClose))
5871                 SetError( FormulaError::Pair);
5872         }
5873         PushTokenArray( pNew, true );
5874         return GetToken();
5875     }
5876     return true;
5877 }
5878 
5879 formula::ParamClass ScCompiler::GetForceArrayParameter( const formula::FormulaToken* pToken, sal_uInt16 nParam ) const
5880 {
5881     return ScParameterClassification::GetParameterType( pToken, nParam);
5882 }
5883 
5884 bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken* token, int parameter)
5885 {
5886     formula::ParamClass param = ScParameterClassification::GetParameterType( token, parameter );
5887     return param == Value || param == Array;
5888 }
5889 
5890 bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* token) const
5891 {
5892     if (mbMatrixFlag)
5893         return true;
5894     formula::ParamClass paramClass = token->GetInForceArray();
5895     if (paramClass == formula::ForceArray
5896         || paramClass == formula::ReferenceOrForceArray
5897         || paramClass == formula::SuppressedReferenceOrForceArray
5898         || paramClass == formula::ReferenceOrRefArray)
5899     {
5900         return true;
5901     }
5902     formula::ParamClass returnType = ScParameterClassification::GetParameterType( token, SAL_MAX_UINT16 );
5903     return returnType == formula::Reference;
5904 }
5905 
5906 void ScCompiler::HandleIIOpCode(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
5907 {
5908     if (!mbComputeII)
5909         return;
5910 #ifdef DBG_UTIL
5911     if(!HandleIIOpCodeInternal(token, pppToken, nNumParams))
5912         mUnhandledPossibleImplicitIntersectionsOpCodes.insert(token->GetOpCode());
5913 #else
5914     HandleIIOpCodeInternal(token, pppToken, nNumParams);
5915 #endif
5916 }
5917 
5918 // return true if opcode is handled
5919 bool ScCompiler::HandleIIOpCodeInternal(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
5920 {
5921     if (nNumParams > 0 && *pppToken[0] == nullptr)
5922         return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray())
5923 
5924     const OpCode nOpCode = token->GetOpCode();
5925 
5926     if (nOpCode == ocPush)
5927     {
5928         if(token->GetType() == svDoubleRef)
5929             mUnhandledPossibleImplicitIntersections.insert( token );
5930         return true;
5931     }
5932     else if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
5933     {
5934         if (nNumParams != 3)
5935             return false;
5936 
5937         if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
5938             return false;
5939 
5940         if ((*pppToken[0])->GetType() != svDoubleRef)
5941             return false;
5942 
5943         const StackVar eSumRangeType = (*pppToken[2])->GetType();
5944 
5945         if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
5946             return false;
5947 
5948         const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
5949 
5950         ScComplexRefData aSumRange;
5951         if (eSumRangeType == svSingleRef)
5952         {
5953             aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
5954             aSumRange.Ref2 = aSumRange.Ref1;
5955         }
5956         else
5957             aSumRange = *(*pppToken[2])->GetDoubleRef();
5958 
5959         CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
5960         // TODO mark parameters as handled
5961         return true;
5962     }
5963     else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
5964     {
5965         if (nNumParams != 1)
5966             return false;
5967 
5968         if( !ParameterMayBeImplicitIntersection( token, 0 ))
5969             return false;
5970         if (SkipImplicitIntersectionOptimization(token))
5971             return false;
5972 
5973         if ((*pppToken[0])->GetType() != svDoubleRef)
5974             return false;
5975 
5976         mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
5977         return true;
5978     }
5979     else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP)
5980               || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == ocRoundDown)
5981     {
5982         if (nNumParams != 2)
5983             return false;
5984 
5985         if( !ParameterMayBeImplicitIntersection( token, 0 ) || !ParameterMayBeImplicitIntersection( token, 1 ))
5986             return false;
5987         if (SkipImplicitIntersectionOptimization(token))
5988             return false;
5989 
5990         // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
5991         if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix)
5992             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
5993         if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix)
5994             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[1], token );
5995         return true;
5996     }
5997     else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < SC_OPCODE_STOP_UN_OP)
5998               || nOpCode == ocPercentSign)
5999     {
6000         if (nNumParams != 1)
6001             return false;
6002 
6003         if( !ParameterMayBeImplicitIntersection( token, 0 ))
6004             return false;
6005         if (SkipImplicitIntersectionOptimization(token))
6006             return false;
6007 
6008         if ((*pppToken[0])->GetType() == svDoubleRef)
6009             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6010         return true;
6011     }
6012     else if (nOpCode == ocVLookup)
6013     {
6014         if (nNumParams != 3 && nNumParams != 4)
6015             return false;
6016 
6017         if (SkipImplicitIntersectionOptimization(token))
6018             return false;
6019 
6020         assert( ParameterMayBeImplicitIntersection( token, 0 ));
6021         assert( !ParameterMayBeImplicitIntersection( token, 1 ));
6022         assert( ParameterMayBeImplicitIntersection( token, 2 ));
6023         assert( ParameterMayBeImplicitIntersection( token, 3 ));
6024         if ((*pppToken[2])->GetType() == svDoubleRef)
6025             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[2], token );
6026         if ((*pppToken[0])->GetType() == svDoubleRef)
6027             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6028         if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef)
6029             mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[3], token );
6030         // a range for the second parameters is not an implicit intersection
6031         mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] );
6032         return true;
6033     }
6034     else
6035     {
6036         bool possibleII = false;
6037         for( int i = 0; i < nNumParams; ++i )
6038         {
6039             if( ParameterMayBeImplicitIntersection( token, i ))
6040             {
6041                 possibleII = true;
6042                 break;
6043             }
6044         }
6045         if( !possibleII )
6046         {
6047             // all parameters have been handled, they are not implicit intersections
6048             for( int i = 0; i < nNumParams; ++i )
6049                 mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] );
6050             return true;
6051         }
6052     }
6053 
6054     return false;
6055 }
6056 
6057 void ScCompiler::PostProcessCode()
6058 {
6059     for( const PendingImplicitIntersectionOptimization& item : mPendingImplicitIntersectionOptimizations )
6060     {
6061         if( *item.parameterLocation != item.parameter ) // the parameter has been changed somehow
6062             continue;
6063         if( item.parameterLocation >= pCode ) // the location is not inside the code (pCode points after the end)
6064             continue;
6065         // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
6066         if( item.operation->IsInForceArray())
6067             continue;
6068         ReplaceDoubleRefII( item.parameterLocation );
6069     }
6070     mPendingImplicitIntersectionOptimizations.clear();
6071 }
6072 
6073 void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok)
6074 {
6075     const ScComplexRefData* pRange = (*ppDoubleRefTok)->GetDoubleRef();
6076     if (!pRange)
6077         return;
6078 
6079     const ScComplexRefData& rRange = *pRange;
6080 
6081     // Can't do optimization reliably in this case (when row references are absolute).
6082     // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
6083     // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
6084     // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
6085     // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
6086     // fix this, but that is unknown at this stage, so skip such cases.
6087     if (!rRange.Ref1.IsRowRel() && !rRange.Ref2.IsRowRel())
6088         return;
6089 
6090     ScRange aAbsRange = rRange.toAbs(pDoc, aPos);
6091     if (aAbsRange.aStart == aAbsRange.aEnd)
6092         return; // Nothing to do (trivial case).
6093 
6094     ScAddress aAddr;
6095 
6096     if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos))
6097         return;
6098 
6099     ScSingleRefData aSingleRef;
6100     aSingleRef.InitFlags();
6101     aSingleRef.SetColRel(rRange.Ref1.IsColRel());
6102     aSingleRef.SetRowRel(true);
6103     aSingleRef.SetTabRel(rRange.Ref1.IsTabRel());
6104     aSingleRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos);
6105 
6106     // Replace the original doubleref token with computed singleref token
6107     FormulaToken* pNewSingleRefTok = new ScSingleRefToken(pDoc->GetSheetLimits(), aSingleRef);
6108     (*ppDoubleRefTok)->DecRef();
6109     *ppDoubleRefTok = pNewSingleRefTok;
6110     pNewSingleRefTok->IncRef();
6111 }
6112 
6113 bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr, const ScAddress& rFormulaPos)
6114 {
6115     assert(rRange.aStart != rRange.aEnd);
6116 
6117     bool bOk = false;
6118     SCCOL nMyCol = rFormulaPos.Col();
6119     SCROW nMyRow = rFormulaPos.Row();
6120     SCTAB nMyTab = rFormulaPos.Tab();
6121     SCCOL nCol = 0;
6122     SCROW nRow = 0;
6123     SCTAB nTab;
6124     nTab = rRange.aStart.Tab();
6125     if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
6126     {
6127         nRow = rRange.aStart.Row();
6128         if ( nRow == rRange.aEnd.Row() )
6129         {
6130             bOk = true;
6131             nCol = nMyCol;
6132         }
6133         else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
6134                 && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
6135         {
6136             bOk = true;
6137             nCol = nMyCol;
6138             nRow = nMyRow;
6139         }
6140     }
6141     else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
6142     {
6143         nCol = rRange.aStart.Col();
6144         if ( nCol == rRange.aEnd.Col() )
6145         {
6146             bOk = true;
6147             nRow = nMyRow;
6148         }
6149         else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
6150                 && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
6151         {
6152             bOk = true;
6153             nCol = nMyCol;
6154             nRow = nMyRow;
6155         }
6156     }
6157     if ( bOk )
6158     {
6159         if ( nTab == rRange.aEnd.Tab() )
6160             ;   // all done
6161         else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
6162             nTab = nMyTab;
6163         else
6164             bOk = false;
6165         if ( bOk )
6166             rAdr.Set( nCol, nRow, nTab );
6167     }
6168 
6169     return bOk;
6170 }
6171 
6172 static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta)
6173 {
6174     rXDelta = rRange.aEnd.Col() - rRange.aStart.Col();
6175     rYDelta = rRange.aEnd.Row() - rRange.aStart.Row();
6176 }
6177 
6178 bool ScCompiler::AdjustSumRangeShape(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange)
6179 {
6180     ScRange aAbs = rSumRange.toAbs(pDoc, aPos);
6181 
6182     // Current sum-range end col/row
6183     SCCOL nEndCol = aAbs.aEnd.Col();
6184     SCROW nEndRow = aAbs.aEnd.Row();
6185 
6186     // Current behaviour is, we will get a #NAME? for the below case, so bail out.
6187     // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula
6188     // has a single-ref as the sum-range.
6189     if (!pDoc->ValidCol(nEndCol) || !pDoc->ValidRow(nEndRow))
6190         return false;
6191 
6192     SCCOL nXDeltaSum = 0;
6193     SCROW nYDeltaSum = 0;
6194 
6195     lcl_GetColRowDeltas(aAbs, nXDeltaSum, nYDeltaSum);
6196 
6197     aAbs = rBaseRange.toAbs(pDoc, aPos);
6198     SCCOL nXDelta = 0;
6199     SCROW nYDelta = 0;
6200 
6201     lcl_GetColRowDeltas(aAbs, nXDelta, nYDelta);
6202 
6203     if (nXDelta == nXDeltaSum &&
6204         nYDelta == nYDeltaSum)
6205         return false;  // shapes of base-range match current sum-range
6206 
6207     // Try to make the sum-range to take the same shape as base-range,
6208     // by adjusting Ref2 member of rSumRange if the resultant sum-range don't
6209     // go out-of-bounds.
6210 
6211     SCCOL nXInc = nXDelta - nXDeltaSum;
6212     SCROW nYInc = nYDelta - nYDeltaSum;
6213 
6214     // Don't let a valid End[Col,Row] go beyond (pDoc->MaxCol(),pDoc->MaxRow()) to match
6215     // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks
6216     // the base-range by the (out-of-bound)amount clipped off the sum-range.
6217     // TODO: Probably we can optimize (from threading perspective) rBaseRange
6218     //       by shrinking it here correspondingly (?)
6219     if (nEndCol + nXInc > pDoc->MaxCol())
6220         nXInc = pDoc->MaxCol() - nEndCol;
6221     if (nEndRow + nYInc > pDoc->MaxRow())
6222         nYInc = pDoc->MaxRow() - nEndRow;
6223 
6224     rSumRange.Ref2.IncCol(nXInc);
6225     rSumRange.Ref2.IncRow(nYInc);
6226 
6227     return true;
6228 }
6229 
6230 void ScCompiler::CorrectSumRange(const ScComplexRefData& rBaseRange,
6231                                  ScComplexRefData& rSumRange,
6232                                  FormulaToken** ppSumRangeToken)
6233 {
6234     if (!AdjustSumRangeShape(rBaseRange, rSumRange))
6235         return;
6236 
6237     // Replace sum-range token
6238     FormulaToken* pNewSumRangeTok = new ScDoubleRefToken(pDoc->GetSheetLimits(), rSumRange);
6239     (*ppSumRangeToken)->DecRef();
6240     *ppSumRangeToken = pNewSumRangeTok;
6241     pNewSumRangeTok->IncRef();
6242 }
6243 
6244 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6245