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