Line data Source code
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 "formula/FormulaCompiler.hxx"
21 : #include "formula/errorcodes.hxx"
22 : #include "formula/token.hxx"
23 : #include "formula/tokenarray.hxx"
24 : #include "core_resource.hxx"
25 : #include "core_resource.hrc"
26 :
27 : #include "osl/mutex.hxx"
28 :
29 : #include <svl/zforlist.hxx>
30 : #include <tools/rc.hxx>
31 : #include <tools/rcid.h>
32 : #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
33 : #include <com/sun/star/sheet/FormulaMapGroup.hpp>
34 : #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
35 : #include <rtl/strbuf.hxx>
36 : #include <algorithm>
37 :
38 : namespace formula
39 : {
40 : using namespace ::com::sun::star;
41 :
42 : static const sal_Char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
43 :
44 : namespace {
45 :
46 : class FormulaCompilerRecursionGuard
47 : {
48 : private:
49 : short& rRecursion;
50 : public:
51 95140 : FormulaCompilerRecursionGuard( short& rRec )
52 95140 : : rRecursion( rRec ) { ++rRecursion; }
53 95140 : ~FormulaCompilerRecursionGuard() { --rRecursion; }
54 : };
55 :
56 5705 : short lcl_GetRetFormat( OpCode eOpCode )
57 : {
58 5705 : switch (eOpCode)
59 : {
60 : case ocEqual:
61 : case ocNotEqual:
62 : case ocLess:
63 : case ocGreater:
64 : case ocLessEqual:
65 : case ocGreaterEqual:
66 : case ocAnd:
67 : case ocOr:
68 : case ocXor:
69 : case ocNot:
70 : case ocTrue:
71 : case ocFalse:
72 : case ocIsEmpty:
73 : case ocIsString:
74 : case ocIsNonString:
75 : case ocIsLogical:
76 : case ocIsRef:
77 : case ocIsValue:
78 : case ocIsFormula:
79 : case ocIsNA:
80 : case ocIsErr:
81 : case ocIsError:
82 : case ocIsEven:
83 : case ocIsOdd:
84 : case ocExact:
85 195 : return css::util::NumberFormat::LOGICAL;
86 : case ocGetActDate:
87 : case ocGetDate:
88 : case ocEasterSunday :
89 4 : return css::util::NumberFormat::DATE;
90 : case ocGetActTime:
91 3 : return css::util::NumberFormat::DATETIME;
92 : case ocGetTime:
93 0 : return css::util::NumberFormat::TIME;
94 : case ocNPV:
95 : case ocPV:
96 : case ocSYD:
97 : case ocDDB:
98 : case ocDB:
99 : case ocVBD:
100 : case ocSLN:
101 : case ocPMT:
102 : case ocFV:
103 : case ocIpmt:
104 : case ocPpmt:
105 : case ocCumIpmt:
106 : case ocCumPrinc:
107 36 : return css::util::NumberFormat::CURRENCY;
108 : case ocRate:
109 : case ocIRR:
110 : case ocMIRR:
111 : case ocRRI:
112 : case ocEffective:
113 : case ocNominal:
114 : case ocPercentSign:
115 16 : return css::util::NumberFormat::PERCENT;
116 : default:
117 5451 : return css::util::NumberFormat::NUMBER;
118 : }
119 : }
120 :
121 188069 : inline void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
122 : const OUString* pTable, sal_uInt16 nOpCode )
123 : {
124 188069 : sheet::FormulaOpCodeMapEntry aEntry;
125 188069 : aEntry.Token.OpCode = nOpCode;
126 188069 : aEntry.Name = pTable[nOpCode];
127 188069 : rVec.push_back( aEntry);
128 188069 : }
129 :
130 938 : void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
131 : const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
132 : {
133 45493 : for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
134 44555 : lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
135 938 : }
136 :
137 1407 : void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
138 : const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
139 : {
140 8442 : for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
141 7035 : lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
142 1407 : }
143 :
144 153 : class OpCodeList : public Resource // temp object for resource
145 : {
146 : public:
147 :
148 : OpCodeList( sal_uInt16, FormulaCompiler::NonConstOpCodeMapPtr,
149 : FormulaCompiler::SeparatorType = FormulaCompiler::SEMICOLON_BASE );
150 :
151 : private:
152 : bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
153 : void putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp, const CharClass* pCharClass );
154 :
155 : private:
156 : FormulaCompiler::SeparatorType meSepType;
157 : };
158 :
159 153 : OpCodeList::OpCodeList( sal_uInt16 nRID, FormulaCompiler::NonConstOpCodeMapPtr xMap,
160 : FormulaCompiler::SeparatorType eSepType ) :
161 153 : Resource( ResId( nRID, *ResourceManager::getResManager()))
162 153 : , meSepType( eSepType)
163 : {
164 153 : SvtSysLocale aSysLocale;
165 153 : const CharClass* pCharClass = (xMap->isEnglish() ? NULL : aSysLocale.GetCharClassPtr());
166 153 : if (meSepType == FormulaCompiler::RESOURCE_BASE)
167 : {
168 24276 : for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
169 : {
170 24225 : putDefaultOpCode( xMap, i, pCharClass);
171 : }
172 : }
173 : else
174 : {
175 48552 : for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
176 : {
177 48450 : OUString aOpStr;
178 48450 : if ( getOpCodeString( aOpStr, i) )
179 306 : xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
180 : else
181 48144 : putDefaultOpCode( xMap, i, pCharClass);
182 48450 : }
183 : }
184 :
185 153 : FreeResource();
186 153 : }
187 :
188 48450 : bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
189 : {
190 48450 : switch (nOp)
191 : {
192 : case SC_OPCODE_SEP:
193 : {
194 102 : if (meSepType == FormulaCompiler::COMMA_BASE)
195 : {
196 0 : rStr = ",";
197 0 : return true;
198 : }
199 102 : else if (meSepType == FormulaCompiler::SEMICOLON_BASE)
200 : {
201 102 : rStr = ";";
202 102 : return true;
203 : }
204 : }
205 0 : break;
206 : case SC_OPCODE_ARRAY_COL_SEP:
207 : {
208 102 : if (meSepType == FormulaCompiler::COMMA_BASE)
209 : {
210 0 : rStr = ",";
211 0 : return true;
212 : }
213 102 : else if (meSepType == FormulaCompiler::SEMICOLON_BASE)
214 : {
215 102 : rStr = ";";
216 102 : return true;
217 : }
218 : }
219 0 : break;
220 : case SC_OPCODE_ARRAY_ROW_SEP:
221 : {
222 102 : if (meSepType == FormulaCompiler::COMMA_BASE)
223 : {
224 0 : rStr = ";";
225 0 : return true;
226 : }
227 102 : else if (meSepType == FormulaCompiler::SEMICOLON_BASE)
228 : {
229 102 : rStr = "|";
230 102 : return true;
231 : }
232 : }
233 0 : break;
234 : }
235 :
236 48144 : return false;
237 : }
238 :
239 72369 : void OpCodeList::putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp,
240 : const CharClass* pCharClass )
241 : {
242 72369 : ResId aRes( nOp, *ResourceManager::getResManager());
243 72369 : aRes.SetRT( RSC_STRING);
244 72369 : if (IsAvailableRes( aRes))
245 62485 : xMap->putOpCode( aRes.toString(), OpCode( nOp), pCharClass);
246 72369 : }
247 :
248 : // static
249 347 : const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
250 : {
251 347 : if ( !pStr )
252 0 : return NULL;
253 2582 : while ( *pStr )
254 : {
255 1888 : if ( *pStr == c )
256 0 : return pStr;
257 1888 : pStr++;
258 : }
259 347 : return NULL;
260 : }
261 :
262 304 : struct OpCodeMapData
263 : {
264 : FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
265 : osl::Mutex maMtx;
266 : };
267 :
268 :
269 : } // namespace
270 :
271 :
272 60428 : void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
273 : {
274 : // Different symbols may map to the same AddIn, but the same AddIn may not
275 : // map to different symbols, the first pair wins. Same symbol of course may
276 : // not map to different AddIns, again the first pair wins and also the
277 : // AddIn->symbol mapping is not inserted in other cases.
278 60428 : bool bOk = mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second;
279 : SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn);
280 60428 : if (bOk)
281 : {
282 60428 : bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
283 : // Failed insertion of the AddIn is ok for different symbols mapping to
284 : // the same AddIn. Make this INFO only.
285 : SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
286 : }
287 60428 : }
288 :
289 3156 : void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
290 : {
291 3156 : bool bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
292 3156 : if (bOk)
293 80 : mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn));
294 3156 : }
295 :
296 0 : uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
297 : const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
298 : {
299 0 : const sal_Int32 nLen = rNames.getLength();
300 0 : uno::Sequence< sheet::FormulaToken > aTokens( nLen);
301 0 : sheet::FormulaToken* pToken = aTokens.getArray();
302 0 : OUString const * pName = rNames.getConstArray();
303 0 : OUString const * const pStop = pName + nLen;
304 0 : for ( ; pName < pStop; ++pName, ++pToken)
305 : {
306 0 : OpCodeHashMap::const_iterator iLook( mpHashMap->find( *pName));
307 0 : if (iLook != mpHashMap->end())
308 0 : pToken->OpCode = (*iLook).second;
309 : else
310 : {
311 0 : OUString aIntName;
312 0 : if (hasExternals())
313 : {
314 0 : ExternalHashMap::const_iterator iExt( mpExternalHashMap->find( *pName));
315 0 : if (iExt != mpExternalHashMap->end())
316 0 : aIntName = (*iExt).second;
317 : // Check for existence not needed here, only name-mapping is of
318 : // interest.
319 : }
320 0 : if (aIntName.isEmpty())
321 0 : aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
322 0 : if (aIntName.isEmpty())
323 0 : pToken->OpCode = getOpCodeUnknown();
324 : else
325 : {
326 0 : pToken->OpCode = ocExternal;
327 0 : pToken->Data <<= aIntName;
328 0 : }
329 : }
330 : }
331 0 : return aTokens;
332 : }
333 :
334 2814 : uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
335 : const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
336 : {
337 : using namespace sheet;
338 :
339 : // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
340 : // we don't know in advance how many elements it will have we use a
341 : // temporary vector to add elements and then copy to Sequence :-(
342 2814 : ::std::vector< FormulaOpCodeMapEntry > aVec;
343 :
344 2814 : if (nGroups == FormulaMapGroup::SPECIAL)
345 : {
346 : // Use specific order, keep in sync with
347 : // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
348 : static const struct
349 : {
350 : sal_Int32 nOff;
351 : OpCode eOp;
352 : } aMap[] = {
353 : { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
354 : { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
355 : { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
356 : { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
357 : { FormulaMapGroupSpecialOffset::NAME , ocName } ,
358 : { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
359 : { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
360 : { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
361 : { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
362 : { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
363 : { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
364 : /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
365 : { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
366 : { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName }
367 : };
368 469 : const size_t nCount = sizeof(aMap)/sizeof(aMap[0]);
369 : // Preallocate vector elements.
370 469 : if (aVec.size() < nCount)
371 : {
372 469 : FormulaOpCodeMapEntry aEntry;
373 469 : aEntry.Token.OpCode = getOpCodeUnknown();
374 469 : aVec.resize( nCount, aEntry);
375 : } // if (aVec.size() < nCount)
376 :
377 469 : FormulaOpCodeMapEntry aEntry;
378 6566 : for (size_t i=0; i < nCount; ++i)
379 : {
380 6097 : size_t nIndex = static_cast< size_t >( aMap[i].nOff );
381 6097 : if (aVec.size() <= nIndex)
382 : {
383 : // The offsets really should be aligned with the size, so if
384 : // the vector was preallocated above this code to resize it is
385 : // just a measure in case the table isn't in sync with the API,
386 : // usually it isn't executed.
387 0 : aEntry.Token.OpCode = getOpCodeUnknown();
388 0 : aVec.resize( nIndex + 1, aEntry );
389 : }
390 6097 : aEntry.Token.OpCode = aMap[i].eOp;
391 6097 : aVec[nIndex] = aEntry;
392 469 : }
393 : }
394 : else
395 : {
396 : /* FIXME: Once we support error constants in formulas we'll need a map
397 : * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
398 : * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
399 :
400 : // Anything else but SPECIAL.
401 2345 : if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
402 : {
403 : static const sal_uInt16 aOpCodes[] = {
404 : SC_OPCODE_OPEN,
405 : SC_OPCODE_CLOSE,
406 : SC_OPCODE_SEP,
407 : };
408 469 : lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
409 : }
410 2345 : if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
411 : {
412 : static const sal_uInt16 aOpCodes[] = {
413 : SC_OPCODE_ARRAY_OPEN,
414 : SC_OPCODE_ARRAY_CLOSE,
415 : SC_OPCODE_ARRAY_ROW_SEP,
416 : SC_OPCODE_ARRAY_COL_SEP
417 : };
418 469 : lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
419 : }
420 2345 : if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
421 : {
422 : // Due to the nature of the percent operator following its operand
423 : // it isn't sorted into unary operators for compiler interna.
424 469 : lclPushOpCodeMapEntry( aVec, mpTable, ocPercentSign );
425 : // "+" can be used as unary operator too, push only if binary group is not set
426 469 : if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
427 469 : lclPushOpCodeMapEntry( aVec, mpTable, ocAdd );
428 : // regular unary operators
429 1876 : for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
430 : {
431 1407 : switch (nOp)
432 : {
433 : // NOT and NEG in fact are functions but for legacy reasons
434 : // are sorted into unary operators for compiler interna.
435 : case SC_OPCODE_NOT :
436 : case SC_OPCODE_NEG :
437 938 : break; // nothing,
438 : default:
439 469 : lclPushOpCodeMapEntry( aVec, mpTable, nOp );
440 : }
441 : }
442 : }
443 2345 : if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
444 : {
445 8442 : for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
446 : {
447 7973 : switch (nOp)
448 : {
449 : // AND and OR in fact are functions but for legacy reasons
450 : // are sorted into binary operators for compiler interna.
451 : case SC_OPCODE_AND :
452 : case SC_OPCODE_OR :
453 938 : break; // nothing,
454 : default:
455 7035 : lclPushOpCodeMapEntry( aVec, mpTable, nOp );
456 : }
457 : }
458 : }
459 2345 : if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
460 : {
461 : // Function names are not consecutive, skip the gaps between
462 : // functions with no parameter, functions with 1 parameter
463 : lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_NO_PAR,
464 469 : ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
465 : lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_1_PAR,
466 469 : ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
467 : // Additional functions not within range of functions.
468 : static const sal_uInt16 aOpCodes[] = {
469 : SC_OPCODE_IF,
470 : SC_OPCODE_IF_ERROR,
471 : SC_OPCODE_IF_NA,
472 : SC_OPCODE_CHOOSE,
473 : SC_OPCODE_AND,
474 : SC_OPCODE_OR,
475 : SC_OPCODE_NOT,
476 : SC_OPCODE_NEG
477 : };
478 469 : lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
479 : // functions with 2 or more parameters.
480 128975 : for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
481 : {
482 128506 : switch (nOp)
483 : {
484 : // NO_NAME is in SPECIAL.
485 : case SC_OPCODE_NO_NAME :
486 469 : break; // nothing,
487 : default:
488 128037 : lclPushOpCodeMapEntry( aVec, mpTable, nOp );
489 : }
490 : }
491 : // If AddIn functions are present in this mapping, use them, and only those.
492 469 : if (hasExternals())
493 : {
494 51590 : for (ExternalHashMap::const_iterator it( mpExternalHashMap->begin());it != mpExternalHashMap->end(); ++it)
495 : {
496 51121 : FormulaOpCodeMapEntry aEntry;
497 51121 : aEntry.Name = (*it).first;
498 51121 : aEntry.Token.Data <<= OUString( (*it).second);
499 51121 : aEntry.Token.OpCode = ocExternal;
500 51121 : aVec.push_back( aEntry);
501 51121 : }
502 : }
503 : else
504 : {
505 0 : rCompiler.fillAddInToken( aVec, isEnglish());
506 : }
507 : }
508 : }
509 2814 : return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
510 : }
511 :
512 :
513 249841 : void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
514 : {
515 : DBG_ASSERT( 0 < eOp && sal_uInt16(eOp) < mnSymbols, "OpCodeMap::putOpCode: OpCode out of range");
516 249841 : if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
517 : {
518 : SAL_WARN_IF( !(mpTable[eOp].isEmpty() || (mpTable[eOp] == rStr) ||
519 : (eOp == ocCurrency) || (eOp == ocSep) || (eOp == ocArrayColSep) ||
520 : (eOp == ocArrayRowSep)), "formula.core",
521 : "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
522 : << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
523 : << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
524 : // Case preserving opcode -> string, upper string -> opcode
525 249841 : mpTable[eOp] = rStr;
526 249841 : OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
527 249841 : mpHashMap->insert( OpCodeHashMap::value_type( aUpper, eOp));
528 : }
529 249841 : }
530 :
531 : // class FormulaCompiler
532 :
533 11970 : FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr )
534 : :
535 : pArr( &rArr ),
536 : pCode( NULL ),
537 : pStack( NULL ),
538 : eLastOp( ocPush ),
539 : nRecursion( 0 ),
540 : nNumFmt( css::util::NumberFormat::UNDEFINED ),
541 : pc( 0 ),
542 : meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
543 : bAutoCorrect( false ),
544 : bCorrected( false ),
545 : glSubTotal( false ),
546 : mbJumpCommandReorder(true),
547 11970 : mbStopOnError(true)
548 : {
549 11970 : }
550 :
551 32905 : FormulaCompiler::FormulaCompiler()
552 : :
553 : pArr( NULL ),
554 : pCode( NULL ),
555 : pStack( NULL ),
556 : eLastOp( ocPush ),
557 : nRecursion(0),
558 : nNumFmt( css::util::NumberFormat::UNDEFINED ),
559 : pc( 0 ),
560 : meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
561 : bAutoCorrect( false ),
562 : bCorrected( false ),
563 : glSubTotal( false ),
564 : mbJumpCommandReorder(true),
565 32905 : mbStopOnError(true)
566 : {
567 32905 : }
568 :
569 44876 : FormulaCompiler::~FormulaCompiler()
570 : {
571 44876 : }
572 :
573 48516 : FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
574 : {
575 48516 : FormulaCompiler::OpCodeMapPtr xMap;
576 : using namespace sheet;
577 48516 : switch (nLanguage)
578 : {
579 : case FormulaLanguage::ODFF :
580 8107 : if (!mxSymbolsODFF)
581 5762 : InitSymbolsODFF();
582 8107 : xMap = mxSymbolsODFF;
583 8107 : break;
584 : case FormulaLanguage::ODF_11 :
585 350 : if (!mxSymbolsPODF)
586 350 : InitSymbolsPODF();
587 350 : xMap = mxSymbolsPODF;
588 350 : break;
589 : case FormulaLanguage::ENGLISH :
590 273 : if (!mxSymbolsEnglish)
591 273 : InitSymbolsEnglish();
592 273 : xMap = mxSymbolsEnglish;
593 273 : break;
594 : case FormulaLanguage::NATIVE :
595 38155 : if (!mxSymbolsNative)
596 37949 : InitSymbolsNative();
597 38155 : xMap = mxSymbolsNative;
598 38155 : break;
599 : case FormulaLanguage::XL_ENGLISH:
600 306 : if (!mxSymbolsEnglishXL)
601 306 : InitSymbolsEnglishXL();
602 306 : xMap = mxSymbolsEnglishXL;
603 306 : break;
604 : case FormulaLanguage::OOXML:
605 1325 : if (!mxSymbolsOOXML)
606 1325 : InitSymbolsOOXML();
607 1325 : xMap = mxSymbolsOOXML;
608 1325 : break;
609 : default:
610 : ; // nothing, NULL map returned
611 : }
612 48516 : return xMap;
613 : }
614 :
615 0 : OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
616 : {
617 0 : return OUString();
618 : }
619 :
620 469 : FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
621 : const uno::Sequence<
622 : const sheet::FormulaOpCodeMapEntry > & rMapping,
623 : bool bEnglish )
624 : {
625 : using sheet::FormulaOpCodeMapEntry;
626 : // Filter / API maps are never Core
627 : NonConstOpCodeMapPtr xMap( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, false,
628 : FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
629 469 : FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED)));
630 938 : SvtSysLocale aSysLocale;
631 469 : const CharClass* pCharClass = (xMap->isEnglish() ? NULL : aSysLocale.GetCharClassPtr());
632 469 : FormulaOpCodeMapEntry const * pArr2 = rMapping.getConstArray();
633 469 : FormulaOpCodeMapEntry const * const pStop = pArr2 + rMapping.getLength();
634 239633 : for ( ; pArr2 < pStop; ++pArr2)
635 : {
636 239164 : OpCode eOp = OpCode(pArr2->Token.OpCode);
637 239164 : if (eOp != ocExternal)
638 184731 : xMap->putOpCode( pArr2->Name, eOp, pCharClass);
639 : else
640 : {
641 54433 : OUString aExternalName;
642 54433 : if (pArr2->Token.Data >>= aExternalName)
643 54433 : xMap->putExternal( pArr2->Name, aExternalName);
644 : else
645 : {
646 : SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
647 54433 : }
648 : }
649 : }
650 938 : return xMap;
651 : }
652 :
653 70822 : void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, bool bDestroy = false )
654 : {
655 70822 : static OpCodeMapData aSymbolMap;
656 70822 : osl::MutexGuard aGuard(&aSymbolMap.maMtx);
657 :
658 70822 : if ( bDestroy )
659 : {
660 49 : aSymbolMap.mxSymbolMap.reset();
661 : }
662 70773 : else if (!aSymbolMap.mxSymbolMap)
663 : {
664 : // Core
665 : aSymbolMap.mxSymbolMap.reset(
666 : new FormulaCompiler::OpCodeMap(
667 50 : SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI));
668 50 : OModuleClient aModuleClient;
669 50 : OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
670 : // No AddInMap for native core mapping.
671 : }
672 :
673 70822 : xMap = aSymbolMap.mxSymbolMap;
674 70822 : }
675 :
676 32308 : const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
677 : {
678 32308 : NonConstOpCodeMapPtr xSymbolsNative;
679 32308 : lcl_fillNativeSymbols( xSymbolsNative);
680 32308 : return xSymbolsNative->getSymbol( eOp );
681 : }
682 :
683 29588 : sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
684 : {
685 29588 : return GetNativeSymbol(eOp)[0];
686 : }
687 :
688 37949 : void FormulaCompiler::InitSymbolsNative() const
689 : {
690 37949 : lcl_fillNativeSymbols( mxSymbolsNative);
691 37949 : }
692 :
693 273 : void FormulaCompiler::InitSymbolsEnglish() const
694 : {
695 273 : static OpCodeMapData aMap;
696 273 : osl::MutexGuard aGuard(&aMap.maMtx);
697 273 : if (!aMap.mxSymbolMap)
698 49 : loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
699 273 : mxSymbolsEnglish = aMap.mxSymbolMap;
700 273 : }
701 :
702 350 : void FormulaCompiler::InitSymbolsPODF() const
703 : {
704 350 : static OpCodeMapData aMap;
705 350 : osl::MutexGuard aGuard(&aMap.maMtx);
706 350 : if (!aMap.mxSymbolMap)
707 13 : loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, RESOURCE_BASE);
708 350 : mxSymbolsPODF = aMap.mxSymbolMap;
709 350 : }
710 :
711 5762 : void FormulaCompiler::InitSymbolsODFF() const
712 : {
713 5762 : static OpCodeMapData aMap;
714 5762 : osl::MutexGuard aGuard(&aMap.maMtx);
715 5762 : if (!aMap.mxSymbolMap)
716 35 : loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, RESOURCE_BASE);
717 5762 : mxSymbolsODFF = aMap.mxSymbolMap;
718 5762 : }
719 :
720 306 : void FormulaCompiler::InitSymbolsEnglishXL() const
721 : {
722 306 : static OpCodeMapData aMap;
723 306 : osl::MutexGuard aGuard(&aMap.maMtx);
724 306 : if (!aMap.mxSymbolMap)
725 3 : loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
726 306 : mxSymbolsEnglishXL = aMap.mxSymbolMap;
727 :
728 : // TODO: For now, just replace the separators to the Excel English
729 : // variants. Later, if we want to properly map Excel functions with Calc
730 : // functions, we'll need to do a little more work here.
731 306 : mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, NULL);
732 306 : mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, NULL);
733 306 : mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, NULL);
734 306 : }
735 :
736 1325 : void FormulaCompiler::InitSymbolsOOXML() const
737 : {
738 1325 : static OpCodeMapData aMap;
739 1325 : osl::MutexGuard aGuard(&aMap.maMtx);
740 1325 : if (!aMap.mxSymbolMap)
741 3 : loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, RESOURCE_BASE);
742 1325 : mxSymbolsOOXML = aMap.mxSymbolMap;
743 1325 : }
744 :
745 :
746 103 : void FormulaCompiler::loadSymbols( sal_uInt16 nSymbols, FormulaGrammar::Grammar eGrammar,
747 : NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
748 : {
749 103 : if ( !rxMap.get() )
750 : {
751 : // not Core
752 103 : rxMap.reset( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar ));
753 103 : OModuleClient aModuleClient;
754 206 : OpCodeList aOpCodeList( nSymbols, rxMap, eSepType);
755 :
756 103 : fillFromAddInMap( rxMap, eGrammar);
757 : // Fill from collection for AddIns not already present.
758 103 : if ( FormulaGrammar::GRAM_ENGLISH != eGrammar )
759 51 : fillFromAddInCollectionUpperName( rxMap);
760 : else
761 155 : fillFromAddInCollectionEnglishName( rxMap);
762 : }
763 103 : }
764 :
765 0 : void FormulaCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr /*xMap */) const
766 : {
767 0 : }
768 :
769 48 : void FormulaCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr /*xMap */) const
770 : {
771 48 : }
772 :
773 48 : void FormulaCompiler::fillFromAddInMap( NonConstOpCodeMapPtr /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
774 : {
775 48 : }
776 :
777 0 : OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
778 : {
779 0 : FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
780 :
781 0 : formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap()->find( rName ) );
782 0 : bool bFound = (iLook != xMap->getHashMap()->end());
783 0 : return bFound ? (*iLook).second : OpCode(ocNone);
784 : }
785 :
786 24707 : bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
787 : {
788 24707 : bool bRet = false;
789 24707 : switch (eOp)
790 : {
791 : // no parameters:
792 : case ocRandom:
793 : case ocGetActDate:
794 : case ocGetActTime:
795 : // one parameter:
796 : case ocFormula:
797 : case ocInfo:
798 : // more than one parameters:
799 : // ocIndirect/ocIndirectXL otherwise would have to do
800 : // StopListening and StartListening on a reference for every
801 : // interpreted value.
802 : case ocIndirect:
803 : case ocIndirectXL:
804 : // ocOffset results in indirect references.
805 : case ocOffset:
806 : // ocDebugVar shows internal value that may change as the internal state changes.
807 : case ocDebugVar:
808 101 : bRet = true;
809 101 : break;
810 : default:
811 24606 : bRet = false;
812 24606 : break;
813 : }
814 24707 : return bRet;
815 : }
816 :
817 1611 : bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
818 : {
819 1611 : switch (eOp)
820 : {
821 : case ocIf:
822 : case ocIfError:
823 : case ocIfNA:
824 : case ocChoose:
825 48 : return true;
826 : default:
827 : ;
828 : }
829 1563 : return false;
830 : }
831 :
832 : // Remove quotes, escaped quotes are unescaped.
833 16 : bool FormulaCompiler::DeQuote( OUString& rStr )
834 : {
835 16 : sal_Int32 nLen = rStr.getLength();
836 16 : if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
837 : {
838 0 : rStr = rStr.copy( 1, nLen-2 );
839 0 : rStr = rStr.replaceAll( "\\\'", "\'" );
840 0 : return true;
841 : }
842 16 : return false;
843 : }
844 :
845 0 : void FormulaCompiler::fillAddInToken(
846 : ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
847 : bool /*_bIsEnglish*/) const
848 : {
849 0 : }
850 :
851 0 : bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
852 : {
853 0 : switch (eOpCode)
854 : {
855 : case ocDde :
856 : case ocGrowth :
857 : case ocTrend :
858 : case ocLogest :
859 : case ocLinest :
860 : case ocFrequency :
861 : case ocMatTrans :
862 : case ocMatMult :
863 : case ocMatInv :
864 : case ocMatrixUnit :
865 : case ocModalValue_Multi :
866 0 : return true;
867 : default:
868 : {
869 : // added to avoid warnings
870 : }
871 : }
872 0 : return false;
873 : }
874 :
875 :
876 1244 : FormulaCompiler::OpCodeMap::~OpCodeMap()
877 : {
878 622 : delete mpReverseExternalHashMap;
879 622 : delete mpExternalHashMap;
880 622 : delete [] mpTable;
881 622 : delete mpHashMap;
882 1244 : }
883 :
884 0 : void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
885 : {
886 : SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
887 : "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
888 : << " '" << mpTable[eOp] << "' with empty name!");
889 0 : if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
890 0 : mpHashMap->insert( OpCodeHashMap::value_type( mpTable[eOp], eOp));
891 : else
892 : {
893 0 : mpTable[eOp] = rSymbol;
894 0 : mpHashMap->insert( OpCodeHashMap::value_type( rSymbol, eOp));
895 : }
896 0 : }
897 :
898 0 : void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r, bool bOverrideKnownBad )
899 : {
900 0 : delete mpHashMap;
901 0 : mpHashMap = new OpCodeHashMap( mnSymbols);
902 :
903 0 : sal_uInt16 n = r.getSymbolCount();
904 : SAL_WARN_IF( n != mnSymbols, "formula.core",
905 : "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
906 0 : if (n > mnSymbols)
907 0 : n = mnSymbols;
908 :
909 : // OpCode 0 (ocPush) should never be in a map.
910 : SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
911 : "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
912 : << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
913 :
914 : // For bOverrideKnownBad when copying from the English core map (ODF 1.1
915 : // and API) to the native map (UI "use English function names") replace the
916 : // known bad legacy function names with correct ones.
917 0 : if (bOverrideKnownBad && r.mbCore &&
918 0 : FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
919 0 : FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
920 : {
921 0 : for (sal_uInt16 i = 1; i < n; ++i)
922 : {
923 0 : OUString aSymbol;
924 0 : OpCode eOp = OpCode(i);
925 0 : switch (eOp)
926 : {
927 : case ocRRI:
928 0 : aSymbol = "RRI";
929 0 : break;
930 : case ocTableOp:
931 0 : aSymbol = "MULTIPLE.OPERATIONS";
932 0 : break;
933 : default:
934 0 : aSymbol = r.mpTable[i];
935 : }
936 0 : putCopyOpCode( aSymbol, eOp);
937 0 : }
938 : }
939 : else
940 : {
941 0 : for (sal_uInt16 i = 1; i < n; ++i)
942 : {
943 0 : OpCode eOp = OpCode(i);
944 0 : const OUString& rSymbol = r.mpTable[i];
945 0 : putCopyOpCode( rSymbol, eOp);
946 : }
947 : }
948 :
949 : // TODO: maybe copy the external maps too?
950 0 : }
951 :
952 :
953 37 : sal_uInt16 FormulaCompiler::GetErrorConstant( const OUString& rName ) const
954 : {
955 37 : sal_uInt16 nError = 0;
956 37 : OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
957 37 : if (iLook != mxSymbols->getHashMap()->end())
958 : {
959 37 : switch ((*iLook).second)
960 : {
961 : // Not all may make sense in a formula, but these we know as
962 : // opcodes.
963 : case ocErrNull:
964 0 : nError = errNoCode;
965 0 : break;
966 : case ocErrDivZero:
967 4 : nError = errDivisionByZero;
968 4 : break;
969 : case ocErrValue:
970 0 : nError = errNoValue;
971 0 : break;
972 : case ocErrRef:
973 23 : nError = errNoRef;
974 23 : break;
975 : case ocErrName:
976 0 : nError = errNoName;
977 0 : break;
978 : case ocErrNum:
979 0 : nError = errIllegalFPOperation;
980 0 : break;
981 : case ocErrNA:
982 10 : nError = NOTAVAILABLE;
983 10 : break;
984 : default:
985 : ; // nothing
986 : }
987 : }
988 37 : return nError;
989 : }
990 :
991 454 : void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
992 : {
993 454 : mbJumpCommandReorder = bEnable;
994 454 : }
995 :
996 453 : void FormulaCompiler::EnableStopOnError( bool bEnable )
997 : {
998 453 : mbStopOnError = bEnable;
999 453 : }
1000 :
1001 0 : void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, sal_uInt16 nError ) const
1002 : {
1003 : OpCode eOp;
1004 0 : switch (nError)
1005 : {
1006 : default:
1007 : case errNoCode:
1008 0 : eOp = ocErrNull;
1009 0 : break;
1010 : case errDivisionByZero:
1011 0 : eOp = ocErrDivZero;
1012 0 : break;
1013 : case errNoValue:
1014 0 : eOp = ocErrValue;
1015 0 : break;
1016 : case errNoRef:
1017 0 : eOp = ocErrRef;
1018 0 : break;
1019 : case errNoName:
1020 0 : eOp = ocErrName;
1021 0 : break;
1022 : case errIllegalFPOperation:
1023 0 : eOp = ocErrNum;
1024 0 : break;
1025 : case NOTAVAILABLE:
1026 0 : eOp = ocErrNA;
1027 0 : break;
1028 : }
1029 0 : rBuffer.append( mxSymbols->getSymbol( eOp));
1030 0 : }
1031 :
1032 :
1033 938 : sal_Int32 FormulaCompiler::OpCodeMap::getOpCodeUnknown()
1034 : {
1035 : static const sal_Int32 kOpCodeUnknown = -1;
1036 938 : return kOpCodeUnknown;
1037 : }
1038 :
1039 69989 : bool FormulaCompiler::GetToken()
1040 : {
1041 : static const short nRecursionMax = 42;
1042 69989 : FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1043 69989 : if ( nRecursion > nRecursionMax )
1044 : {
1045 0 : SetError( errStackOverflow );
1046 0 : mpToken = new FormulaByteToken( ocStop );
1047 0 : return false;
1048 : }
1049 69989 : if ( bAutoCorrect && !pStack )
1050 : { // don't merge stacked subroutine code into entered formula
1051 0 : aCorrectedFormula += aCorrectedSymbol;
1052 0 : aCorrectedSymbol.clear();
1053 : }
1054 69989 : bool bStop = false;
1055 69989 : if (pArr->GetCodeError() && mbStopOnError)
1056 0 : bStop = true;
1057 : else
1058 : {
1059 : short nWasColRowName;
1060 69989 : if ( pArr->nIndex
1061 69989 : && pArr->pCode[ pArr->nIndex-1 ]->GetOpCode() == ocColRowName )
1062 0 : nWasColRowName = 1;
1063 : else
1064 69989 : nWasColRowName = 0;
1065 69989 : mpToken = pArr->Next();
1066 140429 : while( mpToken && mpToken->GetOpCode() == ocSpaces )
1067 : {
1068 451 : if ( nWasColRowName )
1069 0 : nWasColRowName++;
1070 451 : if ( bAutoCorrect && !pStack )
1071 0 : CreateStringFromToken( aCorrectedFormula, mpToken.get(), false );
1072 451 : mpToken = pArr->Next();
1073 : }
1074 69989 : if ( bAutoCorrect && !pStack && mpToken )
1075 0 : CreateStringFromToken( aCorrectedSymbol, mpToken.get(), false );
1076 69989 : if( !mpToken )
1077 : {
1078 9013 : if( pStack )
1079 : {
1080 499 : PopTokenArray();
1081 499 : return GetToken();
1082 : }
1083 : else
1084 8514 : bStop = true;
1085 : }
1086 : else
1087 : {
1088 60976 : if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1089 : { // convert an ocSpaces to ocIntersect in RPN
1090 0 : mpToken = new FormulaByteToken( ocIntersect );
1091 0 : pArr->nIndex--; // we advanced to the second ocColRowName, step back
1092 : }
1093 : }
1094 : }
1095 69490 : if( bStop )
1096 : {
1097 8514 : mpToken = new FormulaByteToken( ocStop );
1098 8514 : return false;
1099 : }
1100 121932 : if ( mpToken->GetOpCode() == ocSubTotal ||
1101 60956 : mpToken->GetOpCode() == ocAggregate )
1102 441 : glSubTotal = true;
1103 60535 : else if ( mpToken->IsExternalRef() )
1104 : {
1105 199 : return HandleExternalReference(*mpToken);
1106 : }
1107 60336 : else if( mpToken->GetOpCode() == ocName )
1108 : {
1109 431 : return HandleRange();
1110 : }
1111 59905 : else if( mpToken->GetOpCode() == ocColRowName )
1112 : {
1113 0 : return HandleColRowName();
1114 : }
1115 59905 : else if( mpToken->GetOpCode() == ocDBArea )
1116 : {
1117 12 : return HandleDbData();
1118 : }
1119 59893 : else if( mpToken->GetOpCode() == ocTableRef )
1120 : {
1121 0 : return HandleTableRef();
1122 : }
1123 60334 : return true;
1124 : }
1125 :
1126 :
1127 : // RPN creation by recursion
1128 31764 : void FormulaCompiler::Factor()
1129 : {
1130 31764 : if (pArr->GetCodeError() && mbStopOnError)
1131 31764 : return;
1132 :
1133 31764 : CurrentFactor pFacToken( this );
1134 :
1135 31764 : OpCode eOp = mpToken->GetOpCode();
1136 31764 : if( eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef ||
1137 9404 : eOp == ocDBArea || eOp == ocTableRef
1138 9404 : || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocDBArea)
1139 1 : || (eOp == ocTableRef) || (eOp == ocColRowName) || (eOp == ocBad)))
1140 : )
1141 : {
1142 22360 : PutCode( mpToken );
1143 22360 : eOp = NextToken();
1144 44720 : if( eOp == ocOpen )
1145 : {
1146 : // PUSH( is an error that may be caused by an unknown function.
1147 : SetError(
1148 0 : ( mpToken->GetType() == svString
1149 0 : || mpToken->GetType() == svSingleRef )
1150 0 : ? errNoName : errOperatorExpected );
1151 0 : if ( bAutoCorrect && !pStack )
1152 : { // assume multiplication
1153 0 : aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1154 0 : bCorrected = true;
1155 0 : NextToken();
1156 0 : eOp = Expression();
1157 0 : if( eOp != ocClose )
1158 0 : SetError( errPairExpected);
1159 : else
1160 0 : eOp = NextToken();
1161 : }
1162 : }
1163 : }
1164 9404 : else if( eOp == ocOpen )
1165 : {
1166 1039 : NextToken();
1167 1039 : eOp = Expression();
1168 2078 : while ((eOp == ocSep) && (!pArr->GetCodeError() || !mbStopOnError))
1169 : { // range list (A1;A2) converted to (A1~A2)
1170 0 : pFacToken = mpToken;
1171 0 : NextToken();
1172 0 : eOp = Expression();
1173 : // Do not ignore error here, regardless of bIgnoreErrors, otherwise
1174 : // errors like =(1;) would also result in display of =(1~)
1175 0 : if (!pArr->GetCodeError())
1176 : {
1177 0 : pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1178 0 : PutCode( pFacToken);
1179 : }
1180 : }
1181 1039 : if (eOp != ocClose)
1182 0 : SetError( errPairExpected);
1183 : else
1184 1039 : eOp = NextToken();
1185 : }
1186 : else
1187 : {
1188 8365 : if( nNumFmt == css::util::NumberFormat::UNDEFINED )
1189 5705 : nNumFmt = lcl_GetRetFormat( eOp );
1190 :
1191 8365 : if ( IsOpCodeVolatile( eOp) )
1192 34 : pArr->SetExclusiveRecalcModeAlways();
1193 : else
1194 : {
1195 8331 : switch( eOp )
1196 : {
1197 : // Functions recalculated on every document load.
1198 : // Don't use SetExclusiveRecalcModeOnLoad() which would
1199 : // override ModeAlways, use
1200 : // AddRecalcMode(ScRecalcMode::ONLOAD) instead.
1201 : case ocConvert :
1202 : case ocDde:
1203 : case ocMacro:
1204 : case ocExternal:
1205 114 : pArr->AddRecalcMode( ScRecalcMode::ONLOAD );
1206 114 : break;
1207 : // If the referred cell is moved the value changes.
1208 : case ocColumn :
1209 : case ocRow :
1210 70 : pArr->SetRecalcModeOnRefMove();
1211 70 : break;
1212 : // ocCell needs recalc on move for some possible type values.
1213 : // and recalc mode on load, fdo#60646
1214 : case ocCell :
1215 50 : pArr->SetRecalcModeOnRefMove();
1216 50 : pArr->AddRecalcMode( ScRecalcMode::ONLOAD );
1217 50 : break;
1218 : case ocHyperLink :
1219 : // cell with hyperlink needs to be calculated on load to
1220 : // get its matrix result generated.
1221 4 : pArr->AddRecalcMode( ScRecalcMode::ONLOAD );
1222 4 : pArr->SetHyperLink( true);
1223 4 : break;
1224 : default:
1225 : ; // nothing
1226 : }
1227 : }
1228 8365 : if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
1229 : {
1230 77 : pFacToken = mpToken;
1231 77 : eOp = NextToken();
1232 154 : if (eOp != ocOpen)
1233 : {
1234 0 : SetError( errPairExpected);
1235 0 : PutCode( pFacToken );
1236 : }
1237 : else
1238 : {
1239 77 : eOp = NextToken();
1240 77 : if (eOp != ocClose)
1241 0 : SetError( errPairExpected);
1242 77 : PutCode( pFacToken);
1243 77 : eOp = NextToken();
1244 : }
1245 : }
1246 : // special cases NOT() and NEG()
1247 8288 : else if( eOp == ocNot || eOp == ocNeg
1248 8288 : || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) )
1249 : {
1250 468 : pFacToken = mpToken;
1251 468 : eOp = NextToken();
1252 468 : if( nNumFmt == css::util::NumberFormat::UNDEFINED && eOp == ocNot )
1253 0 : nNumFmt = css::util::NumberFormat::LOGICAL;
1254 468 : if (eOp == ocOpen)
1255 : {
1256 468 : NextToken();
1257 468 : eOp = Expression();
1258 : }
1259 : else
1260 0 : SetError( errPairExpected);
1261 468 : if (eOp != ocClose)
1262 0 : SetError( errPairExpected);
1263 468 : else if ( !pArr->GetCodeError() )
1264 468 : pFacToken->SetByte( 1 );
1265 468 : PutCode( pFacToken );
1266 468 : eOp = NextToken();
1267 : }
1268 13875 : else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
1269 1765 : || eOp == ocExternal
1270 1665 : || eOp == ocMacro
1271 1663 : || eOp == ocAnd
1272 1642 : || eOp == ocOr
1273 1627 : || eOp == ocBad
1274 1611 : || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1275 9431 : || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1276 : {
1277 6210 : pFacToken = mpToken;
1278 6210 : OpCode eMyLastOp = eOp;
1279 6210 : eOp = NextToken();
1280 6210 : bool bNoParam = false;
1281 6210 : bool bBadName = false;
1282 6210 : if (eOp == ocOpen)
1283 : {
1284 6194 : eOp = NextToken();
1285 6194 : if (eOp == ocClose)
1286 51 : bNoParam = true;
1287 : else
1288 6143 : eOp = Expression();
1289 : }
1290 16 : else if (eMyLastOp == ocBad)
1291 : {
1292 : // Just a bad name, not an unknown function, no parameters, no
1293 : // closing expected.
1294 16 : bBadName = true;
1295 16 : bNoParam = true;
1296 : }
1297 : else
1298 0 : SetError( errPairExpected);
1299 6210 : sal_uInt8 nSepCount = 0;
1300 6210 : if( !bNoParam )
1301 : {
1302 6143 : nSepCount++;
1303 19591 : while ((eOp == ocSep) && (!pArr->GetCodeError() || !mbStopOnError))
1304 : {
1305 7305 : nSepCount++;
1306 7305 : NextToken();
1307 7305 : eOp = Expression();
1308 : }
1309 : }
1310 6210 : if (bBadName)
1311 : ; // nothing, keep current token for return
1312 6194 : else if (eOp != ocClose)
1313 0 : SetError( errPairExpected);
1314 : else
1315 6194 : eOp = NextToken();
1316 : // Jumps are just normal functions for the FunctionAutoPilot tree view
1317 6210 : if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1318 1 : pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1319 : else
1320 6209 : pFacToken->SetByte( nSepCount );
1321 6210 : PutCode( pFacToken );
1322 : }
1323 1610 : else if (IsOpCodeJumpCommand(eOp))
1324 : {
1325 : // the PC counters are -1
1326 47 : pFacToken = mpToken;
1327 47 : switch (eOp)
1328 : {
1329 : case ocIf:
1330 30 : pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1331 30 : break;
1332 : case ocChoose:
1333 5 : pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1334 5 : break;
1335 : case ocIfError:
1336 : case ocIfNA:
1337 12 : pFacToken->GetJump()[ 0 ] = 2; // if, behind
1338 12 : break;
1339 : default:
1340 : SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump count case?");
1341 : }
1342 47 : eOp = NextToken();
1343 47 : if (eOp == ocOpen)
1344 : {
1345 47 : NextToken();
1346 47 : eOp = Expression();
1347 : }
1348 : else
1349 0 : SetError( errPairExpected);
1350 47 : PutCode( pFacToken );
1351 : // During AutoCorrect (since pArr->GetCodeError() is
1352 : // ignored) an unlimited ocIf would crash because
1353 : // ScRawToken::Clone() allocates the JumpBuffer according to
1354 : // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1355 : short nJumpMax;
1356 47 : OpCode eFacOpCode = pFacToken->GetOpCode();
1357 47 : switch (eFacOpCode)
1358 : {
1359 : case ocIf:
1360 30 : nJumpMax = 3;
1361 30 : break;
1362 : case ocChoose:
1363 5 : nJumpMax = FORMULA_MAXJUMPCOUNT;
1364 5 : break;
1365 : case ocIfError:
1366 : case ocIfNA:
1367 12 : nJumpMax = 2;
1368 12 : break;
1369 : default:
1370 0 : nJumpMax = 0;
1371 : SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump max case?");
1372 : }
1373 47 : short nJumpCount = 0;
1374 305 : while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
1375 211 : && (!pArr->GetCodeError() || !mbStopOnError))
1376 : {
1377 82 : if ( ++nJumpCount <= nJumpMax )
1378 82 : pFacToken->GetJump()[nJumpCount] = pc-1;
1379 82 : NextToken();
1380 82 : eOp = Expression();
1381 : // ocSep or ocClose terminate the subexpression
1382 82 : PutCode( mpToken );
1383 : }
1384 47 : if (eOp != ocClose)
1385 0 : SetError( errPairExpected);
1386 : else
1387 : {
1388 47 : eOp = NextToken();
1389 : // always limit to nJumpMax, no arbitrary overwrites
1390 47 : if ( ++nJumpCount <= nJumpMax )
1391 47 : pFacToken->GetJump()[ nJumpCount ] = pc-1;
1392 47 : eFacOpCode = pFacToken->GetOpCode();
1393 : bool bLimitOk;
1394 47 : switch (eFacOpCode)
1395 : {
1396 : case ocIf:
1397 30 : bLimitOk = (nJumpCount <= 3);
1398 30 : break;
1399 : case ocChoose:
1400 5 : bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT); /* TODO: check, really <, not <=? */
1401 5 : break;
1402 : case ocIfError:
1403 : case ocIfNA:
1404 12 : bLimitOk = (nJumpCount <= 2);
1405 12 : break;
1406 : default:
1407 0 : bLimitOk = false;
1408 : SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump limit case?");
1409 : }
1410 47 : if (bLimitOk)
1411 47 : pFacToken->GetJump()[ 0 ] = nJumpCount;
1412 : else
1413 0 : SetError( errIllegalParameter);
1414 : }
1415 : }
1416 1563 : else if ( eOp == ocMissing )
1417 : {
1418 13 : PutCode( mpToken );
1419 13 : eOp = NextToken();
1420 : }
1421 1550 : else if ( eOp == ocClose )
1422 : {
1423 0 : SetError( errParameterExpected );
1424 : }
1425 1550 : else if ( eOp == ocSep )
1426 : { // Subsequent ocSep
1427 0 : SetError( errParameterExpected );
1428 0 : if ( bAutoCorrect && !pStack )
1429 : {
1430 0 : aCorrectedSymbol.clear();
1431 0 : bCorrected = true;
1432 : }
1433 : }
1434 1550 : else if ( mpToken->IsExternalRef() )
1435 : {
1436 0 : PutCode( mpToken);
1437 0 : eOp = NextToken();
1438 : }
1439 : else
1440 : {
1441 1550 : SetError( errUnknownToken );
1442 1550 : if ( bAutoCorrect && !pStack )
1443 : {
1444 0 : if ( eOp == ocStop )
1445 : { // trailing operator w/o operand
1446 0 : sal_Int32 nLen = aCorrectedFormula.getLength();
1447 0 : if ( nLen )
1448 0 : aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
1449 0 : aCorrectedSymbol.clear();
1450 0 : bCorrected = true;
1451 : }
1452 : }
1453 : }
1454 31764 : }
1455 : }
1456 :
1457 31764 : void FormulaCompiler::RangeLine()
1458 : {
1459 31764 : Factor();
1460 63528 : while (mpToken->GetOpCode() == ocRange)
1461 : {
1462 0 : FormulaToken** pCode1 = pCode - 1;
1463 0 : FormulaTokenRef p = mpToken;
1464 0 : NextToken();
1465 0 : Factor();
1466 0 : FormulaToken** pCode2 = pCode - 1;
1467 0 : if (!MergeRangeReference( pCode1, pCode2))
1468 0 : PutCode(p);
1469 0 : }
1470 31764 : }
1471 :
1472 31764 : void FormulaCompiler::IntersectionLine()
1473 : {
1474 31764 : RangeLine();
1475 63528 : while (mpToken->GetOpCode() == ocIntersect)
1476 : {
1477 0 : FormulaTokenRef p = mpToken;
1478 0 : NextToken();
1479 0 : RangeLine();
1480 0 : PutCode(p);
1481 0 : }
1482 31764 : }
1483 :
1484 31760 : void FormulaCompiler::UnionLine()
1485 : {
1486 31760 : IntersectionLine();
1487 63524 : while (mpToken->GetOpCode() == ocUnion)
1488 : {
1489 4 : FormulaTokenRef p = mpToken;
1490 4 : NextToken();
1491 4 : IntersectionLine();
1492 4 : PutCode(p);
1493 4 : }
1494 31760 : }
1495 :
1496 31915 : void FormulaCompiler::UnaryLine()
1497 : {
1498 31915 : if( mpToken->GetOpCode() == ocAdd )
1499 0 : GetToken();
1500 38670 : else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
1501 6755 : mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
1502 : {
1503 155 : FormulaTokenRef p = mpToken;
1504 155 : NextToken();
1505 155 : UnaryLine();
1506 155 : PutCode( p );
1507 : }
1508 : else
1509 31760 : UnionLine();
1510 31915 : }
1511 :
1512 31760 : void FormulaCompiler::PostOpLine()
1513 : {
1514 31760 : UnaryLine();
1515 63520 : while ( mpToken->GetOpCode() == ocPercentSign )
1516 : { // this operator _follows_ its operand
1517 0 : PutCode( mpToken );
1518 0 : NextToken();
1519 : }
1520 31760 : }
1521 :
1522 31757 : void FormulaCompiler::PowLine()
1523 : {
1524 31757 : PostOpLine();
1525 63517 : while (mpToken->GetOpCode() == ocPow)
1526 : {
1527 3 : FormulaTokenRef p = mpToken;
1528 3 : NextToken();
1529 3 : PostOpLine();
1530 3 : PutCode(p);
1531 3 : }
1532 31757 : }
1533 :
1534 29662 : void FormulaCompiler::MulDivLine()
1535 : {
1536 29662 : PowLine();
1537 61419 : while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
1538 : {
1539 2095 : FormulaTokenRef p = mpToken;
1540 2095 : NextToken();
1541 2095 : PowLine();
1542 2095 : PutCode(p);
1543 2095 : }
1544 29662 : }
1545 :
1546 25504 : void FormulaCompiler::AddSubLine()
1547 : {
1548 25504 : MulDivLine();
1549 55166 : while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
1550 : {
1551 4158 : FormulaTokenRef p = mpToken;
1552 4158 : NextToken();
1553 4158 : MulDivLine();
1554 4158 : PutCode(p);
1555 4158 : }
1556 25504 : }
1557 :
1558 25492 : void FormulaCompiler::ConcatLine()
1559 : {
1560 25492 : AddSubLine();
1561 50996 : while (mpToken->GetOpCode() == ocAmpersand)
1562 : {
1563 12 : FormulaTokenRef p = mpToken;
1564 12 : NextToken();
1565 12 : AddSubLine();
1566 12 : PutCode(p);
1567 12 : }
1568 25492 : }
1569 :
1570 25151 : void FormulaCompiler::CompareLine()
1571 : {
1572 25151 : ConcatLine();
1573 50643 : while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
1574 : {
1575 341 : FormulaTokenRef p = mpToken;
1576 341 : NextToken();
1577 341 : ConcatLine();
1578 341 : PutCode(p);
1579 341 : }
1580 25151 : }
1581 :
1582 25151 : void FormulaCompiler::NotLine()
1583 : {
1584 25151 : CompareLine();
1585 50302 : while (mpToken->GetOpCode() == ocNot)
1586 : {
1587 0 : FormulaTokenRef p = mpToken;
1588 0 : NextToken();
1589 0 : CompareLine();
1590 0 : PutCode(p);
1591 0 : }
1592 25151 : }
1593 :
1594 25151 : OpCode FormulaCompiler::Expression()
1595 : {
1596 : static const short nRecursionMax = 42;
1597 25151 : FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1598 25151 : if ( nRecursion > nRecursionMax )
1599 : {
1600 0 : SetError( errStackOverflow );
1601 0 : return ocStop; //! generate token instead?
1602 : }
1603 25151 : NotLine();
1604 50302 : while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
1605 : {
1606 0 : FormulaTokenRef p = mpToken;
1607 0 : mpToken->SetByte( 2 ); // 2 parameters!
1608 0 : NextToken();
1609 0 : NotLine();
1610 0 : PutCode(p);
1611 0 : }
1612 25151 : return mpToken->GetOpCode();
1613 : }
1614 :
1615 :
1616 0 : void FormulaCompiler::SetError( sal_uInt16 /*nError*/ )
1617 : {
1618 0 : }
1619 :
1620 0 : FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/,
1621 : bool /*bReuseDoubleRef*/ )
1622 : {
1623 0 : return FormulaTokenRef();
1624 : }
1625 :
1626 0 : bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
1627 : {
1628 : FormulaToken *p1, *p2;
1629 0 : if (pc < 2 || !pCode1 || !pCode2 ||
1630 0 : (pCode2 - pCode1 != 1) || (pCode - pCode2 != 1) ||
1631 0 : ((p1 = *pCode1) == 0) || ((p2 = *pCode2) == 0) )
1632 0 : return false;
1633 :
1634 0 : FormulaTokenRef p = ExtendRangeReference( *p1, *p2, true);
1635 0 : if (!p)
1636 0 : return false;
1637 :
1638 0 : p->IncRef();
1639 0 : p1->DecRef();
1640 0 : p2->DecRef();
1641 0 : *pCode1 = p.get();
1642 0 : --pCode, --pc;
1643 :
1644 0 : return true;
1645 : }
1646 :
1647 10067 : bool FormulaCompiler::CompileTokenArray()
1648 : {
1649 10067 : glSubTotal = false;
1650 10067 : bCorrected = false;
1651 10067 : if (!pArr->GetCodeError() || !mbStopOnError)
1652 : {
1653 10067 : if ( bAutoCorrect )
1654 : {
1655 0 : aCorrectedFormula.clear();
1656 0 : aCorrectedSymbol.clear();
1657 : }
1658 10067 : pArr->DelRPN();
1659 10067 : pStack = NULL;
1660 : FormulaToken* pData[ FORMULA_MAXTOKENS ];
1661 10067 : pCode = pData;
1662 10067 : bool bWasForced = pArr->IsRecalcModeForced();
1663 10067 : if ( bWasForced )
1664 : {
1665 0 : if ( bAutoCorrect )
1666 0 : aCorrectedFormula = "=";
1667 : }
1668 10067 : pArr->ClearRecalcMode();
1669 10067 : pArr->Reset();
1670 10067 : eLastOp = ocOpen;
1671 10067 : pc = 0;
1672 10067 : NextToken();
1673 10067 : OpCode eOp = Expression();
1674 : // Some trailing garbage that doesn't form an expression?
1675 10067 : if (eOp != ocStop)
1676 1551 : SetError( errOperatorExpected);
1677 :
1678 10067 : sal_uInt16 nErrorBeforePop = pArr->GetCodeError();
1679 :
1680 20134 : while( pStack )
1681 0 : PopTokenArray();
1682 10067 : if( pc )
1683 : {
1684 8517 : pArr->pRPN = new FormulaToken*[ pc ];
1685 8517 : pArr->nRPN = pc;
1686 8517 : memcpy( pArr->pRPN, pData, pc * sizeof( FormulaToken* ) );
1687 : }
1688 :
1689 : // once an error, always an error
1690 10067 : if( !pArr->GetCodeError() && nErrorBeforePop )
1691 0 : pArr->SetCodeError( nErrorBeforePop);
1692 :
1693 10067 : if (pArr->GetCodeError() && mbStopOnError)
1694 : {
1695 1551 : pArr->DelRPN();
1696 1551 : pArr->SetHyperLink( false);
1697 : }
1698 :
1699 10067 : if ( bWasForced )
1700 0 : pArr->SetRecalcModeForced();
1701 : }
1702 10067 : if( nNumFmt == css::util::NumberFormat::UNDEFINED )
1703 4362 : nNumFmt = css::util::NumberFormat::NUMBER;
1704 10067 : return glSubTotal;
1705 : }
1706 :
1707 499 : void FormulaCompiler::PopTokenArray()
1708 : {
1709 499 : if( pStack )
1710 : {
1711 499 : FormulaArrayStack* p = pStack;
1712 499 : pStack = p->pNext;
1713 : // obtain special RecalcMode from SharedFormula
1714 499 : if ( pArr->IsRecalcModeAlways() )
1715 0 : p->pArr->SetExclusiveRecalcModeAlways();
1716 499 : else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
1717 0 : p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
1718 499 : p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
1719 499 : if ( pArr->IsHyperLink() ) // fdo 87534
1720 0 : p->pArr->SetHyperLink( true );
1721 499 : if( p->bTemp )
1722 499 : delete pArr;
1723 499 : pArr = p->pArr;
1724 499 : delete p;
1725 : }
1726 499 : }
1727 :
1728 146 : void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
1729 : {
1730 146 : OUStringBuffer aBuffer( pArr->GetLen() * 5 );
1731 146 : CreateStringFromTokenArray( aBuffer );
1732 146 : rFormula = aBuffer.makeStringAndClear();
1733 146 : }
1734 :
1735 2724 : void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
1736 : {
1737 2724 : rBuffer.setLength(0);
1738 2724 : if( !pArr->GetLen() )
1739 2724 : return;
1740 :
1741 2724 : FormulaTokenArray* pSaveArr = pArr;
1742 2724 : bool bODFF = FormulaGrammar::isODFF( meGrammar);
1743 2724 : if (bODFF || FormulaGrammar::isPODF( meGrammar) )
1744 : {
1745 : // Scan token array for missing args and re-write if present.
1746 1354 : MissingConventionODF aConv( bODFF);
1747 1354 : if (pArr->NeedsPodfRewrite( aConv))
1748 16 : pArr = pArr->RewriteMissing( aConv );
1749 : }
1750 1370 : else if ( FormulaGrammar::isOOXML( meGrammar ) )
1751 : {
1752 : // Scan token array for missing args and rewrite if present.
1753 560 : MissingConventionOOXML aConv;
1754 560 : if (pArr->NeedsOoxmlRewrite())
1755 253 : pArr = pArr->RewriteMissing( aConv );
1756 : }
1757 :
1758 : // At least one character per token, plus some are references, some are
1759 : // function names, some are numbers, ...
1760 2724 : rBuffer.ensureCapacity( pArr->GetLen() * 5 );
1761 :
1762 2724 : if ( pArr->IsRecalcModeForced() )
1763 0 : rBuffer.append( '=');
1764 2724 : const FormulaToken* t = pArr->First();
1765 21471 : while( t )
1766 16023 : t = CreateStringFromToken( rBuffer, t, true );
1767 :
1768 2724 : if (pSaveArr != pArr)
1769 : {
1770 269 : delete pArr;
1771 269 : pArr = pSaveArr;
1772 : }
1773 : }
1774 :
1775 25863 : const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP,
1776 : bool bAllowArrAdvance )
1777 : {
1778 25863 : OUStringBuffer aBuffer;
1779 25863 : const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP, bAllowArrAdvance );
1780 25863 : rFormula += aBuffer.makeStringAndClear();
1781 25863 : return p;
1782 : }
1783 :
1784 41886 : const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
1785 : bool bAllowArrAdvance )
1786 : {
1787 41886 : bool bNext = true;
1788 41886 : bool bSpaces = false;
1789 41886 : const FormulaToken* t = pTokenP;
1790 41886 : OpCode eOp = t->GetOpCode();
1791 41886 : if( eOp >= ocAnd && eOp <= ocOr )
1792 : {
1793 : // AND, OR infix?
1794 31 : if ( bAllowArrAdvance )
1795 31 : t = pArr->Next();
1796 : else
1797 0 : t = pArr->PeekNext();
1798 31 : bNext = false;
1799 31 : bSpaces = ( !t || t->GetOpCode() != ocOpen );
1800 : }
1801 41886 : if( bSpaces )
1802 0 : rBuffer.append( ' ');
1803 :
1804 41886 : if( eOp == ocSpaces )
1805 : {
1806 337 : bool bIntersectionOp = mxSymbols->isODFF();
1807 337 : if (bIntersectionOp)
1808 : {
1809 160 : const FormulaToken* p = pArr->PeekPrevNoSpaces();
1810 160 : bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
1811 160 : if (bIntersectionOp)
1812 : {
1813 0 : p = pArr->PeekNextNoSpaces();
1814 0 : bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
1815 : }
1816 : }
1817 337 : if (bIntersectionOp)
1818 0 : rBuffer.appendAscii( "!!");
1819 : else
1820 : {
1821 : // most times it's just one blank
1822 337 : sal_uInt8 n = t->GetByte();
1823 674 : for ( sal_uInt8 j=0; j<n; ++j )
1824 : {
1825 337 : rBuffer.append( ' ');
1826 : }
1827 : }
1828 : }
1829 41549 : else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1830 0 : rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
1831 41549 : else if( (sal_uInt16) eOp < mxSymbols->getSymbolCount()) // Keyword:
1832 41549 : rBuffer.append( mxSymbols->getSymbol( eOp));
1833 : else
1834 : {
1835 : SAL_WARN( "formula.core","unknown OpCode");
1836 0 : rBuffer.append( GetNativeSymbol( ocErrName ));
1837 : }
1838 41886 : if( bNext )
1839 : {
1840 41855 : if (t->IsExternalRef())
1841 : {
1842 56 : CreateStringFromExternal( rBuffer, pTokenP);
1843 : }
1844 : else
1845 : {
1846 41799 : switch( t->GetType() )
1847 : {
1848 : case svDouble:
1849 2414 : AppendDouble( rBuffer, t->GetDouble() );
1850 2414 : break;
1851 :
1852 : case svString:
1853 418 : if( eOp == ocBad || eOp == ocStringXML )
1854 71 : rBuffer.append( t->GetString().getString());
1855 : else
1856 347 : AppendString( rBuffer, t->GetString().getString() );
1857 418 : break;
1858 : case svSingleRef:
1859 15011 : CreateStringFromSingleRef( rBuffer, t);
1860 15011 : break;
1861 : case svDoubleRef:
1862 13643 : CreateStringFromDoubleRef( rBuffer, t);
1863 13643 : break;
1864 : case svMatrix:
1865 4 : CreateStringFromMatrix( rBuffer, t );
1866 4 : break;
1867 :
1868 : case svIndex:
1869 55 : CreateStringFromIndex( rBuffer, t );
1870 55 : if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
1871 : {
1872 : // Suppress all TableRef related tokens, the resulting
1873 : // range was written by CreateStringFromIndex().
1874 0 : const FormulaToken* const p = pArr->PeekNext();
1875 0 : if (p && p->GetOpCode() == ocTableRefOpen)
1876 : {
1877 0 : int nLevel = 0;
1878 0 : do
1879 : {
1880 0 : t = pArr->Next();
1881 0 : if (!t)
1882 0 : break;
1883 :
1884 : // Switch cases correspond with those in
1885 : // ScCompiler::HandleTableRef()
1886 0 : switch (t->GetOpCode())
1887 : {
1888 : case ocTableRefOpen:
1889 0 : ++nLevel;
1890 0 : break;
1891 : case ocTableRefClose:
1892 0 : --nLevel;
1893 0 : break;
1894 : case ocTableRefItemAll:
1895 : case ocTableRefItemHeaders:
1896 : case ocTableRefItemData:
1897 : case ocTableRefItemTotals:
1898 : case ocTableRefItemThisRow:
1899 : case ocSep:
1900 : case ocPush:
1901 : case ocRange:
1902 : case ocSpaces:
1903 0 : break;
1904 : default:
1905 0 : nLevel = 0;
1906 0 : bNext = false;
1907 : }
1908 : } while (nLevel);
1909 : }
1910 : }
1911 55 : break;
1912 : case svExternal:
1913 : {
1914 : // mapped or translated name of AddIns
1915 89 : OUString aAddIn( t->GetExternal() );
1916 89 : bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
1917 89 : if (!bMapped && mxSymbols->hasExternals())
1918 : {
1919 88 : ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap()->find( aAddIn);
1920 88 : if (iLook != mxSymbols->getReverseExternalHashMap()->end())
1921 : {
1922 88 : aAddIn = (*iLook).second;
1923 88 : bMapped = true;
1924 : }
1925 : }
1926 89 : if (!bMapped && !mxSymbols->isEnglish())
1927 1 : LocalizeString( aAddIn );
1928 89 : rBuffer.append( aAddIn);
1929 : }
1930 89 : break;
1931 : case svError:
1932 0 : AppendErrorConstant( rBuffer, t->GetError());
1933 0 : break;
1934 : case svByte:
1935 : case svJump:
1936 : case svFAP:
1937 : case svMissing:
1938 : case svSep:
1939 10165 : break; // Opcodes
1940 : default:
1941 : SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
1942 : } // of switch
1943 : }
1944 : }
1945 41886 : if( bSpaces )
1946 0 : rBuffer.append( ' ');
1947 41886 : if ( bAllowArrAdvance )
1948 : {
1949 16023 : if( bNext )
1950 15992 : t = pArr->Next();
1951 16023 : return t;
1952 : }
1953 25863 : return pTokenP;
1954 : }
1955 :
1956 :
1957 2426 : void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
1958 : {
1959 2426 : if ( mxSymbols->isEnglish() )
1960 : {
1961 : ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
1962 : rtl_math_StringFormat_Automatic,
1963 2388 : rtl_math_DecimalPlaces_Max, '.', true );
1964 : }
1965 : else
1966 : {
1967 38 : SvtSysLocale aSysLocale;
1968 : ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
1969 : rtl_math_StringFormat_Automatic,
1970 : rtl_math_DecimalPlaces_Max,
1971 38 : aSysLocale.GetLocaleDataPtr()->getNumDecimalSep()[0],
1972 38 : true );
1973 : }
1974 2426 : }
1975 :
1976 0 : void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
1977 : {
1978 0 : rBuffer.append( mxSymbols->getSymbol( static_cast<OpCode>(bVal ? ocTrue : ocFalse)) );
1979 0 : }
1980 :
1981 347 : void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
1982 : {
1983 347 : rBuffer.append( '"');
1984 347 : if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == NULL )
1985 347 : rBuffer.append( rStr );
1986 : else
1987 : {
1988 0 : OUString aStr = rStr.replaceAll( "\"", "\"\"" );
1989 0 : rBuffer.append(aStr);
1990 : }
1991 347 : rBuffer.append( '"');
1992 347 : }
1993 :
1994 0 : bool FormulaCompiler::NeedsTableRefTransformation() const
1995 : {
1996 : /* TODO: currently only UI representations use Table structured
1997 : * references. Not defined in ODFF, and not implemented yet for OOXML
1998 : * export. Change this once OOXML export is implemented, until then write
1999 : * A1 style references also for OOXML to not lose functionality. */
2000 : // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2001 : // symbol is not defined there.
2002 0 : return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar)
2003 0 : || FormulaGrammar::isOOXML( meGrammar);
2004 : }
2005 :
2006 467 : void FormulaCompiler::UpdateSeparatorsNative(
2007 : const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2008 : {
2009 467 : NonConstOpCodeMapPtr xSymbolsNative;
2010 467 : lcl_fillNativeSymbols( xSymbolsNative);
2011 467 : xSymbolsNative->putOpCode( rSep, ocSep, NULL);
2012 467 : xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, NULL);
2013 467 : xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, NULL);
2014 467 : }
2015 :
2016 49 : void FormulaCompiler::ResetNativeSymbols()
2017 : {
2018 49 : NonConstOpCodeMapPtr xSymbolsNative;
2019 49 : lcl_fillNativeSymbols( xSymbolsNative, true);
2020 49 : lcl_fillNativeSymbols( xSymbolsNative);
2021 49 : }
2022 :
2023 0 : void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2024 : {
2025 0 : NonConstOpCodeMapPtr xSymbolsNative;
2026 0 : lcl_fillNativeSymbols( xSymbolsNative);
2027 0 : xSymbolsNative->copyFrom( *xMap, true);
2028 0 : }
2029 :
2030 :
2031 69047 : OpCode FormulaCompiler::NextToken()
2032 : {
2033 69047 : if( !GetToken() )
2034 8514 : return ocStop;
2035 60533 : OpCode eOp = mpToken->GetOpCode();
2036 : // There must be an operator before a push
2037 82758 : if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2038 33195 : !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2039 3712 : (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
2040 0 : SetError( errOperatorExpected);
2041 : // Operator and Plus => operator
2042 63866 : if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2043 3333 : (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2044 0 : eOp = NextToken();
2045 : else
2046 : {
2047 : // Before an operator there must not be another operator, with the
2048 : // exception of AND and OR.
2049 60533 : if ( eOp != ocAnd && eOp != ocOr &&
2050 13368 : (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
2051 13226 : && (eLastOp == ocOpen || eLastOp == ocSep ||
2052 6613 : (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2053 : {
2054 0 : SetError( errVariableExpected);
2055 0 : if ( bAutoCorrect && !pStack )
2056 : {
2057 0 : if ( eOp == eLastOp || eLastOp == ocOpen )
2058 : { // throw away duplicated operator
2059 0 : aCorrectedSymbol.clear();
2060 0 : bCorrected = true;
2061 : }
2062 : else
2063 : {
2064 0 : sal_Int32 nPos = aCorrectedFormula.getLength();
2065 0 : if ( nPos )
2066 : {
2067 0 : nPos--;
2068 0 : sal_Unicode c = aCorrectedFormula[ nPos ];
2069 0 : switch ( eOp )
2070 : { // swap operators
2071 : case ocGreater:
2072 0 : if ( c == mxSymbols->getSymbolChar( ocEqual) )
2073 : { // >= instead of =>
2074 0 : aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2075 0 : OUString( mxSymbols->getSymbolChar(ocGreater) ) );
2076 0 : aCorrectedSymbol = OUString(c);
2077 0 : bCorrected = true;
2078 : }
2079 0 : break;
2080 : case ocLess:
2081 0 : if ( c == mxSymbols->getSymbolChar( ocEqual) )
2082 : { // <= instead of =<
2083 0 : aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2084 0 : OUString( mxSymbols->getSymbolChar(ocLess) ) );
2085 0 : aCorrectedSymbol = OUString(c);
2086 0 : bCorrected = true;
2087 : }
2088 0 : else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2089 : { // <> instead of ><
2090 0 : aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2091 0 : OUString( mxSymbols->getSymbolChar(ocLess) ) );
2092 0 : aCorrectedSymbol = OUString(c);
2093 0 : bCorrected = true;
2094 : }
2095 0 : break;
2096 : case ocMul:
2097 0 : if ( c == mxSymbols->getSymbolChar( ocSub) )
2098 : { // *- instead of -*
2099 0 : aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2100 0 : OUString( mxSymbols->getSymbolChar(ocMul) ) );
2101 0 : aCorrectedSymbol = OUString(c);
2102 0 : bCorrected = true;
2103 : }
2104 0 : break;
2105 : case ocDiv:
2106 0 : if ( c == mxSymbols->getSymbolChar( ocSub) )
2107 : { // /- instead of -/
2108 0 : aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2109 0 : OUString( mxSymbols->getSymbolChar(ocDiv) ) );
2110 0 : aCorrectedSymbol = OUString(c);
2111 0 : bCorrected = true;
2112 : }
2113 0 : break;
2114 : default:
2115 : ; // nothing
2116 : }
2117 : }
2118 : }
2119 : }
2120 : }
2121 60533 : eLastOp = eOp;
2122 : }
2123 60533 : return eOp;
2124 : }
2125 36025 : void FormulaCompiler::PutCode( FormulaTokenRef& p )
2126 : {
2127 36025 : if( pc >= FORMULA_MAXTOKENS - 1 )
2128 : {
2129 0 : if ( pc == FORMULA_MAXTOKENS - 1 )
2130 : {
2131 0 : p = new FormulaByteToken( ocStop );
2132 0 : p->IncRef();
2133 0 : *pCode++ = p.get();
2134 0 : ++pc;
2135 : }
2136 0 : SetError( errCodeOverflow);
2137 0 : return;
2138 : }
2139 36025 : if (pArr->GetCodeError() && mbJumpCommandReorder)
2140 0 : return;
2141 36025 : ForceArrayOperator( p, pCurrentFactorToken);
2142 36025 : p->IncRef();
2143 36025 : *pCode++ = p.get();
2144 36025 : pc++;
2145 : }
2146 :
2147 :
2148 0 : bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2149 : {
2150 0 : return true;
2151 : }
2152 :
2153 0 : bool FormulaCompiler::HandleRange()
2154 : {
2155 0 : return true;
2156 : }
2157 :
2158 0 : bool FormulaCompiler::HandleColRowName()
2159 : {
2160 0 : return true;
2161 : }
2162 :
2163 0 : bool FormulaCompiler::HandleDbData()
2164 : {
2165 0 : return true;
2166 : }
2167 :
2168 0 : bool FormulaCompiler::HandleTableRef()
2169 : {
2170 0 : return true;
2171 : }
2172 :
2173 0 : void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2174 : {
2175 0 : }
2176 :
2177 0 : void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2178 : {
2179 0 : }
2180 :
2181 0 : void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2182 : {
2183 0 : }
2184 :
2185 0 : void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2186 : {
2187 0 : }
2188 :
2189 0 : void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2190 : {
2191 0 : }
2192 :
2193 0 : void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2194 : {
2195 0 : }
2196 :
2197 499 : void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
2198 : {
2199 499 : if ( bAutoCorrect && !pStack )
2200 : { // don't merge stacked subroutine code into entered formula
2201 0 : aCorrectedFormula += aCorrectedSymbol;
2202 0 : aCorrectedSymbol.clear();
2203 : }
2204 499 : FormulaArrayStack* p = new FormulaArrayStack;
2205 499 : p->pNext = pStack;
2206 499 : p->pArr = pArr;
2207 499 : p->bTemp = bTemp;
2208 499 : pStack = p;
2209 499 : pArr = pa;
2210 499 : }
2211 :
2212 : } // namespace formula
2213 :
2214 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|