File: | home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx |
Warning: | line 2639, column 22 Dereference of null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
40 | namespace formula | |||
41 | { | |||
42 | using namespace ::com::sun::star; | |||
43 | ||||
44 | static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" }; | |||
45 | ||||
46 | namespace { | |||
47 | ||||
48 | class FormulaCompilerRecursionGuard | |||
49 | { | |||
50 | private: | |||
51 | short& rRecursion; | |||
52 | public: | |||
53 | explicit FormulaCompilerRecursionGuard( short& rRec ) | |||
54 | : rRecursion( rRec ) { ++rRecursion; } | |||
55 | ~FormulaCompilerRecursionGuard() { --rRecursion; } | |||
56 | }; | |||
57 | ||||
58 | SvNumFormatType 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 | ||||
123 | void 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 | ||||
132 | void 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 | ||||
139 | void 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 | ||||
146 | CharClass* 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 | ||||
154 | class OpCodeList | |||
155 | { | |||
156 | public: | |||
157 | ||||
158 | OpCodeList(bool bLocalized, const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&, | |||
159 | FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE ); | |||
160 | ||||
161 | private: | |||
162 | bool getOpCodeString( OUString& rStr, sal_uInt16 nOp ); | |||
163 | void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass ); | |||
164 | ||||
165 | private: | |||
166 | FormulaCompiler::SeparatorType meSepType; | |||
167 | const std::pair<const char*, int>* mpSymbols; | |||
168 | bool mbLocalized; | |||
169 | }; | |||
170 | ||||
171 | OpCodeList::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 | ||||
199 | bool 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 | ||||
235 | void 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 | |||
254 | const 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 | ||||
267 | struct OpCodeMapData | |||
268 | { | |||
269 | FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap; | |||
270 | osl::Mutex maMtx; | |||
271 | }; | |||
272 | ||||
273 | ||||
274 | bool isPotentialRangeLeftOp( OpCode eOp ) | |||
275 | { | |||
276 | switch (eOp) | |||
277 | { | |||
278 | case ocClose: | |||
279 | return true; | |||
280 | default: | |||
281 | return false; | |||
282 | } | |||
283 | } | |||
284 | ||||
285 | bool 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 | ||||
297 | bool 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 | */ | |||
321 | bool 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 | ||||
351 | bool 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 | ||||
360 | bool 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 | ||||
370 | bool 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 | ||||
384 | void 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 | ||||
401 | void 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 | ||||
408 | uno::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 | ||||
446 | uno::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 | ||||
610 | void 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 | ||||
709 | FormulaCompiler::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 | ||||
732 | FormulaTokenArray FormulaCompiler::smDummyTokenArray; | |||
733 | ||||
734 | FormulaCompiler::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 | ||||
757 | FormulaCompiler::~FormulaCompiler() | |||
758 | { | |||
759 | } | |||
760 | ||||
761 | FormulaCompiler::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 | ||||
808 | OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const | |||
809 | { | |||
810 | return OUString(); | |||
811 | } | |||
812 | ||||
813 | FormulaCompiler::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 | ||||
844 | static 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 | ||||
867 | const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp ) | |||
868 | { | |||
869 | NonConstOpCodeMapPtr xSymbolsNative; | |||
870 | lcl_fillNativeSymbols( xSymbolsNative); | |||
871 | return xSymbolsNative->getSymbol( eOp ); | |||
872 | } | |||
873 | ||||
874 | sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp ) | |||
875 | { | |||
876 | return GetNativeSymbol(eOp)[0]; | |||
877 | } | |||
878 | ||||
879 | void FormulaCompiler::InitSymbolsNative() const | |||
880 | { | |||
881 | lcl_fillNativeSymbols( mxSymbolsNative); | |||
882 | } | |||
883 | ||||
884 | void 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 | ||||
893 | void 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 | ||||
902 | void 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 | ||||
912 | void 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 | ||||
921 | void 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 | ||||
937 | void 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 | ||||
947 | void 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 | ||||
965 | void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const | |||
966 | { | |||
967 | } | |||
968 | ||||
969 | void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const | |||
970 | { | |||
971 | } | |||
972 | ||||
973 | void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const | |||
974 | { | |||
975 | } | |||
976 | ||||
977 | OpCode 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 | ||||
986 | bool 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 | ||||
1016 | bool 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. | |||
1032 | bool 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 | ||||
1044 | void FormulaCompiler::fillAddInToken( | |||
1045 | ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/, | |||
1046 | bool /*_bIsEnglish*/) const | |||
1047 | { | |||
1048 | } | |||
1049 | ||||
1050 | bool 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 | ||||
1076 | void 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 | ||||
1090 | void 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 | ||||
1144 | FormulaError 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 | ||||
1194 | void FormulaCompiler::EnableJumpCommandReorder( bool bEnable ) | |||
1195 | { | |||
1196 | mbJumpCommandReorder = bEnable; | |||
1197 | } | |||
1198 | ||||
1199 | void FormulaCompiler::EnableStopOnError( bool bEnable ) | |||
1200 | { | |||
1201 | mbStopOnError = bEnable; | |||
1202 | } | |||
1203 | ||||
1204 | void 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 | ||||
1244 | bool 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 | |||
1367 | void 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 | ||||
1840 | void FormulaCompiler::RangeLine() | |||
1841 | { | |||
1842 | Factor(); | |||
1843 | while (mpToken->GetOpCode() == ocRange) | |||
1844 | { | |||
1845 | FormulaToken** pCode1 = pCode - 1; | |||
1846 | FormulaTokenRef p = mpToken; | |||
1847 | NextToken(); | |||
1848 | Factor(); | |||
1849 | FormulaToken** pCode2 = pCode - 1; | |||
1850 | if (!MergeRangeReference( pCode1, pCode2)) | |||
1851 | PutCode(p); | |||
1852 | } | |||
1853 | } | |||
1854 | ||||
1855 | void FormulaCompiler::IntersectionLine() | |||
1856 | { | |||
1857 | 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 | ||||
1888 | void 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 | ||||
1900 | void 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 | ||||
1921 | void 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 | ||||
1936 | void 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 | ||||
1956 | void 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 | ||||
1976 | void 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 | ||||
1996 | void 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 | ||||
2016 | void 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 | ||||
2036 | OpCode 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 | ||||
2066 | void FormulaCompiler::SetError( FormulaError /*nError*/ ) | |||
2067 | { | |||
2068 | } | |||
2069 | ||||
2070 | FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ ) | |||
2071 | { | |||
2072 | return FormulaTokenRef(); | |||
2073 | } | |||
2074 | ||||
2075 | bool 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 | ||||
2095 | bool 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 | ||||
2161 | void 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 | ||||
2185 | void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula ) | |||
2186 | { | |||
2187 | OUStringBuffer aBuffer( pArr->GetLen() * 5 ); | |||
2188 | CreateStringFromTokenArray( aBuffer ); | |||
2189 | rFormula = aBuffer.makeStringAndClear(); | |||
2190 | } | |||
2191 | ||||
2192 | void 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 | ||||
2241 | const 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 | ||||
2249 | const 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 | ||||
2447 | void 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 | ||||
2466 | void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const | |||
2467 | { | |||
2468 | rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) ); | |||
2469 | } | |||
2470 | ||||
2471 | void 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 | ||||
2484 | bool 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 | ||||
2493 | void 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 | ||||
2503 | void FormulaCompiler::ResetNativeSymbols() | |||
2504 | { | |||
2505 | NonConstOpCodeMapPtr xSymbolsNative; | |||
2506 | lcl_fillNativeSymbols( xSymbolsNative, true); | |||
2507 | lcl_fillNativeSymbols( xSymbolsNative); | |||
2508 | } | |||
2509 | ||||
2510 | void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap ) | |||
2511 | { | |||
2512 | NonConstOpCodeMapPtr xSymbolsNative; | |||
2513 | lcl_fillNativeSymbols( xSymbolsNative); | |||
2514 | xSymbolsNative->copyFrom( *xMap ); | |||
2515 | } | |||
2516 | ||||
2517 | ||||
2518 | OpCode 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 | ||||
2631 | void FormulaCompiler::PutCode( FormulaTokenRef& p ) | |||
2632 | { | |||
2633 | if( pc >= FORMULA_MAXTOKENS8192 - 1 ) | |||
2634 | { | |||
2635 | if ( pc == FORMULA_MAXTOKENS8192 - 1 ) | |||
2636 | { | |||
2637 | p = new FormulaByteToken( ocStop ); | |||
2638 | p->IncRef(); | |||
2639 | *pCode++ = p.get(); | |||
| ||||
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 | ||||
2654 | bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/) | |||
2655 | { | |||
2656 | return true; | |||
2657 | } | |||
2658 | ||||
2659 | bool FormulaCompiler::HandleRange() | |||
2660 | { | |||
2661 | return true; | |||
2662 | } | |||
2663 | ||||
2664 | bool FormulaCompiler::HandleColRowName() | |||
2665 | { | |||
2666 | return true; | |||
2667 | } | |||
2668 | ||||
2669 | bool FormulaCompiler::HandleDbData() | |||
2670 | { | |||
2671 | return true; | |||
2672 | } | |||
2673 | ||||
2674 | bool FormulaCompiler::HandleTableRef() | |||
2675 | { | |||
2676 | return true; | |||
2677 | } | |||
2678 | ||||
2679 | void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const | |||
2680 | { | |||
2681 | } | |||
2682 | ||||
2683 | void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const | |||
2684 | { | |||
2685 | } | |||
2686 | ||||
2687 | void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const | |||
2688 | { | |||
2689 | } | |||
2690 | ||||
2691 | void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const | |||
2692 | { | |||
2693 | } | |||
2694 | ||||
2695 | void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const | |||
2696 | { | |||
2697 | } | |||
2698 | ||||
2699 | void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const | |||
2700 | { | |||
2701 | } | |||
2702 | ||||
2703 | formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const | |||
2704 | { | |||
2705 | return ParamClass::Unknown; | |||
2706 | } | |||
2707 | ||||
2708 | void 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 | ||||
2783 | void 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 | ||||
2793 | void 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: */ |