File: | home/maarten/src/libreoffice/core/formula/source/core/api/FormulaCompiler.cxx |
Warning: | line 1789, column 25 Value stored to 'bLimitOk' is never read |
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; |
Value stored to 'bLimitOk' is never read | |
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: */ |