Bug Summary

File:home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx
Warning:line 2639, column 22
Dereference of null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name FormulaCompiler.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D FORMULA_DLLIMPLEMENTATION -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/formula/inc -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx
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#include <sal/macros.h>
20#include <sal/log.hxx>
21#include <formula/FormulaCompiler.hxx>
22#include <formula/errorcodes.hxx>
23#include <formula/token.hxx>
24#include <formula/tokenarray.hxx>
25#include <core_resource.hxx>
26#include <core_resource.hrc>
27
28#include <osl/mutex.hxx>
29
30#include <svl/zforlist.hxx>
31#include <unotools/charclass.hxx>
32#include <vcl/svapp.hxx>
33#include <vcl/settings.hxx>
34#include <comphelper/processfactory.hxx>
35#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
36#include <com/sun/star/sheet/FormulaMapGroup.hpp>
37#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
38#include <algorithm>
39
40namespace formula
41{
42 using namespace ::com::sun::star;
43
44 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
45
46namespace {
47
48class FormulaCompilerRecursionGuard
49{
50 private:
51 short& rRecursion;
52 public:
53 explicit FormulaCompilerRecursionGuard( short& rRec )
54 : rRecursion( rRec ) { ++rRecursion; }
55 ~FormulaCompilerRecursionGuard() { --rRecursion; }
56};
57
58SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
59{
60 switch (eOpCode)
61 {
62 case ocEqual:
63 case ocNotEqual:
64 case ocLess:
65 case ocGreater:
66 case ocLessEqual:
67 case ocGreaterEqual:
68 case ocAnd:
69 case ocOr:
70 case ocXor:
71 case ocNot:
72 case ocTrue:
73 case ocFalse:
74 case ocIsEmpty:
75 case ocIsString:
76 case ocIsNonString:
77 case ocIsLogical:
78 case ocIsRef:
79 case ocIsValue:
80 case ocIsFormula:
81 case ocIsNA:
82 case ocIsErr:
83 case ocIsError:
84 case ocIsEven:
85 case ocIsOdd:
86 case ocExact:
87 return SvNumFormatType::LOGICAL;
88 case ocGetActDate:
89 case ocGetDate:
90 case ocEasterSunday :
91 return SvNumFormatType::DATE;
92 case ocGetActTime:
93 return SvNumFormatType::DATETIME;
94 case ocGetTime:
95 return SvNumFormatType::TIME;
96 case ocNPV:
97 case ocPV:
98 case ocSYD:
99 case ocDDB:
100 case ocDB:
101 case ocVBD:
102 case ocSLN:
103 case ocPMT:
104 case ocFV:
105 case ocIpmt:
106 case ocPpmt:
107 case ocCumIpmt:
108 case ocCumPrinc:
109 return SvNumFormatType::CURRENCY;
110 case ocRate:
111 case ocIRR:
112 case ocMIRR:
113 case ocRRI:
114 case ocEffect:
115 case ocNominal:
116 case ocPercentSign:
117 return SvNumFormatType::PERCENT;
118 default:
119 return SvNumFormatType::NUMBER;
120 }
121}
122
123void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
124 const OUString* pTable, sal_uInt16 nOpCode )
125{
126 sheet::FormulaOpCodeMapEntry aEntry;
127 aEntry.Token.OpCode = nOpCode;
128 aEntry.Name = pTable[nOpCode];
129 rVec.push_back( aEntry);
130}
131
132void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
133 const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
134{
135 for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
136 lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
137}
138
139void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
140 const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
141{
142 for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
143 lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
144}
145
146CharClass* createCharClassIfNonEnglishUI()
147{
148 const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
149 if (rLanguageTag.getLanguage() == "en")
150 return nullptr;
151 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
152}
153
154class OpCodeList
155{
156public:
157
158 OpCodeList(bool bLocalized, const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
159 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
160
161private:
162 bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
163 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
164
165private:
166 FormulaCompiler::SeparatorType meSepType;
167 const std::pair<const char*, int>* mpSymbols;
168 bool mbLocalized;
169};
170
171OpCodeList::OpCodeList(bool bLocalized, const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
172 FormulaCompiler::SeparatorType eSepType)
173 : meSepType(eSepType)
174 , mpSymbols(pSymbols)
175 , mbLocalized(bLocalized)
176{
177 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
178 const CharClass* pCharClass = xCharClass.get();
179 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
180 {
181 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID(500 - 1); ++i)
182 {
183 putDefaultOpCode( xMap, i, pCharClass);
184 }
185 }
186 else
187 {
188 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID(500 - 1); ++i)
189 {
190 OUString aOpStr;
191 if ( getOpCodeString( aOpStr, i) )
192 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
193 else
194 putDefaultOpCode( xMap, i, pCharClass);
195 }
196 }
197}
198
199bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
200{
201 switch (nOp)
202 {
203 case SC_OPCODE_SEP12:
204 {
205 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
206 {
207 rStr = ";";
208 return true;
209 }
210 }
211 break;
212 case SC_OPCODE_ARRAY_COL_SEP27:
213 {
214 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
215 {
216 rStr = ";";
217 return true;
218 }
219 }
220 break;
221 case SC_OPCODE_ARRAY_ROW_SEP26:
222 {
223 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
224 {
225 rStr = "|";
226 return true;
227 }
228 }
229 break;
230 }
231
232 return false;
233}
234
235void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
236 const CharClass* pCharClass )
237{
238 const char* pKey = nullptr;
239 for (const std::pair<const char*, int>* pSymbol = mpSymbols; pSymbol->first; ++pSymbol)
240 {
241 if (nOp == pSymbol->second)
242 {
243 pKey = pSymbol->first;
244 break;
245 }
246 }
247 if (!pKey)
248 return;
249 OUString sKey = !mbLocalized ? OUString::createFromAscii(pKey) : ForResId(pKey);
250 xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
251}
252
253// static
254const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
255{
256 if ( !pStr )
257 return nullptr;
258 while ( *pStr )
259 {
260 if ( *pStr == c )
261 return pStr;
262 pStr++;
263 }
264 return nullptr;
265}
266
267struct OpCodeMapData
268{
269 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
270 osl::Mutex maMtx;
271};
272
273
274bool isPotentialRangeLeftOp( OpCode eOp )
275{
276 switch (eOp)
277 {
278 case ocClose:
279 return true;
280 default:
281 return false;
282 }
283}
284
285bool isRangeResultFunction( OpCode eOp )
286{
287 switch (eOp)
288 {
289 case ocIndirect:
290 case ocOffset:
291 return true;
292 default:
293 return false;
294 }
295}
296
297bool isRangeResultOpCode( OpCode eOp )
298{
299 switch (eOp)
300 {
301 case ocRange:
302 case ocUnion:
303 case ocIntersect:
304 case ocIndirect:
305 case ocOffset:
306 return true;
307 default:
308 return false;
309 }
310}
311
312/**
313 @param pToken
314 MUST be a valid token, caller has to ensure.
315
316 @param bRight
317 If bRPN==false, bRight==false means opcodes for left side are
318 checked, bRight==true means opcodes for right side. If bRPN==true
319 it doesn't matter except for the ocSep converted to ocUnion case.
320 */
321bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
322{
323 switch (pToken->GetType())
324 {
325 case svByte: // could be range result, but only a few
326 if (bRPN)
327 return isRangeResultOpCode( pToken->GetOpCode());
328 else if (bRight)
329 return isRangeResultFunction( pToken->GetOpCode());
330 else
331 return isPotentialRangeLeftOp( pToken->GetOpCode());
332 case svSingleRef:
333 case svDoubleRef:
334 case svIndex: // could be range
335 //case svRefList: // um..what?
336 case svExternalSingleRef:
337 case svExternalDoubleRef:
338 case svExternalName: // could be range
339 return true;
340 case svSep:
341 // A special case if a previous ocSep was converted to ocUnion it
342 // stays svSep instead of svByte.
343 return bRPN && !bRight && pToken->GetOpCode() == ocUnion;
344 default:
345 // Separators are not part of RPN and right opcodes need to be
346 // other StackVar types or functions and thus svByte.
347 return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
348 }
349}
350
351bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
352{
353 FormulaToken* pToken1 = *pCode1;
354 FormulaToken* pToken2 = *pCode2;
355 if (pToken1 && pToken2)
356 return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
357 return false;
358}
359
360bool isAdjacentRpnEnd( sal_uInt16 nPC,
361 FormulaToken const * const * const pCode,
362 FormulaToken const * const * const pCode1,
363 FormulaToken const * const * const pCode2 )
364{
365 return nPC >= 2 && pCode1 && pCode2 &&
366 (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
367 (*pCode1 != nullptr) && (*pCode2 != nullptr);
368}
369
370bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
371 FormulaToken const * const * const pCode,
372 FormulaToken const * const * const pCode1,
373 FormulaToken const * const * const pCode2 )
374{
375 return nPC >= 2 && pCode1 && pCode2 &&
376 (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
377 (*pCode1 != nullptr) && (*pCode2 != nullptr);
378}
379
380
381} // namespace
382
383
384void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
385{
386 // Different symbols may map to the same AddIn, but the same AddIn may not
387 // map to different symbols, the first pair wins. Same symbol of course may
388 // not map to different AddIns, again the first pair wins and also the
389 // AddIn->symbol mapping is not inserted in other cases.
390 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
391 SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn)do { if (true && (!bOk)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "OpCodeMap::putExternal: symbol not inserted, "
<< rSymbol << " -> " << rAddIn) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "391" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putExternal: symbol not inserted, "
<< rSymbol << " -> " << rAddIn), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "391" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putExternal: symbol not inserted, " <<
rSymbol << " -> " << rAddIn) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "391" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putExternal: symbol not inserted, "
<< rSymbol << " -> " << rAddIn), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "391" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
392 if (bOk)
393 {
394 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
395 // Failed insertion of the AddIn is ok for different symbols mapping to
396 // the same AddIn. Make this INFO only.
397 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol)do { if (true && (!bOk)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_INFO, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "OpCodeMap::putExternal: AddIn not inserted, "
<< rAddIn << " -> " << rSymbol) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "397" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putExternal: AddIn not inserted, "
<< rAddIn << " -> " << rSymbol), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol; ::sal::detail::log( (::
SAL_DETAIL_LOG_LEVEL_INFO), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "397" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putExternal: AddIn not inserted, " <<
rAddIn << " -> " << rSymbol) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "397" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putExternal: AddIn not inserted, "
<< rAddIn << " -> " << rSymbol), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol; ::sal::detail::log( (::
SAL_DETAIL_LOG_LEVEL_INFO), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "397" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
398 }
399}
400
401void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
402{
403 bool bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
404 if (bOk)
405 maExternalHashMap.emplace(rSymbol, rAddIn);
406}
407
408uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
409 const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
410{
411 const sal_Int32 nLen = rNames.getLength();
412 uno::Sequence< sheet::FormulaToken > aTokens( nLen);
413 sheet::FormulaToken* pToken = aTokens.getArray();
414 OUString const * pName = rNames.getConstArray();
415 OUString const * const pStop = pName + nLen;
416 for ( ; pName < pStop; ++pName, ++pToken)
417 {
418 OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
419 if (iLook != maHashMap.end())
420 pToken->OpCode = (*iLook).second;
421 else
422 {
423 OUString aIntName;
424 if (hasExternals())
425 {
426 ExternalHashMap::const_iterator iExt( maExternalHashMap.find( *pName));
427 if (iExt != maExternalHashMap.end())
428 aIntName = (*iExt).second;
429 // Check for existence not needed here, only name-mapping is of
430 // interest.
431 }
432 if (aIntName.isEmpty())
433 aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
434 if (aIntName.isEmpty())
435 pToken->OpCode = getOpCodeUnknown();
436 else
437 {
438 pToken->OpCode = ocExternal;
439 pToken->Data <<= aIntName;
440 }
441 }
442 }
443 return aTokens;
444}
445
446uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
447 const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
448{
449 using namespace sheet;
450
451 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
452 // we don't know in advance how many elements it will have we use a
453 // temporary vector to add elements and then copy to Sequence :-(
454 ::std::vector< FormulaOpCodeMapEntry > aVec;
455
456 if (nGroups == FormulaMapGroup::SPECIAL)
457 {
458 // Use specific order, keep in sync with
459 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
460 static const struct
461 {
462 sal_Int32 nOff;
463 OpCode eOp;
464 } aMap[] = {
465 { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
466 { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
467 { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
468 { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
469 { FormulaMapGroupSpecialOffset::NAME , ocName } ,
470 { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
471 { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
472 { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
473 { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
474 { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
475 { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
476 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
477 { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
478 { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName }
479 };
480 const size_t nCount = SAL_N_ELEMENTS(aMap)(sizeof(sal_n_array_size(aMap)));
481 // Preallocate vector elements.
482 FormulaOpCodeMapEntry aEntry;
483 aEntry.Token.OpCode = getOpCodeUnknown();
484 aVec.resize(nCount, aEntry);
485
486 for (auto& i : aMap)
487 {
488 size_t nIndex = static_cast< size_t >( i.nOff );
489 if (aVec.size() <= nIndex)
490 {
491 // The offsets really should be aligned with the size, so if
492 // the vector was preallocated above this code to resize it is
493 // just a measure in case the table isn't in sync with the API,
494 // usually it isn't executed.
495 aEntry.Token.OpCode = getOpCodeUnknown();
496 aVec.resize( nIndex + 1, aEntry );
497 }
498 aEntry.Token.OpCode = i.eOp;
499 aVec[nIndex] = aEntry;
500 }
501 }
502 else
503 {
504 /* FIXME: Once we support error constants in formulas we'll need a map
505 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
506 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
507
508 // Anything else but SPECIAL.
509 if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
510 {
511 static const sal_uInt16 aOpCodes[] = {
512 SC_OPCODE_OPEN10,
513 SC_OPCODE_CLOSE11,
514 SC_OPCODE_SEP12,
515 };
516 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes)(sizeof(sal_n_array_size(aOpCodes))) );
517 }
518 if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
519 {
520 static const sal_uInt16 aOpCodes[] = {
521 SC_OPCODE_ARRAY_OPEN24,
522 SC_OPCODE_ARRAY_CLOSE25,
523 SC_OPCODE_ARRAY_ROW_SEP26,
524 SC_OPCODE_ARRAY_COL_SEP27
525 };
526 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes)(sizeof(sal_n_array_size(aOpCodes))) );
527 }
528 if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
529 {
530 // Due to the nature of the percent operator following its operand
531 // it isn't sorted into unary operators for compiler interna.
532 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocPercentSign );
533 // "+" can be used as unary operator too, push only if binary group is not set
534 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
535 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocAdd );
536 // regular unary operators
537 for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP70; nOp < SC_OPCODE_STOP_UN_OP71 && nOp < mnSymbols; ++nOp)
538 {
539 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
540 }
541 }
542 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
543 {
544 for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP50; nOp < SC_OPCODE_STOP_BIN_OP67 && nOp < mnSymbols; ++nOp)
545 {
546 switch (nOp)
547 {
548 // AND and OR in fact are functions but for legacy reasons
549 // are sorted into binary operators for compiler interna.
550 case SC_OPCODE_AND62 :
551 case SC_OPCODE_OR63 :
552 break; // nothing,
553 default:
554 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
555 }
556 }
557 }
558 if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
559 {
560 // Function names are not consecutive, skip the gaps between
561 // functions with no parameter, functions with 1 parameter
562 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_NO_PAR75,
563 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR84, mnSymbols ) );
564 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_1_PAR90,
565 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR180, mnSymbols ) );
566 // Additional functions not within range of functions.
567 static const sal_uInt16 aOpCodes[] = {
568 SC_OPCODE_IF6,
569 SC_OPCODE_IF_ERROR7,
570 SC_OPCODE_IF_NA8,
571 SC_OPCODE_CHOOSE9,
572 SC_OPCODE_AND62,
573 SC_OPCODE_OR63
574 };
575 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes)(sizeof(sal_n_array_size(aOpCodes))) );
576 // functions with 2 or more parameters.
577 for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR201; nOp < SC_OPCODE_STOP_2_PAR500 && nOp < mnSymbols; ++nOp)
578 {
579 switch (nOp)
580 {
581 // NO_NAME is in SPECIAL.
582 case SC_OPCODE_NO_NAME367 :
583 break; // nothing,
584 default:
585 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
586 }
587 }
588 // If AddIn functions are present in this mapping, use them, and only those.
589 if (hasExternals())
590 {
591 for (auto const& elem : maExternalHashMap)
592 {
593 FormulaOpCodeMapEntry aEntry;
594 aEntry.Name = elem.first;
595 aEntry.Token.Data <<= elem.second;
596 aEntry.Token.OpCode = ocExternal;
597 aVec.push_back( aEntry);
598 }
599 }
600 else
601 {
602 rCompiler.fillAddInToken( aVec, isEnglish());
603 }
604 }
605 }
606 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
607}
608
609
610void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
611{
612 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
613 {
614 bool bPutOp = mpTable[eOp].isEmpty();
615 bool bRemoveFromMap = false;
616 if (!bPutOp)
617 {
618 switch (eOp)
619 {
620 // These OpCodes are meant to overwrite and also remove an
621 // existing mapping.
622 case ocCurrency:
623 bPutOp = true;
624 bRemoveFromMap = true;
625 break;
626 // These separator OpCodes are meant to overwrite and also
627 // remove an existing mapping if it is not used for one of the
628 // other separators.
629 case ocArrayColSep:
630 bPutOp = true;
631 bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
632 break;
633 case ocArrayRowSep:
634 bPutOp = true;
635 bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
636 break;
637 // For ocSep keep the ";" in map but remove any other if it is
638 // not used for ocArrayColSep or ocArrayRowSep.
639 case ocSep:
640 bPutOp = true;
641 bRemoveFromMap = (mpTable[eOp] != ";" &&
642 mpTable[ocArrayColSep] != mpTable[eOp] &&
643 mpTable[ocArrayRowSep] != mpTable[eOp]);
644 break;
645 // These OpCodes are known to be duplicates in the Excel
646 // external API mapping because of different parameter counts
647 // in different BIFF versions. Names are identical and entries
648 // are ignored.
649 case ocLinest:
650 case ocTrend:
651 case ocLogest:
652 case ocGrowth:
653 case ocTrunc:
654 case ocFixed:
655 case ocGetDayOfWeek:
656 case ocHLookup:
657 case ocVLookup:
658 case ocGetDiffDate360:
659 if (rStr == mpTable[eOp])
660 return;
661 [[fallthrough]];
662 // These OpCodes are known to be added to an existing mapping,
663 // but only for the OOXML external API mapping. This is *not*
664 // FormulaLanguage::OOXML. Keep the first
665 // (correct) definition for the OpCode, all following are
666 // additional alias entries in the map.
667 case ocErrorType:
668 case ocMultiArea:
669 case ocBackSolver:
670 case ocEasterSunday:
671 case ocCurrent:
672 case ocStyle:
673 if (mbEnglish &&
674 FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
675 {
676 // Both bPutOp and bRemoveFromMap stay false.
677 break;
678 }
679 [[fallthrough]];
680 default:
681 SAL_WARN("formula.core",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putOpCode: reusing OpCode " << static_cast
<sal_uInt16>(eOp) << ", replacing '" << mpTable
[eOp] << "' with '" << rStr << "' in " <<
(mbEnglish ? "" : "non-") << "English map 0x" <<
::std::hex << meGrammar) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
682 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putOpCode: reusing OpCode " << static_cast
<sal_uInt16>(eOp) << ", replacing '" << mpTable
[eOp] << "' with '" << rStr << "' in " <<
(mbEnglish ? "" : "non-") << "English map 0x" <<
::std::hex << meGrammar) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
683 << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putOpCode: reusing OpCode " << static_cast
<sal_uInt16>(eOp) << ", replacing '" << mpTable
[eOp] << "' with '" << rStr << "' in " <<
(mbEnglish ? "" : "non-") << "English map 0x" <<
::std::hex << meGrammar) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
684 << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putOpCode: reusing OpCode " << static_cast
<sal_uInt16>(eOp) << ", replacing '" << mpTable
[eOp] << "' with '" << rStr << "' in " <<
(mbEnglish ? "" : "non-") << "English map 0x" <<
::std::hex << meGrammar) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "OpCodeMap::putOpCode: reusing OpCode "
<< static_cast<sal_uInt16>(eOp) << ", replacing '"
<< mpTable[eOp] << "' with '" << rStr <<
"' in " << (mbEnglish ? "" : "non-") << "English map 0x"
<< ::std::hex << meGrammar; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "684" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
685 }
686 }
687
688 // Case preserving opcode -> string, upper string -> opcode
689 if (bRemoveFromMap)
690 {
691 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
692 // Ensure we remove a mapping only for the requested OpCode.
693 OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
694 if (it != maHashMap.end() && (*it).second == eOp)
695 maHashMap.erase( it);
696 }
697 if (bPutOp)
698 mpTable[eOp] = rStr;
699 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
700 maHashMap.emplace(aUpper, eOp);
701 }
702 else
703 {
704 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putOpCode: OpCode out of range"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "704" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: OpCode out of range"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "OpCodeMap::putOpCode: OpCode out of range"; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"),
("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "704" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putOpCode: OpCode out of range") == 1
) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "704" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "OpCodeMap::putOpCode: OpCode out of range"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "OpCodeMap::putOpCode: OpCode out of range"; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"),
("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "704" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
705 }
706}
707
708
709FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
710 :
711 nCurrentFactorParam(0),
712 pArr( &rArr ),
713 maArrIterator( rArr ),
714 pCode( nullptr ),
715 pStack( nullptr ),
716 eLastOp( ocPush ),
717 nRecursion( 0 ),
718 nNumFmt( SvNumFormatType::UNDEFINED ),
719 pc( 0 ),
720 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
721 bAutoCorrect( false ),
722 bCorrected( false ),
723 glSubTotal( false ),
724 needsRPNTokenCheck( false ),
725 mbJumpCommandReorder(true),
726 mbStopOnError(true),
727 mbComputeII(bComputeII),
728 mbMatrixFlag(bMatrixFlag)
729{
730}
731
732FormulaTokenArray FormulaCompiler::smDummyTokenArray;
733
734FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
735 :
736 nCurrentFactorParam(0),
737 pArr( nullptr ),
738 maArrIterator( smDummyTokenArray ),
739 pCode( nullptr ),
740 pStack( nullptr ),
741 eLastOp( ocPush ),
742 nRecursion(0),
743 nNumFmt( SvNumFormatType::UNDEFINED ),
744 pc( 0 ),
745 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
746 bAutoCorrect( false ),
747 bCorrected( false ),
748 glSubTotal( false ),
749 needsRPNTokenCheck( false ),
750 mbJumpCommandReorder(true),
751 mbStopOnError(true),
752 mbComputeII(bComputeII),
753 mbMatrixFlag(bMatrixFlag)
754{
755}
756
757FormulaCompiler::~FormulaCompiler()
758{
759}
760
761FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
762{
763 FormulaCompiler::OpCodeMapPtr xMap;
764 using namespace sheet;
765 switch (nLanguage)
766 {
767 case FormulaLanguage::ODFF :
768 if (!mxSymbolsODFF)
769 InitSymbolsODFF();
770 xMap = mxSymbolsODFF;
771 break;
772 case FormulaLanguage::ODF_11 :
773 if (!mxSymbolsPODF)
774 InitSymbolsPODF();
775 xMap = mxSymbolsPODF;
776 break;
777 case FormulaLanguage::ENGLISH :
778 if (!mxSymbolsEnglish)
779 InitSymbolsEnglish();
780 xMap = mxSymbolsEnglish;
781 break;
782 case FormulaLanguage::NATIVE :
783 if (!mxSymbolsNative)
784 InitSymbolsNative();
785 xMap = mxSymbolsNative;
786 break;
787 case FormulaLanguage::XL_ENGLISH:
788 if (!mxSymbolsEnglishXL)
789 InitSymbolsEnglishXL();
790 xMap = mxSymbolsEnglishXL;
791 break;
792 case FormulaLanguage::OOXML:
793 if (!mxSymbolsOOXML)
794 InitSymbolsOOXML();
795 xMap = mxSymbolsOOXML;
796 break;
797 case FormulaLanguage::API :
798 if (!mxSymbolsAPI)
799 InitSymbolsAPI();
800 xMap = mxSymbolsAPI;
801 break;
802 default:
803 ; // nothing, NULL map returned
804 }
805 return xMap;
806}
807
808OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
809{
810 return OUString();
811}
812
813FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
814 const uno::Sequence<
815 const sheet::FormulaOpCodeMapEntry > & rMapping,
816 bool bEnglish )
817{
818 using sheet::FormulaOpCodeMapEntry;
819 // Filter / API maps are never Core
820 NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID(500 - 1) + 1, false,
821 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
822 FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
823 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
824 const CharClass* pCharClass = xCharClass.get();
825 for (auto const& rMapEntry : rMapping)
826 {
827 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
828 if (eOp != ocExternal)
829 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
830 else
831 {
832 OUString aExternalName;
833 if (rMapEntry.Token.Data >>= aExternalName)
834 xMap->putExternal( rMapEntry.Name, aExternalName);
835 else
836 {
837 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "837" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "837" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "837" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "837" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
838 }
839 }
840 }
841 return xMap;
842}
843
844static void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, bool bDestroy = false )
845{
846 static OpCodeMapData aSymbolMap;
847 osl::MutexGuard aGuard(&aSymbolMap.maMtx);
848
849 if ( bDestroy )
850 {
851 aSymbolMap.mxSymbolMap.reset();
852 }
853 else if (!aSymbolMap.mxSymbolMap)
854 {
855 // Core
856 aSymbolMap.mxSymbolMap =
857 std::make_shared<FormulaCompiler::OpCodeMap>(
858 SC_OPCODE_LAST_OPCODE_ID(500 - 1) + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
859 OpCodeList aOpCodeListSymbols(false, RID_STRLIST_FUNCTION_NAMES_SYMBOLS, aSymbolMap.mxSymbolMap);
860 OpCodeList aOpCodeListNative(true, RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
861 // No AddInMap for native core mapping.
862 }
863
864 xMap = aSymbolMap.mxSymbolMap;
865}
866
867const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
868{
869 NonConstOpCodeMapPtr xSymbolsNative;
870 lcl_fillNativeSymbols( xSymbolsNative);
871 return xSymbolsNative->getSymbol( eOp );
872}
873
874sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
875{
876 return GetNativeSymbol(eOp)[0];
877}
878
879void FormulaCompiler::InitSymbolsNative() const
880{
881 lcl_fillNativeSymbols( mxSymbolsNative);
882}
883
884void FormulaCompiler::InitSymbolsEnglish() const
885{
886 static OpCodeMapData aMap;
887 osl::MutexGuard aGuard(&aMap.maMtx);
888 if (!aMap.mxSymbolMap)
889 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
890 mxSymbolsEnglish = aMap.mxSymbolMap;
891}
892
893void FormulaCompiler::InitSymbolsPODF() const
894{
895 static OpCodeMapData aMap;
896 osl::MutexGuard aGuard(&aMap.maMtx);
897 if (!aMap.mxSymbolMap)
898 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
899 mxSymbolsPODF = aMap.mxSymbolMap;
900}
901
902void FormulaCompiler::InitSymbolsAPI() const
903{
904 static OpCodeMapData aMap;
905 osl::MutexGuard aGuard(&aMap.maMtx);
906 if (!aMap.mxSymbolMap)
907 // XFunctionAccess API always used PODF grammar, keep it.
908 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
909 mxSymbolsAPI = aMap.mxSymbolMap;
910}
911
912void FormulaCompiler::InitSymbolsODFF() const
913{
914 static OpCodeMapData aMap;
915 osl::MutexGuard aGuard(&aMap.maMtx);
916 if (!aMap.mxSymbolMap)
917 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
918 mxSymbolsODFF = aMap.mxSymbolMap;
919}
920
921void FormulaCompiler::InitSymbolsEnglishXL() const
922{
923 static OpCodeMapData aMap;
924 osl::MutexGuard aGuard(&aMap.maMtx);
925 if (!aMap.mxSymbolMap)
926 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
927 mxSymbolsEnglishXL = aMap.mxSymbolMap;
928
929 // TODO: For now, just replace the separators to the Excel English
930 // variants. Later, if we want to properly map Excel functions with Calc
931 // functions, we'll need to do a little more work here.
932 mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
933 mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
934 mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
935}
936
937void FormulaCompiler::InitSymbolsOOXML() const
938{
939 static OpCodeMapData aMap;
940 osl::MutexGuard aGuard(&aMap.maMtx);
941 if (!aMap.mxSymbolMap)
942 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
943 mxSymbolsOOXML = aMap.mxSymbolMap;
944}
945
946
947void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
948 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
949{
950 if ( rxMap )
951 return;
952
953 // not Core
954 rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID(500 - 1) + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
955 OpCodeList aOpCodeList(false, pSymbols, rxMap, eSepType);
956
957 fillFromAddInMap( rxMap, eGrammar);
958 // Fill from collection for AddIns not already present.
959 if ( FormulaGrammar::GRAM_ENGLISH != eGrammar )
960 fillFromAddInCollectionUpperName( rxMap);
961 else
962 fillFromAddInCollectionEnglishName( rxMap);
963}
964
965void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
966{
967}
968
969void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
970{
971}
972
973void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
974{
975}
976
977OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
978{
979 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
980
981 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
982 bool bFound = (iLook != xMap->getHashMap().end());
983 return bFound ? (*iLook).second : ocNone;
984}
985
986bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
987{
988 bool bRet = false;
989 switch (eOp)
990 {
991 // no parameters:
992 case ocRandom:
993 case ocGetActDate:
994 case ocGetActTime:
995 // one parameter:
996 case ocFormula:
997 case ocInfo:
998 // more than one parameters:
999 // ocIndirect otherwise would have to do
1000 // StopListening and StartListening on a reference for every
1001 // interpreted value.
1002 case ocIndirect:
1003 // ocOffset results in indirect references.
1004 case ocOffset:
1005 // ocDebugVar shows internal value that may change as the internal state changes.
1006 case ocDebugVar:
1007 bRet = true;
1008 break;
1009 default:
1010 bRet = false;
1011 break;
1012 }
1013 return bRet;
1014}
1015
1016bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
1017{
1018 switch (eOp)
1019 {
1020 case ocIf:
1021 case ocIfError:
1022 case ocIfNA:
1023 case ocChoose:
1024 return true;
1025 default:
1026 ;
1027 }
1028 return false;
1029}
1030
1031// Remove quotes, escaped quotes are unescaped.
1032bool FormulaCompiler::DeQuote( OUString& rStr )
1033{
1034 sal_Int32 nLen = rStr.getLength();
1035 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1036 {
1037 rStr = rStr.copy( 1, nLen-2 );
1038 rStr = rStr.replaceAll( "\\\'", "\'" );
1039 return true;
1040 }
1041 return false;
1042}
1043
1044void FormulaCompiler::fillAddInToken(
1045 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1046 bool /*_bIsEnglish*/) const
1047{
1048}
1049
1050bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
1051{
1052 switch (eOpCode)
1053 {
1054 case ocDde :
1055 case ocGrowth :
1056 case ocTrend :
1057 case ocLogest :
1058 case ocLinest :
1059 case ocFrequency :
1060 case ocMatTrans :
1061 case ocMatMult :
1062 case ocMatInv :
1063 case ocMatrixUnit :
1064 case ocModalValue_Multi :
1065 case ocFourier :
1066 return true;
1067 default:
1068 {
1069 // added to avoid warnings
1070 }
1071 }
1072 return false;
1073}
1074
1075
1076void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
1077{
1078 SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",do { if (true && (!mpTable[eOp].isEmpty() && rSymbol
.isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1079 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)do { if (true && (!mpTable[eOp].isEmpty() && rSymbol
.isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1080 << " '" << mpTable[eOp] << "' with empty name!")do { if (true && (!mpTable[eOp].isEmpty() && rSymbol
.isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::putCopyOpCode: NOT replacing OpCode "
<< static_cast<sal_uInt16>(eOp) << " '" <<
mpTable[eOp] << "' with empty name!"), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast
<sal_uInt16>(eOp) << " '" << mpTable[eOp] <<
"' with empty name!"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1080" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1081 if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
1082 maHashMap.emplace(mpTable[eOp], eOp);
1083 else
1084 {
1085 mpTable[eOp] = rSymbol;
1086 maHashMap.emplace(rSymbol, eOp);
1087 }
1088}
1089
1090void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
1091{
1092 maHashMap = OpCodeHashMap( mnSymbols);
1093
1094 sal_uInt16 n = r.getSymbolCount();
1095 SAL_WARN_IF( n != mnSymbols, "formula.core",do { if (true && (n != mnSymbols)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::copyFrom: unequal size, this: " <<
mnSymbols << " that: " << n) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1096 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n)do { if (true && (n != mnSymbols)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::copyFrom: unequal size, this: " <<
mnSymbols << " that: " << n) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: unequal size, this: "
<< mnSymbols << " that: " << n), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1096" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1097 if (n > mnSymbols)
1098 n = mnSymbols;
1099
1100 // OpCode 0 (ocPush) should never be in a map.
1101 SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",do { if (true && (!mpTable[0].isEmpty() || !r.mpTable
[0].isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1102 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"do { if (true && (!mpTable[0].isEmpty() || !r.mpTable
[0].isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1103 << mpTable[0] << "' that: '" << r.mpTable[0] << "'")do { if (true && (!mpTable[0].isEmpty() || !r.mpTable
[0].isEmpty())) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
<< mpTable[0] << "' that: '" << r.mpTable
[0] << "'"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1103" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1104
1105 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1106 // and API) to the native map (UI "use English function names") replace the
1107 // known bad legacy function names with correct ones.
1108 if (r.mbCore &&
1109 FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
1110 FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
1111 {
1112 for (sal_uInt16 i = 1; i < n; ++i)
1113 {
1114 OUString aSymbol;
1115 OpCode eOp = OpCode(i);
1116 switch (eOp)
1117 {
1118 case ocRRI:
1119 aSymbol = "RRI";
1120 break;
1121 case ocTableOp:
1122 aSymbol = "MULTIPLE.OPERATIONS";
1123 break;
1124 default:
1125 aSymbol = r.mpTable[i];
1126 }
1127 putCopyOpCode( aSymbol, eOp);
1128 }
1129 }
1130 else
1131 {
1132 for (sal_uInt16 i = 1; i < n; ++i)
1133 {
1134 OpCode eOp = OpCode(i);
1135 const OUString& rSymbol = r.mpTable[i];
1136 putCopyOpCode( rSymbol, eOp);
1137 }
1138 }
1139
1140 // TODO: maybe copy the external maps too?
1141}
1142
1143
1144FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
1145{
1146 FormulaError nError = FormulaError::NONE;
1147 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1148 if (iLook != mxSymbols->getHashMap().end())
1149 {
1150 switch ((*iLook).second)
1151 {
1152 // Not all may make sense in a formula, but these we know as
1153 // opcodes.
1154 case ocErrNull:
1155 nError = FormulaError::NoCode;
1156 break;
1157 case ocErrDivZero:
1158 nError = FormulaError::DivisionByZero;
1159 break;
1160 case ocErrValue:
1161 nError = FormulaError::NoValue;
1162 break;
1163 case ocErrRef:
1164 nError = FormulaError::NoRef;
1165 break;
1166 case ocErrName:
1167 nError = FormulaError::NoName;
1168 break;
1169 case ocErrNum:
1170 nError = FormulaError::IllegalFPOperation;
1171 break;
1172 case ocErrNA:
1173 nError = FormulaError::NotAvailable;
1174 break;
1175 default:
1176 ; // nothing
1177 }
1178 }
1179 else
1180 {
1181 // Per convention recognize detailed "#ERRxxx!" constants, always
1182 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1183 // digits.
1184 if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
1185 {
1186 sal_uInt32 nErr = rName.copy( 4, rName.getLength() - 5).toUInt32();
1187 if (0 < nErr && nErr <= SAL_MAX_UINT16((sal_uInt16) 0xFFFF) && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
1188 nError = static_cast<FormulaError>(nErr);
1189 }
1190 }
1191 return nError;
1192}
1193
1194void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
1195{
1196 mbJumpCommandReorder = bEnable;
1197}
1198
1199void FormulaCompiler::EnableStopOnError( bool bEnable )
1200{
1201 mbStopOnError = bEnable;
1202}
1203
1204void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1205{
1206 OpCode eOp;
1207 switch (nError)
1208 {
1209 case FormulaError::NoCode:
1210 eOp = ocErrNull;
1211 break;
1212 case FormulaError::DivisionByZero:
1213 eOp = ocErrDivZero;
1214 break;
1215 case FormulaError::NoValue:
1216 eOp = ocErrValue;
1217 break;
1218 case FormulaError::NoRef:
1219 eOp = ocErrRef;
1220 break;
1221 case FormulaError::NoName:
1222 eOp = ocErrName;
1223 break;
1224 case FormulaError::IllegalFPOperation:
1225 eOp = ocErrNum;
1226 break;
1227 case FormulaError::NotAvailable:
1228 eOp = ocErrNA;
1229 break;
1230 default:
1231 {
1232 // Per convention create detailed "#ERRxxx!" constants, always
1233 // untranslated.
1234 rBuffer.append("#ERR");
1235 rBuffer.append(static_cast<sal_Int32>(nError));
1236 rBuffer.append('!');
1237 return;
1238 }
1239 }
1240 rBuffer.append( mxSymbols->getSymbol( eOp));
1241}
1242
1243
1244bool FormulaCompiler::GetToken()
1245{
1246 static const short nRecursionMax = 42;
1247 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1248 if ( nRecursion > nRecursionMax )
1249 {
1250 SetError( FormulaError::StackOverflow );
1251 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1252 return false;
1253 }
1254 if ( bAutoCorrect && !pStack )
1255 { // don't merge stacked subroutine code into entered formula
1256 aCorrectedFormula += aCorrectedSymbol;
1257 aCorrectedSymbol.clear();
1258 }
1259 bool bStop = false;
1260 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1261 bStop = true;
1262 else
1263 {
1264 FormulaTokenRef pSpacesToken;
1265 short nWasColRowName;
1266 if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
1267 nWasColRowName = 1;
1268 else
1269 nWasColRowName = 0;
1270 mpToken = maArrIterator.Next();
1271 while( mpToken && mpToken->GetOpCode() == ocSpaces )
1272 {
1273 // For significant whitespace remember last ocSpaces token. Usually
1274 // there's only one even for multiple spaces.
1275 pSpacesToken = mpToken;
1276 if ( nWasColRowName )
1277 nWasColRowName++;
1278 if ( bAutoCorrect && !pStack )
1279 CreateStringFromToken( aCorrectedFormula, mpToken.get() );
1280 mpToken = maArrIterator.Next();
1281 }
1282 if ( bAutoCorrect && !pStack && mpToken )
1283 CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
1284 if( !mpToken )
1285 {
1286 if( pStack )
1287 {
1288 PopTokenArray();
1289 // mpLastToken was popped as well and corresponds to the
1290 // then current last token during PushTokenArray(), e.g. for
1291 // HandleRange().
1292 return GetToken();
1293 }
1294 else
1295 bStop = true;
1296 }
1297 else
1298 {
1299 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1300 { // convert an ocSpaces to ocIntersect in RPN
1301 mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
1302 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1303 }
1304 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1305 mpLastToken && mpToken &&
1306 isPotentialRangeType( mpLastToken.get(), false, false) &&
1307 isPotentialRangeType( mpToken.get(), false, true))
1308 {
1309 // Let IntersectionLine() <- Factor() decide how to treat this,
1310 // once the actual arguments are determined in RPN.
1311 mpLastToken = mpToken = pSpacesToken;
1312 maArrIterator.StepBack(); // step back from next non-spaces token
1313 return true;
1314 }
1315 }
1316 }
1317 if( bStop )
1318 {
1319 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1320 return false;
1321 }
1322
1323 // Remember token for next round and any PushTokenArray() calls that may
1324 // occur in handlers.
1325 mpLastToken = mpToken;
1326
1327 if ( mpToken->IsExternalRef() )
1328 {
1329 return HandleExternalReference(*mpToken);
1330 }
1331 else
1332 {
1333 switch (mpToken->GetOpCode())
1334 {
1335 case ocSubTotal:
1336 case ocAggregate:
1337 glSubTotal = true;
1338 break;
1339 case ocName:
1340 if( HandleRange())
1341 {
1342 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1343 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1344 needsRPNTokenCheck = true;
1345 return true;
1346 }
1347 return false;
1348 case ocColRowName:
1349 return HandleColRowName();
1350 case ocDBArea:
1351 return HandleDbData();
1352 case ocTableRef:
1353 return HandleTableRef();
1354 case ocPush:
1355 if( mbComputeII )
1356 HandleIIOpCode(mpToken.get(), nullptr, 0);
1357 break;
1358 default:
1359 ; // nothing
1360 }
1361 }
1362 return true;
1363}
1364
1365
1366// RPN creation by recursion
1367void FormulaCompiler::Factor()
1368{
1369 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1370 return;
1371
1372 CurrentFactor pFacToken( this );
1373
1374 OpCode eOp = mpToken->GetOpCode();
1375 if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
1376 || eOp == ocTableRef
1377 || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
1378 )
1379 {
1380 PutCode( mpToken );
1381 eOp = NextToken();
1382 if( eOp == ocOpen )
1383 {
1384 // PUSH( is an error that may be caused by an unknown function.
1385 SetError(
1386 ( mpToken->GetType() == svString
1387 || mpToken->GetType() == svSingleRef )
1388 ? FormulaError::NoName : FormulaError::OperatorExpected );
1389 if ( bAutoCorrect && !pStack )
1390 { // assume multiplication
1391 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1392 bCorrected = true;
1393 NextToken();
1394 eOp = Expression();
1395 if( eOp != ocClose )
1396 SetError( FormulaError::PairExpected);
1397 else
1398 NextToken();
1399 }
1400 }
1401 }
1402 else if( eOp == ocOpen )
1403 {
1404 NextToken();
1405 eOp = Expression();
1406 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1407 { // range list (A1;A2) converted to (A1~A2)
1408 pFacToken = mpToken;
1409 NextToken();
1410 CheckSetForceArrayParameter( mpToken, 0);
1411 eOp = Expression();
1412 // Do not ignore error here, regardless of mbStopOnError, to not
1413 // change the formula expression in case of an unexpected state.
1414 if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
1415 {
1416 // Left and right operands must be reference or function
1417 // returning reference to form a range list.
1418 const FormulaToken* p = pCode[-2];
1419 if (p && isPotentialRangeType( p, true, false))
1420 {
1421 p = pCode[-1];
1422 if (p && isPotentialRangeType( p, true, true))
1423 {
1424 pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1425 // XXX NOTE: the token's eType is still svSep here!
1426 PutCode( pFacToken);
1427 }
1428 }
1429 }
1430 }
1431 if (eOp != ocClose)
1432 SetError( FormulaError::PairExpected);
1433 else
1434 NextToken();
1435
1436 /* TODO: if no conversion to ocUnion is involved this could collect
1437 * such expression as a list or (matrix) vector to be passed as
1438 * argument for one parameter (which in fact the ocUnion svRefList is a
1439 * special case of), which would require a new StackVar type and needed
1440 * to be handled by the interpreter for functions that could support it
1441 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1442 * not defined by ODF.
1443 * Does Excel handle =SUM((1;2))?
1444 * As is, the interpreter catches extraneous uncalculated
1445 * subexpressions like 1 of (1;2) as error. */
1446 }
1447 else
1448 {
1449 if( nNumFmt == SvNumFormatType::UNDEFINED )
1450 nNumFmt = lcl_GetRetFormat( eOp );
1451
1452 if ( IsOpCodeVolatile( eOp) )
1453 pArr->SetExclusiveRecalcModeAlways();
1454 else
1455 {
1456 switch( eOp )
1457 {
1458 // Functions recalculated on every document load.
1459 // ONLOAD_LENIENT here to be able to distinguish and not
1460 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1461 // context) but keep an imported result from for example
1462 // OOXML a DDE call. Will be recalculated for ODFF.
1463 case ocConvertOOo :
1464 case ocDde:
1465 case ocMacro:
1466 case ocWebservice:
1467 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1468 break;
1469 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1470 // functions may have to be recalculated or not, we don't
1471 // know, classify as ONLOAD_LENIENT.
1472 case ocExternal:
1473 if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1474 pArr->SetExclusiveRecalcModeAlways();
1475 else
1476 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1477 break;
1478 // If the referred cell is moved the value changes.
1479 case ocColumn :
1480 case ocRow :
1481 pArr->SetRecalcModeOnRefMove();
1482 break;
1483 // ocCell needs recalc on move for some possible type values.
1484 // And recalc mode on load, tdf#60645
1485 case ocCell :
1486 pArr->SetRecalcModeOnRefMove();
1487 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1488 break;
1489 case ocHyperLink :
1490 // Cell with hyperlink needs to be calculated on load to
1491 // get its matrix result generated.
1492 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1493 pArr->SetHyperLink( true);
1494 break;
1495 default:
1496 ; // nothing
1497 }
1498 }
1499 if (SC_OPCODE_START_NO_PAR75 <= eOp && eOp < SC_OPCODE_STOP_NO_PAR84)
1500 {
1501 pFacToken = mpToken;
1502 eOp = NextToken();
1503 if (eOp != ocOpen)
1504 {
1505 SetError( FormulaError::PairExpected);
1506 PutCode( pFacToken );
1507 }
1508 else
1509 {
1510 eOp = NextToken();
1511 if (eOp != ocClose)
1512 SetError( FormulaError::PairExpected);
1513 PutCode( pFacToken);
1514 NextToken();
1515 }
1516 }
1517 else if (SC_OPCODE_START_1_PAR90 <= eOp && eOp < SC_OPCODE_STOP_1_PAR180)
1518 {
1519 if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
1520 {
1521 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1522 // the opcode then has to be changed to ocWeek for backward compatibility
1523 pFacToken = mpToken;
1524 eOp = NextToken();
1525 bool bNoParam = false;
1526 if (eOp == ocOpen)
1527 {
1528 eOp = NextToken();
1529 if (eOp == ocClose)
1530 bNoParam = true;
1531 else
1532 {
1533 CheckSetForceArrayParameter( mpToken, 0);
1534 eOp = Expression();
1535 }
1536 }
1537 else
1538 SetError( FormulaError::PairExpected);
1539 sal_uInt32 nSepCount = 0;
1540 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1541 if( !bNoParam )
1542 {
1543 nSepCount++;
1544 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1545 {
1546 NextToken();
1547 CheckSetForceArrayParameter( mpToken, nSepCount);
1548 nSepCount++;
1549 if (nSepCount > FORMULA_MAXPARAMS255)
1550 SetError( FormulaError::CodeOverflow);
1551 eOp = Expression();
1552 }
1553 }
1554 if (eOp != ocClose)
1555 SetError( FormulaError::PairExpected);
1556 else
1557 NextToken();
1558 pFacToken->SetByte( nSepCount );
1559 if (nSepCount == 2)
1560 {
1561 // An old mode!=1 indicates ISO week, remove argument if
1562 // literal double value and keep function. Anything else
1563 // can not be resolved, there exists no "like ISO but week
1564 // starts on Sunday" mode in WEEKNUM and for an expression
1565 // we can't determine.
1566 // Current index is nSepPos+3 if expression stops, or
1567 // nSepPos+4 if expression continues after the call because
1568 // we just called NextToken() to move away from it.
1569 if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
1570 pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
1571 pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
1572 pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
1573 pArr->RemoveToken( nSepPos, 2) == 2)
1574 {
1575 maArrIterator.AfterRemoveToken( nSepPos, 2);
1576 // Remove the ocPush/svDouble just removed also from
1577 // the compiler local RPN array.
1578 --pCode; --pc;
1579 (*pCode)->DecRef(); // may be dead now
1580 pFacToken->SetByte( nSepCount - 1 );
1581 }
1582 else
1583 {
1584 // For the remaining two arguments cases use the
1585 // compatibility function.
1586 pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
1587 }
1588 }
1589 PutCode( pFacToken );
1590 }
1591 else
1592 {
1593 // standard handling of 1-parameter opcodes
1594 pFacToken = mpToken;
1595 eOp = NextToken();
1596 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
1597 nNumFmt = SvNumFormatType::LOGICAL;
1598 if (eOp == ocOpen)
1599 {
1600 NextToken();
1601 CheckSetForceArrayParameter( mpToken, 0);
1602 eOp = Expression();
1603 }
1604 else
1605 SetError( FormulaError::PairExpected);
1606 if (eOp != ocClose)
1607 SetError( FormulaError::PairExpected);
1608 else if ( pArr->GetCodeError() == FormulaError::NONE )
1609 {
1610 pFacToken->SetByte( 1 );
1611 if (mbComputeII)
1612 {
1613 FormulaToken** pArg = pCode - 1;
1614 HandleIIOpCode(pFacToken, &pArg, 1);
1615 }
1616 }
1617 PutCode( pFacToken );
1618 NextToken();
1619 }
1620 }
1621 else if ((SC_OPCODE_START_2_PAR201 <= eOp && eOp < SC_OPCODE_STOP_2_PAR500)
1622 || eOp == ocExternal
1623 || eOp == ocMacro
1624 || eOp == ocAnd
1625 || eOp == ocOr
1626 || eOp == ocBad
1627 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1628 || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1629 {
1630 pFacToken = mpToken;
1631 OpCode eMyLastOp = eOp;
1632 eOp = NextToken();
1633 bool bNoParam = false;
1634 bool bBadName = false;
1635 if (eOp == ocOpen)
1636 {
1637 eOp = NextToken();
1638 if (eOp == ocClose)
1639 bNoParam = true;
1640 else
1641 {
1642 CheckSetForceArrayParameter( mpToken, 0);
1643 eOp = Expression();
1644 }
1645 }
1646 else if (eMyLastOp == ocBad)
1647 {
1648 // Just a bad name, not an unknown function, no parameters, no
1649 // closing expected.
1650 bBadName = true;
1651 bNoParam = true;
1652 }
1653 else
1654 SetError( FormulaError::PairExpected);
1655 sal_uInt32 nSepCount = 0;
1656 if( !bNoParam )
1657 {
1658 bool bDoIICompute = mbComputeII;
1659 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1660 FormulaToken*** pArgArray = nullptr;
1661 if (bDoIICompute)
1662 {
1663 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII)__builtin_alloca (sizeof(FormulaToken**)*8));
1664 if (!pArgArray)
1665 bDoIICompute = false;
1666 }
1667
1668 nSepCount++;
1669
1670 if (bDoIICompute)
1671 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1672
1673 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1674 {
1675 NextToken();
1676 CheckSetForceArrayParameter( mpToken, nSepCount);
1677 nSepCount++;
1678 if (nSepCount > FORMULA_MAXPARAMS255)
1679 SetError( FormulaError::CodeOverflow);
1680 eOp = Expression();
1681 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII8)
1682 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1683 }
1684 if (bDoIICompute)
1685 HandleIIOpCode(pFacToken, pArgArray,
1686 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII8)));
1687 }
1688 if (bBadName)
1689 ; // nothing, keep current token for return
1690 else if (eOp != ocClose)
1691 SetError( FormulaError::PairExpected);
1692 else
1693 NextToken();
1694 // Jumps are just normal functions for the FunctionAutoPilot tree view
1695 if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1696 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1697 else
1698 pFacToken->SetByte( nSepCount );
1699 PutCode( pFacToken );
1700 }
1701 else if (IsOpCodeJumpCommand(eOp))
1702 {
1703 // the PC counters are -1
1704 pFacToken = mpToken;
1705 switch (eOp)
1706 {
1707 case ocIf:
1708 pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1709 break;
1710 case ocChoose:
1711 pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT32 + 1;
1712 break;
1713 case ocIfError:
1714 case ocIfNA:
1715 pFacToken->GetJump()[ 0 ] = 2; // if, behind
1716 break;
1717 default:
1718 SAL_WARN("formula.core","Jump OpCode: " << +eOp)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "Jump OpCode: " <<
+eOp) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1718" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eOp), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eOp; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1718" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Jump OpCode: " << +eOp) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1718" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eOp), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eOp; ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1718" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1719 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case")(static_cast <bool> (!"FormulaCompiler::Factor: someone forgot to add a jump count case"
) ? void (0) : __assert_fail ("!\"FormulaCompiler::Factor: someone forgot to add a jump count case\""
, "/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
, 1719, __extension__ __PRETTY_FUNCTION__))
;
1720 }
1721 eOp = NextToken();
1722 if (eOp == ocOpen)
1723 {
1724 NextToken();
1725 CheckSetForceArrayParameter( mpToken, 0);
1726 eOp = Expression();
1727 }
1728 else
1729 SetError( FormulaError::PairExpected);
1730 PutCode( pFacToken );
1731 // During AutoCorrect (since pArr->GetCodeError() is
1732 // ignored) an unlimited ocIf would crash because
1733 // ScRawToken::Clone() allocates the JumpBuffer according to
1734 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1735 short nJumpMax;
1736 OpCode eFacOpCode = pFacToken->GetOpCode();
1737 switch (eFacOpCode)
1738 {
1739 case ocIf:
1740 nJumpMax = 3;
1741 break;
1742 case ocChoose:
1743 nJumpMax = FORMULA_MAXJUMPCOUNT32;
1744 break;
1745 case ocIfError:
1746 case ocIfNA:
1747 nJumpMax = 2;
1748 break;
1749 default:
1750 nJumpMax = 0;
1751 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "Jump OpCode: " <<
+eFacOpCode) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1751" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eFacOpCode
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eFacOpCode; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1751" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Jump OpCode: " << +eFacOpCode) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1751" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eFacOpCode
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eFacOpCode; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1751" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1752 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case")(static_cast <bool> (!"FormulaCompiler::Factor: someone forgot to add a jump max case"
) ? void (0) : __assert_fail ("!\"FormulaCompiler::Factor: someone forgot to add a jump max case\""
, "/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
, 1752, __extension__ __PRETTY_FUNCTION__))
;
1753 }
1754 short nJumpCount = 0;
1755 while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT32 - 1)) && (eOp == ocSep)
1756 && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1757 {
1758 if ( ++nJumpCount <= nJumpMax )
1759 pFacToken->GetJump()[nJumpCount] = pc-1;
1760 NextToken();
1761 CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
1762 eOp = Expression();
1763 // ocSep or ocClose terminate the subexpression
1764 PutCode( mpToken );
1765 }
1766 if (eOp != ocClose)
1767 SetError( FormulaError::PairExpected);
1768 else
1769 {
1770 NextToken();
1771 // always limit to nJumpMax, no arbitrary overwrites
1772 if ( ++nJumpCount <= nJumpMax )
1773 pFacToken->GetJump()[ nJumpCount ] = pc-1;
1774 eFacOpCode = pFacToken->GetOpCode();
1775 bool bLimitOk;
1776 switch (eFacOpCode)
1777 {
1778 case ocIf:
1779 bLimitOk = (nJumpCount <= 3);
1780 break;
1781 case ocChoose:
1782 bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT32);
1783 break;
1784 case ocIfError:
1785 case ocIfNA:
1786 bLimitOk = (nJumpCount <= 2);
1787 break;
1788 default:
1789 bLimitOk = false;
1790 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "Jump OpCode: " <<
+eFacOpCode) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1790" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eFacOpCode
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eFacOpCode; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1790" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Jump OpCode: " << +eFacOpCode) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1790" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Jump OpCode: " << +eFacOpCode
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Jump OpCode: " << +eFacOpCode; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "1790" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1791 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case")(static_cast <bool> (!"FormulaCompiler::Factor: someone forgot to add a jump limit case"
) ? void (0) : __assert_fail ("!\"FormulaCompiler::Factor: someone forgot to add a jump limit case\""
, "/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
, 1791, __extension__ __PRETTY_FUNCTION__))
;
1792 }
1793 if (bLimitOk)
1794 pFacToken->GetJump()[ 0 ] = nJumpCount;
1795 else
1796 SetError( FormulaError::IllegalParameter);
1797 }
1798 }
1799 else if ( eOp == ocMissing )
1800 {
1801 PutCode( mpToken );
1802 NextToken();
1803 }
1804 else if ( eOp == ocClose )
1805 {
1806 SetError( FormulaError::ParameterExpected );
1807 }
1808 else if ( eOp == ocSep )
1809 { // Subsequent ocSep
1810 SetError( FormulaError::ParameterExpected );
1811 if ( bAutoCorrect && !pStack )
1812 {
1813 aCorrectedSymbol.clear();
1814 bCorrected = true;
1815 }
1816 }
1817 else if ( mpToken->IsExternalRef() )
1818 {
1819 PutCode( mpToken);
1820 NextToken();
1821 }
1822 else
1823 {
1824 SetError( FormulaError::UnknownToken );
1825 if ( bAutoCorrect && !pStack )
1826 {
1827 if ( eOp == ocStop )
1828 { // trailing operator w/o operand
1829 sal_Int32 nLen = aCorrectedFormula.getLength();
1830 if ( nLen )
1831 aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
1832 aCorrectedSymbol.clear();
1833 bCorrected = true;
1834 }
1835 }
1836 }
1837 }
1838}
1839
1840void FormulaCompiler::RangeLine()
1841{
1842 Factor();
1843 while (mpToken->GetOpCode() == ocRange)
2
Assuming the condition is true
3
Loop condition is true. Entering loop body
1844 {
1845 FormulaToken** pCode1 = pCode - 1;
1846 FormulaTokenRef p = mpToken;
1847 NextToken();
1848 Factor();
1849 FormulaToken** pCode2 = pCode - 1;
1850 if (!MergeRangeReference( pCode1, pCode2))
4
Taking true branch
1851 PutCode(p);
5
Calling 'FormulaCompiler::PutCode'
1852 }
1853}
1854
1855void FormulaCompiler::IntersectionLine()
1856{
1857 RangeLine();
1
Calling 'FormulaCompiler::RangeLine'
1858 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
1859 {
1860 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
1861 FormulaToken** pCode1 = pCode - 1;
1862 FormulaTokenRef p = mpToken;
1863 NextToken();
1864 RangeLine();
1865 FormulaToken** pCode2 = pCode - 1;
1866 if (p->GetOpCode() == ocSpaces)
1867 {
1868 // Convert to intersection if both left and right are references or
1869 // functions (potentially returning references, if not then a space
1870 // or no space would be a syntax error anyway), not other operators
1871 // or operands. Else discard.
1872 if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
1873 {
1874 FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
1875 // Replace ocSpaces with ocIntersect so that when switching
1876 // formula syntax the correct operator string is created.
1877 pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
1878 PutCode( pIntersect);
1879 }
1880 }
1881 else
1882 {
1883 PutCode(p);
1884 }
1885 }
1886}
1887
1888void FormulaCompiler::UnionLine()
1889{
1890 IntersectionLine();
1891 while (mpToken->GetOpCode() == ocUnion)
1892 {
1893 FormulaTokenRef p = mpToken;
1894 NextToken();
1895 IntersectionLine();
1896 PutCode(p);
1897 }
1898}
1899
1900void FormulaCompiler::UnaryLine()
1901{
1902 if( mpToken->GetOpCode() == ocAdd )
1903 GetToken();
1904 else if (SC_OPCODE_START_UN_OP70 <= mpToken->GetOpCode() &&
1905 mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP71)
1906 {
1907 FormulaTokenRef p = mpToken;
1908 NextToken();
1909 UnaryLine();
1910 if (mbComputeII)
1911 {
1912 FormulaToken** pArg = pCode - 1;
1913 HandleIIOpCode(p.get(), &pArg, 1);
1914 }
1915 PutCode( p );
1916 }
1917 else
1918 UnionLine();
1919}
1920
1921void FormulaCompiler::PostOpLine()
1922{
1923 UnaryLine();
1924 while ( mpToken->GetOpCode() == ocPercentSign )
1925 { // this operator _follows_ its operand
1926 if (mbComputeII)
1927 {
1928 FormulaToken** pArg = pCode - 1;
1929 HandleIIOpCode(mpToken.get(), &pArg, 1);
1930 }
1931 PutCode( mpToken );
1932 NextToken();
1933 }
1934}
1935
1936void FormulaCompiler::PowLine()
1937{
1938 PostOpLine();
1939 while (mpToken->GetOpCode() == ocPow)
1940 {
1941 FormulaTokenRef p = mpToken;
1942 FormulaToken** pArgArray[2];
1943 if (mbComputeII)
1944 pArgArray[0] = pCode - 1; // Add first argument
1945 NextToken();
1946 PostOpLine();
1947 if (mbComputeII)
1948 {
1949 pArgArray[1] = pCode - 1; // Add second argument
1950 HandleIIOpCode(p.get(), pArgArray, 2);
1951 }
1952 PutCode(p);
1953 }
1954}
1955
1956void FormulaCompiler::MulDivLine()
1957{
1958 PowLine();
1959 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
1960 {
1961 FormulaTokenRef p = mpToken;
1962 FormulaToken** pArgArray[2];
1963 if (mbComputeII)
1964 pArgArray[0] = pCode - 1; // Add first argument
1965 NextToken();
1966 PowLine();
1967 if (mbComputeII)
1968 {
1969 pArgArray[1] = pCode - 1; // Add second argument
1970 HandleIIOpCode(p.get(), pArgArray, 2);
1971 }
1972 PutCode(p);
1973 }
1974}
1975
1976void FormulaCompiler::AddSubLine()
1977{
1978 MulDivLine();
1979 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
1980 {
1981 FormulaTokenRef p = mpToken;
1982 FormulaToken** pArgArray[2];
1983 if (mbComputeII)
1984 pArgArray[0] = pCode - 1; // Add first argument
1985 NextToken();
1986 MulDivLine();
1987 if (mbComputeII)
1988 {
1989 pArgArray[1] = pCode - 1; // Add second argument
1990 HandleIIOpCode(p.get(), pArgArray, 2);
1991 }
1992 PutCode(p);
1993 }
1994}
1995
1996void FormulaCompiler::ConcatLine()
1997{
1998 AddSubLine();
1999 while (mpToken->GetOpCode() == ocAmpersand)
2000 {
2001 FormulaTokenRef p = mpToken;
2002 FormulaToken** pArgArray[2];
2003 if (mbComputeII)
2004 pArgArray[0] = pCode - 1; // Add first argument
2005 NextToken();
2006 AddSubLine();
2007 if (mbComputeII)
2008 {
2009 pArgArray[1] = pCode - 1; // Add second argument
2010 HandleIIOpCode(p.get(), pArgArray, 2);
2011 }
2012 PutCode(p);
2013 }
2014}
2015
2016void FormulaCompiler::CompareLine()
2017{
2018 ConcatLine();
2019 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2020 {
2021 FormulaTokenRef p = mpToken;
2022 FormulaToken** pArgArray[2];
2023 if (mbComputeII)
2024 pArgArray[0] = pCode - 1; // Add first argument
2025 NextToken();
2026 ConcatLine();
2027 if (mbComputeII)
2028 {
2029 pArgArray[1] = pCode - 1; // Add second argument
2030 HandleIIOpCode(p.get(), pArgArray, 2);
2031 }
2032 PutCode(p);
2033 }
2034}
2035
2036OpCode FormulaCompiler::Expression()
2037{
2038 static const short nRecursionMax = 42;
2039 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2040 if ( nRecursion > nRecursionMax )
2041 {
2042 SetError( FormulaError::StackOverflow );
2043 return ocStop; //! generate token instead?
2044 }
2045 CompareLine();
2046 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2047 {
2048 FormulaTokenRef p = mpToken;
2049 mpToken->SetByte( 2 ); // 2 parameters!
2050 FormulaToken** pArgArray[2];
2051 if (mbComputeII)
2052 pArgArray[0] = pCode - 1; // Add first argument
2053 NextToken();
2054 CompareLine();
2055 if (mbComputeII)
2056 {
2057 pArgArray[1] = pCode - 1; // Add second argument
2058 HandleIIOpCode(p.get(), pArgArray, 2);
2059 }
2060 PutCode(p);
2061 }
2062 return mpToken->GetOpCode();
2063}
2064
2065
2066void FormulaCompiler::SetError( FormulaError /*nError*/ )
2067{
2068}
2069
2070FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
2071{
2072 return FormulaTokenRef();
2073}
2074
2075bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2076{
2077 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2078 return false;
2079
2080 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2081 FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
2082 if (!p)
2083 return false;
2084
2085 p->IncRef();
2086 p1->DecRef();
2087 p2->DecRef();
2088 *pCode1 = p.get();
2089 --pCode;
2090 --pc;
2091
2092 return true;
2093}
2094
2095bool FormulaCompiler::CompileTokenArray()
2096{
2097 glSubTotal = false;
2098 bCorrected = false;
2099 needsRPNTokenCheck = false;
2100 if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
2101 {
2102 if ( bAutoCorrect )
2103 {
2104 aCorrectedFormula.clear();
2105 aCorrectedSymbol.clear();
2106 }
2107 pArr->DelRPN();
2108 maArrIterator.Reset();
2109 pStack = nullptr;
2110 FormulaToken* pDataArray[ FORMULA_MAXTOKENS8192 + 1 ];
2111 // Code in some places refers to the last token as 'pCode - 1', which may
2112 // point before the first element if the expression is bad. So insert a dummy
2113 // node in that place which will make that token be nullptr.
2114 pDataArray[ 0 ] = nullptr;
2115 FormulaToken** pData = pDataArray + 1;
2116 pCode = pData;
2117 bool bWasForced = pArr->IsRecalcModeForced();
2118 if ( bWasForced && bAutoCorrect )
2119 aCorrectedFormula = "=";
2120 pArr->ClearRecalcMode();
2121 maArrIterator.Reset();
2122 eLastOp = ocOpen;
2123 pc = 0;
2124 NextToken();
2125 OpCode eOp = Expression();
2126 // Some trailing garbage that doesn't form an expression?
2127 if (eOp != ocStop)
2128 SetError( FormulaError::OperatorExpected);
2129 PostProcessCode();
2130
2131 FormulaError nErrorBeforePop = pArr->GetCodeError();
2132
2133 while( pStack )
2134 PopTokenArray();
2135 if( pc )
2136 {
2137 pArr->CreateNewRPNArrayFromData( pData, pc );
2138 if( needsRPNTokenCheck )
2139 pArr->CheckAllRPNTokens();
2140 }
2141
2142 // once an error, always an error
2143 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2144 pArr->SetCodeError( nErrorBeforePop);
2145
2146 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
2147 {
2148 pArr->DelRPN();
2149 maArrIterator.Reset();
2150 pArr->SetHyperLink( false);
2151 }
2152
2153 if ( bWasForced )
2154 pArr->SetRecalcModeForced();
2155 }
2156 if( nNumFmt == SvNumFormatType::UNDEFINED )
2157 nNumFmt = SvNumFormatType::NUMBER;
2158 return glSubTotal;
2159}
2160
2161void FormulaCompiler::PopTokenArray()
2162{
2163 if( !pStack )
2164 return;
2165
2166 FormulaArrayStack* p = pStack;
2167 pStack = p->pNext;
2168 // obtain special RecalcMode from SharedFormula
2169 if ( pArr->IsRecalcModeAlways() )
2170 p->pArr->SetExclusiveRecalcModeAlways();
2171 else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
2172 p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
2173 p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
2174 if ( pArr->IsHyperLink() ) // fdo 87534
2175 p->pArr->SetHyperLink( true );
2176 if( p->bTemp )
2177 delete pArr;
2178 pArr = p->pArr;
2179 maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
2180 maArrIterator.Jump(p->nIndex);
2181 mpLastToken = p->mpLastToken;
2182 delete p;
2183}
2184
2185void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
2186{
2187 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2188 CreateStringFromTokenArray( aBuffer );
2189 rFormula = aBuffer.makeStringAndClear();
2190}
2191
2192void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2193{
2194 rBuffer.setLength(0);
2195 if( !pArr->GetLen() )
2196 return;
2197
2198 FormulaTokenArray* pSaveArr = pArr;
2199 int nSaveIndex = maArrIterator.GetIndex();
2200 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2201 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2202 {
2203 // Scan token array for missing args and re-write if present.
2204 MissingConventionODF aConv( bODFF);
2205 if (pArr->NeedsPodfRewrite( aConv))
2206 {
2207 pArr = pArr->RewriteMissing( aConv );
2208 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2209 }
2210 }
2211 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2212 {
2213 // Scan token array for missing args and rewrite if present.
2214 MissingConventionOOXML aConv;
2215 if (pArr->NeedsOoxmlRewrite())
2216 {
2217 pArr = pArr->RewriteMissing( aConv );
2218 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2219 }
2220 }
2221
2222 // At least one character per token, plus some are references, some are
2223 // function names, some are numbers, ...
2224 rBuffer.ensureCapacity( pArr->GetLen() * 5 );
2225
2226 if ( pArr->IsRecalcModeForced() )
2227 rBuffer.append( '=');
2228 const FormulaToken* t = maArrIterator.First();
2229 while( t )
2230 t = CreateStringFromToken( rBuffer, t, true );
2231
2232 if (pSaveArr != pArr)
2233 {
2234 delete pArr;
2235 pArr = pSaveArr;
2236 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2237 maArrIterator.Jump(nSaveIndex);
2238 }
2239}
2240
2241const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2242{
2243 OUStringBuffer aBuffer;
2244 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2245 rFormula += aBuffer;
2246 return p;
2247}
2248
2249const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2250 bool bAllowArrAdvance )
2251{
2252 bool bNext = true;
2253 bool bSpaces = false;
2254 const FormulaToken* t = pTokenP;
2255 OpCode eOp = t->GetOpCode();
2256 if( eOp >= ocAnd && eOp <= ocOr )
2257 {
2258 // AND, OR infix?
2259 if ( bAllowArrAdvance )
2260 t = maArrIterator.Next();
2261 else
2262 t = maArrIterator.PeekNext();
2263 bNext = false;
2264 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2265 }
2266 if( bSpaces )
2267 rBuffer.append( ' ');
2268
2269 if( eOp == ocSpaces )
2270 {
2271 bool bWriteSpaces = true;
2272 if (mxSymbols->isODFF())
2273 {
2274 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2275 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2276 if (bIntersectionOp)
2277 {
2278 p = maArrIterator.PeekNextNoSpaces();
2279 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2280 }
2281 if (bIntersectionOp)
2282 {
2283 rBuffer.append( "!!");
2284 bWriteSpaces = false;
2285 }
2286 }
2287 else if (mxSymbols->isOOXML())
2288 {
2289 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2290 // shall separate a function-name from the left parenthesis (()
2291 // that follows it." and Excel even chokes on it.
2292 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2293 if (p && p->IsFunction())
2294 {
2295 p = maArrIterator.PeekNextNoSpaces();
2296 if (p && p->GetOpCode() == ocOpen)
2297 bWriteSpaces = false;
2298 }
2299 }
2300 if (bWriteSpaces)
2301 {
2302 // most times it's just one blank
2303 sal_uInt8 n = t->GetByte();
2304 for ( sal_uInt8 j=0; j<n; ++j )
2305 {
2306 rBuffer.append( ' ');
2307 }
2308 }
2309 }
2310 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2311 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2312 else if (eOp == ocIntersect)
2313 {
2314 // Nasty, ugly, horrific, terrifying...
2315 if (FormulaGrammar::isExcelSyntax( meGrammar))
2316 rBuffer.append(' ');
2317 else
2318 rBuffer.append( mxSymbols->getSymbol( eOp));
2319 }
2320 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2321 rBuffer.append( mxSymbols->getSymbol( eOp));
2322 else
2323 {
2324 SAL_WARN( "formula.core","unknown OpCode")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "unknown OpCode") == 1
) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("formula.core"
), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2324" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "unknown OpCode"), 0); } else { ::std
::ostringstream sal_detail_stream; sal_detail_stream <<
"unknown OpCode"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2324" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "unknown OpCode") == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2324" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "unknown OpCode"), 0); } else { ::std
::ostringstream sal_detail_stream; sal_detail_stream <<
"unknown OpCode"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2324" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
2325 rBuffer.append( GetNativeSymbol( ocErrName ));
2326 }
2327 if( bNext )
2328 {
2329 if (t->IsExternalRef())
2330 {
2331 CreateStringFromExternal( rBuffer, pTokenP);
2332 }
2333 else
2334 {
2335 switch( t->GetType() )
2336 {
2337 case svDouble:
2338 AppendDouble( rBuffer, t->GetDouble() );
2339 break;
2340
2341 case svString:
2342 if( eOp == ocBad || eOp == ocStringXML )
2343 rBuffer.append( t->GetString().getString());
2344 else
2345 AppendString( rBuffer, t->GetString().getString() );
2346 break;
2347 case svSingleRef:
2348 CreateStringFromSingleRef( rBuffer, t);
2349 break;
2350 case svDoubleRef:
2351 CreateStringFromDoubleRef( rBuffer, t);
2352 break;
2353 case svMatrix:
2354 case svMatrixCell:
2355 CreateStringFromMatrix( rBuffer, t );
2356 break;
2357
2358 case svIndex:
2359 CreateStringFromIndex( rBuffer, t );
2360 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2361 {
2362 // Suppress all TableRef related tokens, the resulting
2363 // range was written by CreateStringFromIndex().
2364 const FormulaToken* const p = maArrIterator.PeekNext();
2365 if (p && p->GetOpCode() == ocTableRefOpen)
2366 {
2367 int nLevel = 0;
2368 do
2369 {
2370 t = maArrIterator.Next();
2371 if (!t)
2372 break;
2373
2374 // Switch cases correspond with those in
2375 // ScCompiler::HandleTableRef()
2376 switch (t->GetOpCode())
2377 {
2378 case ocTableRefOpen:
2379 ++nLevel;
2380 break;
2381 case ocTableRefClose:
2382 --nLevel;
2383 break;
2384 case ocTableRefItemAll:
2385 case ocTableRefItemHeaders:
2386 case ocTableRefItemData:
2387 case ocTableRefItemTotals:
2388 case ocTableRefItemThisRow:
2389 case ocSep:
2390 case ocPush:
2391 case ocRange:
2392 case ocSpaces:
2393 break;
2394 default:
2395 nLevel = 0;
2396 bNext = false;
2397 }
2398 } while (nLevel);
2399 }
2400 }
2401 break;
2402 case svExternal:
2403 {
2404 // mapped or translated name of AddIns
2405 OUString aAddIn( t->GetExternal() );
2406 bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
2407 if (!bMapped && mxSymbols->hasExternals())
2408 {
2409 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2410 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2411 {
2412 aAddIn = (*iLook).second;
2413 bMapped = true;
2414 }
2415 }
2416 if (!bMapped && !mxSymbols->isEnglish())
2417 LocalizeString( aAddIn );
2418 rBuffer.append( aAddIn);
2419 }
2420 break;
2421 case svError:
2422 AppendErrorConstant( rBuffer, t->GetError());
2423 break;
2424 case svByte:
2425 case svJump:
2426 case svFAP:
2427 case svMissing:
2428 case svSep:
2429 break; // Opcodes
2430 default:
2431 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType())do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "formula.core")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType()) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2431" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType()), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2431" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType()) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2431" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType()), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "FormulaCompiler::GetStringFromToken: unknown token type "
<< t->GetType(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("formula.core"), ("/home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx"
":" "2431" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
2432 } // of switch
2433 }
2434 }
2435 if( bSpaces )
2436 rBuffer.append( ' ');
2437 if ( bAllowArrAdvance )
2438 {
2439 if( bNext )
2440 t = maArrIterator.Next();
2441 return t;
2442 }
2443 return pTokenP;
2444}
2445
2446
2447void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2448{
2449 if ( mxSymbols->isEnglish() )
2450 {
2451 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2452 rtl_math_StringFormat_Automatic,
2453 rtl_math_DecimalPlaces_Max, '.', true );
2454 }
2455 else
2456 {
2457 SvtSysLocale aSysLocale;
2458 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2459 rtl_math_StringFormat_Automatic,
2460 rtl_math_DecimalPlaces_Max,
2461 aSysLocale.GetLocaleDataPtr()->getNumDecimalSep()[0],
2462 true );
2463 }
2464}
2465
2466void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2467{
2468 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2469}
2470
2471void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2472{
2473 rBuffer.append( '"');
2474 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2475 rBuffer.append( rStr );
2476 else
2477 {
2478 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2479 rBuffer.append(aStr);
2480 }
2481 rBuffer.append( '"');
2482}
2483
2484bool FormulaCompiler::NeedsTableRefTransformation() const
2485{
2486 // Currently only UI representations and OOXML export use Table structured
2487 // references. Not defined in ODFF.
2488 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2489 // symbol is not defined there.
2490 return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
2491}
2492
2493void FormulaCompiler::UpdateSeparatorsNative(
2494 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2495{
2496 NonConstOpCodeMapPtr xSymbolsNative;
2497 lcl_fillNativeSymbols( xSymbolsNative);
2498 xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
2499 xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
2500 xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
2501}
2502
2503void FormulaCompiler::ResetNativeSymbols()
2504{
2505 NonConstOpCodeMapPtr xSymbolsNative;
2506 lcl_fillNativeSymbols( xSymbolsNative, true);
2507 lcl_fillNativeSymbols( xSymbolsNative);
2508}
2509
2510void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2511{
2512 NonConstOpCodeMapPtr xSymbolsNative;
2513 lcl_fillNativeSymbols( xSymbolsNative);
2514 xSymbolsNative->copyFrom( *xMap );
2515}
2516
2517
2518OpCode FormulaCompiler::NextToken()
2519{
2520 if( !GetToken() )
2521 return ocStop;
2522 OpCode eOp = mpToken->GetOpCode();
2523 // There must be an operator before a push
2524 if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2525 !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2526 (SC_OPCODE_START_BIN_OP50 <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP71)) )
2527 SetError( FormulaError::OperatorExpected);
2528 // Operator and Plus => operator
2529 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2530 (SC_OPCODE_START_BIN_OP50 <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP71)))
2531 eOp = NextToken();
2532 else
2533 {
2534 // Before an operator there must not be another operator, with the
2535 // exception of AND and OR.
2536 if ( eOp != ocAnd && eOp != ocOr &&
2537 (SC_OPCODE_START_BIN_OP50 <= eOp && eOp < SC_OPCODE_STOP_BIN_OP67 )
2538 && (eLastOp == ocOpen || eLastOp == ocSep ||
2539 (SC_OPCODE_START_BIN_OP50 <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP71)))
2540 {
2541 SetError( FormulaError::VariableExpected);
2542 if ( bAutoCorrect && !pStack )
2543 {
2544 if ( eOp == eLastOp || eLastOp == ocOpen )
2545 { // throw away duplicated operator
2546 aCorrectedSymbol.clear();
2547 bCorrected = true;
2548 }
2549 else
2550 {
2551 sal_Int32 nPos = aCorrectedFormula.getLength();
2552 if ( nPos )
2553 {
2554 nPos--;
2555 sal_Unicode c = aCorrectedFormula[ nPos ];
2556 switch ( eOp )
2557 { // swap operators
2558 case ocGreater:
2559 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2560 { // >= instead of =>
2561 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2562 OUString( mxSymbols->getSymbolChar(ocGreater) ) );
2563 aCorrectedSymbol = OUString(c);
2564 bCorrected = true;
2565 }
2566 break;
2567 case ocLess:
2568 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2569 { // <= instead of =<
2570 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2571 OUString( mxSymbols->getSymbolChar(ocLess) ) );
2572 aCorrectedSymbol = OUString(c);
2573 bCorrected = true;
2574 }
2575 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2576 { // <> instead of ><
2577 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2578 OUString( mxSymbols->getSymbolChar(ocLess) ) );
2579 aCorrectedSymbol = OUString(c);
2580 bCorrected = true;
2581 }
2582 break;
2583 case ocMul:
2584 if ( c == mxSymbols->getSymbolChar( ocSub) )
2585 { // *- instead of -*
2586 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2587 OUString( mxSymbols->getSymbolChar(ocMul) ) );
2588 aCorrectedSymbol = OUString(c);
2589 bCorrected = true;
2590 }
2591 break;
2592 case ocDiv:
2593 if ( c == mxSymbols->getSymbolChar( ocSub) )
2594 { // /- instead of -/
2595 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2596 OUString( mxSymbols->getSymbolChar(ocDiv) ) );
2597 aCorrectedSymbol = OUString(c);
2598 bCorrected = true;
2599 }
2600 break;
2601 default:
2602 ; // nothing
2603 }
2604 }
2605 }
2606 }
2607 }
2608 // Nasty, ugly, horrific, terrifying... significant whitespace...
2609 if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
2610 {
2611 // Fake an intersection op as last op for the next round, but at
2612 // least roughly check if it could make sense at all.
2613 FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
2614 if (pPrev && isPotentialRangeType( pPrev, false, false))
2615 {
2616 FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
2617 if (pNext && isPotentialRangeType( pNext, false, true))
2618 eLastOp = ocIntersect;
2619 else
2620 eLastOp = eOp;
2621 }
2622 else
2623 eLastOp = eOp;
2624 }
2625 else
2626 eLastOp = eOp;
2627 }
2628 return eOp;
2629}
2630
2631void FormulaCompiler::PutCode( FormulaTokenRef& p )
2632{
2633 if( pc >= FORMULA_MAXTOKENS8192 - 1 )
6
Assuming the condition is true
7
Taking true branch
2634 {
2635 if ( pc == FORMULA_MAXTOKENS8192 - 1 )
8
Assuming the condition is true
9
Taking true branch
2636 {
2637 p = new FormulaByteToken( ocStop );
2638 p->IncRef();
2639 *pCode++ = p.get();
10
Dereference of null pointer
2640 ++pc;
2641 }
2642 SetError( FormulaError::CodeOverflow);
2643 return;
2644 }
2645 if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
2646 return;
2647 ForceArrayOperator( p);
2648 p->IncRef();
2649 *pCode++ = p.get();
2650 pc++;
2651}
2652
2653
2654bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2655{
2656 return true;
2657}
2658
2659bool FormulaCompiler::HandleRange()
2660{
2661 return true;
2662}
2663
2664bool FormulaCompiler::HandleColRowName()
2665{
2666 return true;
2667}
2668
2669bool FormulaCompiler::HandleDbData()
2670{
2671 return true;
2672}
2673
2674bool FormulaCompiler::HandleTableRef()
2675{
2676 return true;
2677}
2678
2679void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2680{
2681}
2682
2683void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2684{
2685}
2686
2687void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2688{
2689}
2690
2691void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2692{
2693}
2694
2695void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2696{
2697}
2698
2699void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2700{
2701}
2702
2703formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
2704{
2705 return ParamClass::Unknown;
2706}
2707
2708void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
2709{
2710 if (rCurr->GetInForceArray() != ParamClass::Unknown)
2711 // Already set, unnecessary to evaluate again. This happens by calls to
2712 // CurrentFactor::operator=() while descending through Factor() and
2713 // then ascending back (and down and up, ...),
2714 // CheckSetForceArrayParameter() and later PutCode().
2715 return;
2716
2717 if (!(rCurr->GetOpCode() != ocPush && (rCurr->GetType() == svByte || rCurr->GetType() == svJump)))
2718 return;
2719
2720 if (!pCurrentFactorToken || (pCurrentFactorToken.get() == rCurr.get()))
2721 {
2722 if (!pCurrentFactorToken && mbMatrixFlag)
2723 {
2724 // An array/matrix formula acts as ForceArray on all top level
2725 // operators and function calls, so that can be inherited properly
2726 // below.
2727 rCurr->SetInForceArray( ParamClass::ForceArray);
2728 }
2729 return;
2730 }
2731
2732 // Inherited parameter class.
2733 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
2734 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
2735 {
2736 // ReferenceOrRefArray was set only if in ForceArray context already,
2737 // it is valid for the one function only to indicate the preferred
2738 // return type. Propagate as ForceArray if not another parameter
2739 // handling ReferenceOrRefArray.
2740 if (nCurrentFactorParam > 0
2741 && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
2742 == ParamClass::ReferenceOrRefArray))
2743 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
2744 else
2745 rCurr->SetInForceArray( ParamClass::ForceArray);
2746 return;
2747 }
2748 else if (eForceType == ParamClass::ReferenceOrForceArray)
2749 {
2750 // Inherit further only if the return class of the nested function is
2751 // not Reference. Else flag as suppressed.
2752 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) != ParamClass::Reference)
2753 rCurr->SetInForceArray( eForceType);
2754 else
2755 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
2756 return;
2757 }
2758
2759 if (nCurrentFactorParam <= 0)
2760 return;
2761
2762 // Actual current parameter's class.
2763 const formula::ParamClass eType = GetForceArrayParameter(
2764 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
2765 if (eType == ParamClass::ForceArray)
2766 rCurr->SetInForceArray( eType);
2767 else if (eType == ParamClass::ReferenceOrForceArray)
2768 {
2769 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) != ParamClass::Reference)
2770 rCurr->SetInForceArray( eType);
2771 else
2772 rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
2773 }
2774
2775 // Propagate a ForceArrayReturn to caller if the called function
2776 // returns one and the caller so far does not have a stronger array
2777 // mode set.
2778 if (pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
2779 && GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) == ParamClass::ForceArrayReturn)
2780 pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
2781}
2782
2783void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
2784{
2785 if (!pCurrentFactorToken)
2786 return;
2787
2788 nCurrentFactorParam = nParam + 1;
2789
2790 ForceArrayOperator( rCurr);
2791}
2792
2793void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
2794{
2795 if ( bAutoCorrect && !pStack )
2796 { // don't merge stacked subroutine code into entered formula
2797 aCorrectedFormula += aCorrectedSymbol;
2798 aCorrectedSymbol.clear();
2799 }
2800 FormulaArrayStack* p = new FormulaArrayStack;
2801 p->pNext = pStack;
2802 p->pArr = pArr;
2803 p->nIndex = maArrIterator.GetIndex();
2804 p->mpLastToken = mpLastToken;
2805 p->bTemp = bTemp;
2806 pStack = p;
2807 pArr = pa;
2808 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2809}
2810
2811} // namespace formula
2812
2813/* vim:set shiftwidth=4 softtabstop=4 expandtab: */