1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #ifndef INCLUDED_FORMULA_TOKENARRAY_HXX
21 #define INCLUDED_FORMULA_TOKENARRAY_HXX
26 #include <type_traits>
27 #include <unordered_set>
28 #include <unordered_map>
31 #include <formula/ExternalReferenceHelper.hxx>
32 #include <formula/formuladllapi.h>
33 #include <formula/opcode.hxx>
34 #include <formula/token.hxx>
35 #include <o3tl/typed_flags_set.hxx>
36 #include <rtl/ustring.hxx>
37 #include <sal/types.h>
39 namespace com::sun::star
{
40 namespace sheet
{ struct FormulaToken
; }
43 namespace com::sun::star::uno
{ template <typename
> class Sequence
; }
44 namespace formula
{ class FormulaTokenArray
; }
49 class SharedStringPool
;
53 // RecalcMode access only via TokenArray SetExclusiveRecalcMode...() /
56 // Only one of the exclusive bits can be set and one must be set,
57 // handled by TokenArray SetExclusiveRecalcMode...() methods.
58 // Exclusive bits are ordered by priority, AddRecalcMode() relies on that.
59 enum class ScRecalcMode
: sal_uInt8
61 ALWAYS
= 0x01, // exclusive, always
62 ONLOAD_MUST
= 0x02, // exclusive, always after load
63 ONLOAD_ONCE
= 0x04, // exclusive, once after load, import filter
64 ONLOAD_LENIENT
= 0x08, // exclusive, lenient after load (eg. macros not always, aliens, ...)
65 NORMAL
= 0x10, // exclusive
66 FORCED
= 0x20, // combined, also if cell isn't visible, for macros with side effects
67 ONREFMOVE
= 0x40, // combined, if reference was moved
68 EMask
= ALWAYS
| ONLOAD_MUST
| ONLOAD_LENIENT
| ONLOAD_ONCE
| NORMAL
// mask of exclusive bits
72 template<> struct typed_flags
<ScRecalcMode
> : is_typed_flags
<ScRecalcMode
, 0x7f> {};
78 class FORMULA_DLLPUBLIC MissingConvention
83 FORMULA_MISSING_CONVENTION_PODF
,
84 FORMULA_MISSING_CONVENTION_ODFF
,
85 FORMULA_MISSING_CONVENTION_OOXML
87 explicit MissingConvention( Convention eConvention
) : meConvention(eConvention
) {}
88 bool isPODF() const { return meConvention
== FORMULA_MISSING_CONVENTION_PODF
; }
89 bool isODFF() const { return meConvention
== FORMULA_MISSING_CONVENTION_ODFF
; }
90 bool isOOXML() const { return meConvention
== FORMULA_MISSING_CONVENTION_OOXML
; }
91 Convention
getConvention() const { return meConvention
; }
93 Convention meConvention
;
96 class FORMULA_DLLPUBLIC MissingConventionODF
: public MissingConvention
99 explicit MissingConventionODF( bool bODFF
) :
100 MissingConvention( bODFF
?
101 MissingConvention::FORMULA_MISSING_CONVENTION_ODFF
:
102 MissingConvention::FORMULA_MISSING_CONVENTION_PODF
)
105 // Implementation and usage only in token.cxx
106 inline bool isRewriteNeeded( OpCode eOp
) const;
109 class FORMULA_DLLPUBLIC MissingConventionOOXML
: public MissingConvention
112 explicit MissingConventionOOXML() : MissingConvention( MissingConvention::FORMULA_MISSING_CONVENTION_OOXML
) {}
113 // Implementation and usage only in token.cxx
114 static inline bool isRewriteNeeded( OpCode eOp
);
117 typedef std::unordered_set
<OpCode
, std::hash
<std::underlying_type
<OpCode
>::type
> > unordered_opcode_set
;
119 class FORMULA_DLLPUBLIC FormulaTokenArrayStandardRange
122 FormulaToken
** mpBegin
;
123 FormulaToken
** mpEnd
;
126 FormulaTokenArrayStandardRange(FormulaToken
** pBegin
, sal_uInt16 nSize
) :
128 mpEnd(pBegin
+ nSize
)
132 FormulaToken
** begin() const
137 FormulaToken
** end() const
143 class FORMULA_DLLPUBLIC FormulaTokenArrayReferencesIterator
146 FormulaToken
** maIter
;
147 FormulaToken
** maEnd
;
151 while (maIter
!= maEnd
)
153 switch ((*maIter
)->GetType())
157 case svExternalSingleRef
:
158 case svExternalDoubleRef
:
166 enum class Dummy
{ Flag
};
168 FormulaTokenArrayReferencesIterator(const FormulaTokenArrayStandardRange
& rRange
, Dummy
) :
169 maIter(rRange
.end()),
175 FormulaTokenArrayReferencesIterator(const FormulaTokenArrayStandardRange
& rRange
) :
176 maIter(rRange
.begin()),
182 FormulaTokenArrayReferencesIterator
operator++(int)
184 FormulaTokenArrayReferencesIterator
result(*this);
189 FormulaTokenArrayReferencesIterator
const & operator++()
191 assert(maIter
!= maEnd
);
197 FormulaToken
* operator*() const
202 bool operator==(const FormulaTokenArrayReferencesIterator
& rhs
) const
204 return maIter
== rhs
.maIter
;
207 bool operator!=(const FormulaTokenArrayReferencesIterator
& rhs
) const
209 return !operator==(rhs
);
212 static FormulaTokenArrayReferencesIterator
endOf(const FormulaTokenArrayStandardRange
& rRange
)
214 return FormulaTokenArrayReferencesIterator(rRange
, Dummy::Flag
);
218 class FORMULA_DLLPUBLIC FormulaTokenArrayReferencesRange
221 const FormulaTokenArray
& mrFTA
;
224 FormulaTokenArrayReferencesRange(const FormulaTokenArray
& rFTA
) :
229 FormulaTokenArrayReferencesIterator
begin();
231 FormulaTokenArrayReferencesIterator
end();
234 class FORMULA_DLLPUBLIC FormulaTokenArray
237 std::unique_ptr
<FormulaToken
*[]> pCode
; // Token code array
238 FormulaToken
** pRPN
; // RPN array
239 sal_uInt16 nLen
; // Length of token array
240 sal_uInt16 nRPN
; // Length of RPN array
241 FormulaError nError
; // Error code
242 ScRecalcMode nMode
; // Flags to indicate when to recalc this code
243 bool bHyperLink
:1; // If HYPERLINK() occurs in the formula.
244 bool mbFromRangeName
:1; // If this array originates from a named expression
245 bool mbShareable
:1; // Whether or not it can be shared with adjacent cells.
246 bool mbFinalized
:1; // Whether code arrays have their final used size and no more tokens can be added.
249 void Assign( const FormulaTokenArray
& );
250 void Assign( sal_uInt16 nCode
, FormulaToken
**pTokens
);
251 void Move( FormulaTokenArray
&& );
253 /// Also used by the compiler. The token MUST had been allocated with new!
254 FormulaToken
* Add( FormulaToken
* );
259 CODE_ONLY
, ///< replacement only in pCode
260 CODE_AND_RPN
///< replacement in pCode and pRPN
263 /** Also used by the compiler. The token MUST had been allocated with new!
265 Absolute offset in pCode of the token to be replaced.
267 If CODE_ONLY only the token in pCode at nOffset is replaced.
268 If CODE_AND_RPN the token in pCode at nOffset is replaced;
269 if the original token was also referenced in the pRPN array
270 then that reference is replaced with a reference to the new
273 FormulaToken
* ReplaceToken( sal_uInt16 nOffset
, FormulaToken
*, ReplaceMode eMode
);
275 /** Remove a sequence of tokens from pCode array, and pRPN array if the
276 tokens are referenced there.
278 nLen and nRPN are adapted.
281 Start offset into pCode.
283 Count of tokens to remove.
285 @return Count of tokens removed.
287 sal_uInt16
RemoveToken( sal_uInt16 nOffset
, sal_uInt16 nCount
);
290 /** Assignment with incrementing references of FormulaToken entries
292 FormulaTokenArray( const FormulaTokenArray
& );
293 FormulaTokenArray( FormulaTokenArray
&& );
294 virtual ~FormulaTokenArray();
296 virtual void Clear();
299 * The array has its final used size and no more token can be added.
303 void SetFromRangeName( bool b
) { mbFromRangeName
= b
; }
304 bool IsFromRangeName() const { return mbFromRangeName
; }
306 void SetShareable( bool b
) { mbShareable
= b
; }
309 * Check if this token array is shareable between multiple adjacent
310 * formula cells. Certain tokens may not function correctly when shared.
312 * @return true if the token array is shareable, false otherwise.
314 bool IsShareable() const { return mbShareable
; }
317 FormulaToken
* FirstToken() const;
319 /// Return pCode[nIdx], or nullptr if nIdx is out of bounds
320 FormulaToken
* TokenAt( sal_uInt16 nIdx
) const
327 /// Peek at nIdx-1 if not out of bounds, decrements nIdx if successful. Returns NULL if not.
328 FormulaToken
* PeekPrev( sal_uInt16
& nIdx
) const;
330 /// Return the opcode at pCode[nIdx-1], ocNone if nIdx-1 is out of bounds
331 OpCode
OpCodeBefore( sal_uInt16 nIdx
) const
333 if (nIdx
== 0 || nIdx
> nLen
)
336 return pCode
[nIdx
-1]->GetOpCode();
339 FormulaToken
* FirstRPNToken() const;
340 FormulaToken
* LastRPNToken() const;
342 bool HasReferences() const;
344 bool HasExternalRef() const;
345 bool HasOpCode( OpCode
) const;
346 bool HasOpCodeRPN( OpCode
) const;
347 /// Token of type svIndex or opcode ocColRowName
348 bool HasNameOrColRowName() const;
351 * Check if the token array contains any of specified opcode tokens.
353 * @param rOpCodes collection of opcodes to check against.
355 * @return true if the token array contains at least one of the specified
356 * opcode tokens, false otherwise.
358 bool HasOpCodes( const unordered_opcode_set
& rOpCodes
) const;
360 /// Assign pRPN to point to a newly created array filled with the data from pData
361 void CreateNewRPNArrayFromData( FormulaToken
** pData
, sal_uInt16 nSize
)
363 pRPN
= new FormulaToken
*[ nSize
];
365 memcpy( pRPN
, pData
, nSize
* sizeof( FormulaToken
* ) );
368 FormulaToken
** GetArray() const { return pCode
.get(); }
370 FormulaTokenArrayStandardRange
Tokens() const
372 return FormulaTokenArrayStandardRange(pCode
.get(), nLen
);
375 FormulaToken
** GetCode() const { return pRPN
; }
377 FormulaTokenArrayStandardRange
RPNTokens() const
379 return FormulaTokenArrayStandardRange(pRPN
, nRPN
);
382 FormulaTokenArrayReferencesRange
References() const
384 return FormulaTokenArrayReferencesRange(*this);
387 sal_uInt16
GetLen() const { return nLen
; }
388 sal_uInt16
GetCodeLen() const { return nRPN
; }
389 FormulaError
GetCodeError() const { return nError
; }
390 void SetCodeError( FormulaError n
) { nError
= n
; }
391 void SetHyperLink( bool bVal
) { bHyperLink
= bVal
; }
392 bool IsHyperLink() const { return bHyperLink
; }
394 ScRecalcMode
GetRecalcMode() const { return nMode
; }
396 void SetCombinedBitsRecalcMode( ScRecalcMode nBits
)
397 { nMode
|= nBits
& ~ScRecalcMode::EMask
; }
398 ScRecalcMode
GetCombinedBitsRecalcMode() const
399 { return nMode
& ~ScRecalcMode::EMask
; }
401 /** Exclusive bits already set in nMode are zero'ed, nBits
402 may contain combined bits, but only one exclusive bit
404 void SetMaskedRecalcMode( ScRecalcMode nBits
)
405 { nMode
= GetCombinedBitsRecalcMode() | nBits
; }
407 /** Bits aren't set directly but validated and handled
408 according to priority if more than one exclusive bit
410 void AddRecalcMode( ScRecalcMode nBits
);
412 void ClearRecalcMode() { nMode
= ScRecalcMode::NORMAL
; }
413 void SetExclusiveRecalcModeNormal()
414 { SetMaskedRecalcMode( ScRecalcMode::NORMAL
); }
415 void SetExclusiveRecalcModeAlways()
416 { SetMaskedRecalcMode( ScRecalcMode::ALWAYS
); }
417 void SetRecalcModeForced()
418 { nMode
|= ScRecalcMode::FORCED
; }
419 void SetRecalcModeOnRefMove()
420 { nMode
|= ScRecalcMode::ONREFMOVE
; }
421 bool IsRecalcModeNormal() const
422 { return bool(nMode
& ScRecalcMode::NORMAL
); }
423 bool IsRecalcModeAlways() const
424 { return bool(nMode
& ScRecalcMode::ALWAYS
); }
425 bool IsRecalcModeForced() const
426 { return bool(nMode
& ScRecalcMode::FORCED
); }
427 bool IsRecalcModeOnRefMove() const
428 { return bool(nMode
& ScRecalcMode::ONREFMOVE
); }
429 /** Whether recalculation must happen after import, for
431 bool IsRecalcModeMustAfterImport() const
432 { return (nMode
& ScRecalcMode::EMask
) <= ScRecalcMode::ONLOAD_ONCE
; }
433 void ClearRecalcModeMustAfterImport()
435 if (IsRecalcModeMustAfterImport() && !IsRecalcModeAlways())
436 SetExclusiveRecalcModeNormal();
439 /** Get OpCode of the most outer function */
440 inline OpCode
GetOuterFuncOpCode() const;
442 /** Operators +,-,*,/,^,&,=,<>,<,>,<=,>=
443 with DoubleRef in Formula? */
444 bool HasMatrixDoubleRefOps() const;
446 virtual FormulaToken
* AddOpCode(OpCode e
);
448 /** Adds the single token to array.
449 Derived classes must override it when they want to support derived classes from FormulaToken.
450 @return true when an error occurs
452 virtual bool AddFormulaToken(
453 const css::sheet::FormulaToken
& rToken
, svl::SharedStringPool
& rSPool
,
454 ExternalReferenceHelper
* pExtRef
);
456 /** fill the array with the tokens from the sequence.
457 It calls AddFormulaToken for each token in the list.
458 @param _aSequence the token to add
459 @return true when an error occurs
462 const css::uno::Sequence
<css::sheet::FormulaToken
>& rSequence
,
463 svl::SharedStringPool
& rSPool
, ExternalReferenceHelper
* pExtRef
);
466 * Do some checking based on the individual tokens. For now, we use this
467 * only to check whether we can vectorize the token array.
469 virtual void CheckToken( const FormulaToken
& t
);
472 * Call CheckToken() for all RPN tokens.
474 void CheckAllRPNTokens();
476 /** Clones the token and then adds the clone to the pCode array.
477 For just new'ed tokens use Add() instead of cloning it again.
478 Use this AddToken() when adding a token from another origin.
480 FormulaToken
* AddToken( const FormulaToken
& );
482 FormulaToken
* AddString( const svl::SharedString
& rStr
);
483 FormulaToken
* AddDouble( double fVal
);
484 void AddExternal( const sal_Unicode
* pStr
);
485 /** Xcl import may play dirty tricks with OpCode!=ocExternal.
487 FormulaToken
* AddExternal( const OUString
& rStr
, OpCode eOp
= ocExternal
);
488 FormulaToken
* AddBad( const OUString
& rStr
); /// ocBad with OUString
489 FormulaToken
* AddStringXML( const OUString
& rStr
); /// ocStringXML with OUString, temporary during import
491 virtual FormulaToken
* MergeArray( );
493 /** Assignment with incrementing references of FormulaToken entries
495 FormulaTokenArray
& operator=( const FormulaTokenArray
& );
496 FormulaTokenArray
& operator=( FormulaTokenArray
&& );
498 /** Determines if this formula needs any changes to convert it to something
499 previous versions of OOo could consume (Plain Old Formula, pre-ODFF, or
501 bool NeedsPodfRewrite( const MissingConventionODF
& rConv
);
503 /** Determines if this formula needs any changes to convert it to OOXML. */
504 bool NeedsOoxmlRewrite();
506 /** Rewrites to Plain Old Formula or OOXML, substituting missing parameters. The
507 FormulaTokenArray* returned is new'ed. */
508 FormulaTokenArray
* RewriteMissing( const MissingConvention
& rConv
);
510 /** Determines if this formula may be followed by a reference. */
511 bool MayReferenceFollow();
513 /** Re-intern SharedString in case the SharedStringPool differs. */
514 void ReinternStrings( svl::SharedStringPool
& rPool
);
517 inline OpCode
FormulaTokenArray::GetOuterFuncOpCode() const
520 return pRPN
[nRPN
-1]->GetOpCode();
524 inline FormulaTokenArrayReferencesIterator
FormulaTokenArrayReferencesRange::begin()
526 return FormulaTokenArrayReferencesIterator(mrFTA
.Tokens());
529 inline FormulaTokenArrayReferencesIterator
FormulaTokenArrayReferencesRange::end()
531 return FormulaTokenArrayReferencesIterator::endOf(mrFTA
.Tokens());
534 class FORMULA_DLLPUBLIC FormulaTokenIterator
539 const FormulaTokenArray
* pArr
;
543 Item(const FormulaTokenArray
* arr
, short pc
, short stop
);
546 std::vector
<Item
> maStack
;
549 FormulaTokenIterator( const FormulaTokenArray
& );
550 ~FormulaTokenIterator();
552 const FormulaToken
* Next();
553 const FormulaToken
* PeekNextOperator();
554 bool IsEndOfPath() const; /// if a jump or subroutine path is done
555 bool HasStacked() const { return maStack
.size() > 1; }
556 short GetPC() const { return maStack
.back().nPC
; }
558 /** Jump or subroutine call.
559 Program counter values will be incremented before code is executed =>
560 positions are to be passed with -1 offset.
562 Start on code at position nStart+1 (yes, pass with offset -1)
564 After subroutine continue with instruction at position nNext+1
566 Stop before reaching code at position nStop. If not specified the
567 default is to either run the entire code, or to stop if an ocSep or
568 ocClose is encountered, which are only present in ocIf or ocChoose
571 void Jump( short nStart
, short nNext
, short nStop
= SHRT_MAX
);
572 void Push( const FormulaTokenArray
* );
575 /** Reconstruct the iterator afresh from a token array
577 void ReInit( const FormulaTokenArray
& );
580 const FormulaToken
* GetNonEndOfPathToken( short nIdx
) const;
583 // For use in SAL_INFO, SAL_WARN etc
585 template<typename charT
, typename traits
>
586 inline std::basic_ostream
<charT
, traits
> & operator <<(std::basic_ostream
<charT
, traits
> & stream
, const FormulaTokenArray
& point
)
589 static_cast<const void*>(&point
) <<
590 ":{nLen=" << point
.GetLen() <<
591 ",nRPN=" << point
.GetCodeLen() <<
592 ",pCode=" << static_cast<void*>(point
.GetArray()) <<
593 ",pRPN=" << static_cast<void*>(point
.GetCode()) <<
599 class FORMULA_DLLPUBLIC FormulaTokenArrayPlainIterator
602 const FormulaTokenArray
* mpFTA
;
603 sal_uInt16 mnIndex
; // Current step index
606 FormulaTokenArrayPlainIterator( const FormulaTokenArray
& rFTA
) :
617 sal_uInt16
GetIndex() const
622 FormulaToken
* First()
628 void Jump(sal_uInt16 nIndex
)
639 FormulaToken
* Next();
640 FormulaToken
* NextNoSpaces();
641 FormulaToken
* GetNextName();
642 FormulaToken
* GetNextReference();
643 FormulaToken
* GetNextReferenceRPN();
644 FormulaToken
* GetNextReferenceOrName();
645 FormulaToken
* GetNextColRowName();
646 FormulaToken
* PeekNext();
647 FormulaToken
* PeekPrevNoSpaces() const; /// Only after Reset/First/Next/Last/Prev!
648 FormulaToken
* PeekNextNoSpaces() const; /// Only after Reset/First/Next/Last/Prev!
650 FormulaToken
* FirstRPN()
656 FormulaToken
* NextRPN();
658 FormulaToken
* LastRPN()
660 mnIndex
= mpFTA
->GetCodeLen();
664 FormulaToken
* PrevRPN();
666 void AfterRemoveToken( sal_uInt16 nOffset
, sal_uInt16 nCount
);
672 #endif // INCLUDED_FORMULA_TOKENARRAY_HXX
674 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */