3 Copyright (C) 2006 Damien Katz
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 class OpPlus
: public RuntimeNode
28 const List
* Execute(StackFrame
& frame
) const
30 size_t argCount
= frame
.ArgCount();
31 const List
** args
= frame
.ArgArray();
32 fvector
<Element
> processElements(ma
);
33 processElements
.reserve(argCount
);
35 size_t maxElements
= 0;
36 size_t nonNilArgs
= 0;
38 for (size_t i
= 0; i
< argCount
; i
++) {
39 size_t elements
= args
[i
]->size();
43 if (elements
> maxElements
) {
44 maxElements
= args
[i
]->size();
48 if (nonNilArgs
== 0) {
51 else if (nonNilArgs
== 1) {
52 // only a single argument is nonnil, so we can't parallel process
54 for (size_t i
= 0; i
< argCount
; i
++) {
55 if (args
[i
]->size() != 0) {
59 ASSERT(false); //shouldn't be able to hit here
63 List
* pReturnList
= new(f
->ma
) List(f
->ma
);
64 pReturnList
->Reserve(maxElements
);
66 for (size_t i
=0; i
<maxElements
; i
++) {
67 processElements
.clear();
69 for (size_t j
= 0; j
< argCount
; j
++) {
70 if (args
[j
]->size()) {
71 processElements
.push_back(args
[j
]->At(i
));
76 pReturnList
->Add( ParallelProcessElements(processElements
) );
77 } catch(RuntimeException
& e
) {
78 pReturnList
->Add( Element(e
));
86 virtual const Element
ParallelProcessElements(fvector
<Element
>& elements
) const
89 fvector
<Element
>::iterator itor
;
91 for (itor
= elements
.begin(); itor
!= elements
.end(); itor
++) {
92 if ((*itor
)->GetType() == TEXT
) {
100 // convert all elements to text
101 for (itor
= elements
.begin(); itor
!= elements
.end(); itor
++) {
102 // this just returns the same TextElement if already a TextElement
103 TextElement element
= (*itor
)->ToText(ma
);
104 *itor
= element
; // assign back to vector element
106 newLen
+= element
.Length(); // add length
109 char* pBuffer
= new(ma
) char[newLen
];
112 for (itor
= elements
.begin(); itor
!= elements
.end(); itor
++) {
113 TextElement element
= (*itor
)->ToText(ma
);
114 memcpy(p
, element
.Ptr(), element
.Length());
115 p
+= element
.Length();
118 ASSERT(p
== (pBuffer
+ newLen
));
120 return Element(pBuffer
, newLen
);
124 // convert all elements to a number
125 for (itor
= elements
.begin(); itor
!= elements
.end(); itor
++) {
126 // this just returns the same NumberElement if already a NumberElement
127 result
+= (*itor
)->ToNumber();
129 return NumberElement(result
);
134 class OpMinus
: public DualListProcess
138 virtual const Element
ProcessElements(const Element
& a
, const Element
& b
) const
140 double a_dbl
= a
->ToNumber();
141 double b_dbl
= b
->ToNumber();
142 return NumberElement(a_dbl
- b_dbl
);
145 //override to provide special different return value on nil list
146 virtual const List
* NilListEvent(const List
& listA
, const List
& listB
) const
149 if (listA
.size() > 0) {
157 class OpListConcatenate
: public RuntimeNode
160 const List
* Execute(StackFrame
& frame
) const
162 size_t argCount
= frame
.ArgCount();
163 const List
** args
= frame
.ArgArray();
165 size_t nonNilLists
= 0;
166 for (size_t i
=0; i
<argCount
; i
++) {
167 size_t argSize
= args
[i
]->size();
174 if (nonNilLists
== 1) {
175 // only one non-nil list, find it and return it
176 for (size_t i
=0; i
<argCount
; i
++) {
177 if (args
[i
]->size()) {
183 List
* pNewValue
= new(ma
) List(ma
);
184 pNewValue
->Reserve(newSize
);
186 for (size_t i
=0; i
<argCount
; i
++) {
187 const List
* pList
= args
[i
];
188 pNewValue
->Append(*pList
);
197 class OpEqualEqual
: public RuntimeNode
201 const List
* Execute(StackFrame
& frame
) const
203 const List
& a
= frame
.GetArg(0);
204 const List
& b
= frame
.GetArg(1);
206 return f
->BoolList(a
.Equals(b
));
210 class OpNotEqual
: public RuntimeNode
214 const List
* Execute(StackFrame
& frame
) const
216 const List
& a
= frame
.GetArg(0);
217 const List
& b
= frame
.GetArg(1);
219 return f
->BoolList(!a
.Equals(b
));
223 class OpCaseInsensitiveMatch
: public RuntimeNode
227 const List
* Execute(StackFrame
& frame
) const
229 const List
& a
= frame
.GetArg(0);
230 size_t sizeA
= a
.size();
232 const List
& b
= frame
.GetArg(1);
233 size_t sizeB
= b
.size();
236 if (sizeA
== 0 || sizeB
== 0) {
237 return f
->FalseList(); // false is zero
239 for (size_t i
=0; i
< sizeA
; i
++) {
240 const Element elementA
= a
[i
];
241 ElementType typeA
= elementA
->GetType();
242 for (size_t j
=0; j
< sizeB
; j
++) {
243 const Element elementB
= b
[j
];
244 ElementType typeB
= elementB
->GetType();
245 if (typeA
== typeB
) {
246 if (elementA
.EqualsI(elementB
)) {
247 return new(ma
) List(ma
, NumberElement(1.0 + i
));
252 if (elementB
.EqualsI(elementA
->ConvertTo(ma
, typeB
))) {
253 return new(ma
) List(ma
, NumberElement(1.0 + i
));
256 catch (Element::ConversionException
& ) {
257 //ignore conversion exceptions
264 return f
->FalseList();
268 // this abstract class is for operators which take
269 // two lists and pairwise compares them and return
271 class BinaryComparisonOperator
: public RuntimeNode
274 const List
* Execute(StackFrame
& frame
) const
276 const List
& a
= frame
.GetArg(0);
277 const List
& b
= frame
.GetArg(1);
279 size_t maxElements
= std::min(a
.size(), b
.size());
282 for (size_t i
=0; i
< maxElements
; i
++) {
283 result
= Element::Collate(a
[i
], b
[i
]);
290 result
= cmp(a
.size(), b
.size());
293 if (ToBool(result
)) {
294 return f
->TrueList();
297 return f
->FalseList();
301 virtual bool ToBool(int result
) const = 0;
305 class OpLess
: public BinaryComparisonOperator
308 bool ToBool(int result
) const
314 class OpLessEqual
: public BinaryComparisonOperator
317 bool ToBool(int result
) const
323 class OpGreater
: public BinaryComparisonOperator
326 bool ToBool(int result
) const
332 class OpGreaterEqual
: public BinaryComparisonOperator
335 bool ToBool(int result
) const
341 class OpNegate
: public SingleListProcess
344 const Element
ProcessElement(const Element
& element
) const
346 double anumber
= element
->ToNumber();
347 return NumberElement(-anumber
);
351 class OpNot
: public RuntimeNode
354 const List
* Execute(StackFrame
& frame
) const
356 const List
& list
= frame
.GetArg(0);
359 return f
->FalseList();
362 return f
->TrueList();
368 class OpMultiply
: public DualListProcess
372 virtual const Element
ProcessElements(const Element
& a
, const Element
& b
) const
374 double a_dbl
= a
->ToNumber();
375 double b_dbl
= b
->ToNumber();
376 return NumberElement(a_dbl
* b_dbl
);
380 virtual const List
* NilListEvent(const List
& listA
, const List
& listB
) const
382 if (listA
.size() > 0) {
392 class OpDivide
: public DualListProcess
396 virtual const Element
ProcessElements(const Element
& a
, const Element
& b
) const
398 double a_dbl
= a
->ToNumber();
399 double b_dbl
= b
->ToNumber();
401 return NumberElement(a_dbl
/ b_dbl
);
404 return NumberElement(std::numeric_limits
<float>::quiet_NaN());
408 virtual const List
* NilListEvent(const List
& listA
, const List
& listB
) const
417 class OpArraySubscript
: public RuntimeNode
420 // convert an 1 based signed index to positive, 0 based index
421 long FixIndex(long index
, const List
& list
) const
423 if (abs(index
) > 0 && abs(index
) <= (long)list
.size()) {
428 return (long)list
.size() + index
;
436 const List
* Execute(StackFrame
& frame
) const
438 const List
& list
= frame
.GetArg(0);
440 if (list
.size() == 0) {
441 // if nil list, all index operations result in nil regardless
445 if (frame
.ArgCount() == 2) {
446 const List
& indexList
= frame
.GetArg(1);
448 List
* pReturnList
= new(ma
) List(ma
);
449 pReturnList
->Reserve(indexList
.size());
451 for (size_t i
=0; i
< indexList
.size(); i
++) {
453 long index
= FixIndex(indexList
[i
]->ToLong(), list
);
455 pReturnList
->Add(list
[index
]);
457 } catch (RuntimeException
& e
) {
465 ASSERT(frame
.ArgCount() == 3);
467 // we only use the first elements of the 2 start and stop indexes
468 long startIndex
= frame
.GetArg(1)[0]->ToLong();
469 long endIndex
= frame
.GetArg(2)[0]->ToLong();
471 if (startIndex
<= 0) {
475 if (endIndex
< startIndex
) {
479 startIndex
= FixIndex(startIndex
, list
);
481 if (startIndex
== -1 ) {
486 endIndex
= FixIndex(endIndex
, list
);
488 if (endIndex
== -1) {
489 // too big, shrink to fit
490 endIndex
= (long)list
.size() - 1;
493 List
* pReturnList
= new(ma
) List(ma
);
495 if (startIndex
<= endIndex
) {
496 pReturnList
->Reserve(endIndex
- startIndex
+ 1);
498 for (long i
= startIndex
; i
<= endIndex
; i
++) {
499 pReturnList
->Add(list
[i
]);
508 class OpRegExp
: public RuntimeNode
, Destructable
511 URegularExpression
* re
;
513 void CompilerValidateArgsAndInit()
515 std::vector
<UChar
> patternUni
;
516 TextElement patternText
= GetTokenText();
517 const char* pPattern
= patternText
.Ptr();
518 int patternLen
= patternText
.LenInt();
521 ASSERT(*pPattern
== '/'); // must start with a slash
526 if (pPattern
[patternLen
- 1] == '/') {
529 else if (pPattern
[patternLen
- 1] == 'i') {
530 ASSERT(pPattern
[patternLen
- 2] == '/');
531 regexpFlags
= UREGEX_CASE_INSENSITIVE
;
535 // the token is supposed to end in either "/" or "/i"
539 utf8_convert(pPattern
, patternLen
, patternUni
);
542 UErrorCode status
= U_ZERO_ERROR
;
543 re
= uregex_open(&patternUni
[0], (int)patternUni
.size(), regexpFlags
, &pe
, &status
);
544 if (U_FAILURE(status
)) {
545 char* pErrMrg
= new(ma
) char[200];
546 snprintf(pErrMrg
, 200, "Pattern error at position %d: %s \n",
547 pe
.offset
, u_errorName(status
));
548 throw Formula::SyntaxException(pErrMrg
);
551 f
->AddToDestructionQueue(this); // this causes out destruct method to be called
552 //once the whole formula is destroyed
555 const List
* Execute(StackFrame
& frame
) const
557 const List
& srcList
= frame
.GetArg(0);
558 size_t srcListSize
= srcList
.size();
559 if (srcListSize
== 0) {
560 return f
->FalseList();
562 for (size_t i
=0; i
<srcListSize
; i
++) {
563 TextElement src
= srcList
[i
]->ToText(ma
);
564 std::vector
<UChar
> srcUni
;
565 utf8_convert(src
, srcUni
);
566 UErrorCode status
= U_ZERO_ERROR
;
568 uregex_setText(re
, &srcUni
[0], (int)srcUni
.size(), &status
);
569 UBool didMatch
= uregex_matches(re
, 0, &status
);
570 CHECK_STATUS(status
);
573 // return the 1 based offest as the result
574 return new(ma
) List(ma
, NumberElement(1.0 + i
));
577 return f
->FalseList();
586 class OpFieldConcatenation
: public RuntimeNode
590 long m_firstFieldIndex
;
592 void CompilerValidateArgsAndInit()
594 TextElement id
= m_pFirstChild
->GetTokenText();
596 m_id
= id
.ExtractTrailingNumber(&m_firstFieldIndex
);
597 if (m_firstFieldIndex
< 0) {
598 throw Formula::SyntaxException("Identifier must end in digit (0-9)");
603 virtual const RuntimeNode
* FirstNodeToExecute(StackFrame
& frame
) const
605 // we never the execute the first arg
607 if (m_numChildren
== 1) {
608 return NULL
; // its the * node
611 return m_pFirstChild
->GetNextSibling();
616 const List
* Execute(StackFrame
& frame
) const
618 if (m_numChildren
== 1) {
619 return f
->GetDocument().CombineFields(ma
, m_id
, m_firstFieldIndex
, -1);
622 long lastFieldIndex
= frame
.GetArg(0)[0]->ToLong();
624 if (lastFieldIndex
< 0 || lastFieldIndex
< m_firstFieldIndex
) {
628 return f
->GetDocument().CombineFields(ma
, m_id
, m_firstFieldIndex
, lastFieldIndex
);
634 class OpPlusPlus
: public SingleListProcess
638 const List
* Execute(StackFrame
& frame
) const
640 ASSERT(frame
.ArgCount() == 1);
642 const List
* pReturnList
= SingleListProcess::Execute(frame
);
644 // Now assign pReturnList value back to the variable from where it came
646 f
->SetVariableValue( m_pFirstChild
->GetTokenIdAtom(), pReturnList
);
651 const Element
ProcessElement(const Element
& element
) const
653 return NumberElement(element
->ToNumber() + 1.0);
658 class OpMinusMinus
: public OpPlusPlus
661 // override OpPlusPlus's ProcessElement method
662 const Element
ProcessElement(const Element
& element
) const
664 return NumberElement(element
->ToNumber() - 1.0);
670 class OpAnd
: public RuntimeNode
674 const RuntimeNode
* NextNodeToExecute(StackFrame
& frame
, const RuntimeNode
* pPreviousExecuteNode
, const List
* pPreviousValue
) const
676 if (pPreviousValue
->ToBool()) {
677 if (pPreviousExecuteNode
== m_pFirstChild
) {
678 // this is the first arg. Tell it to execute the second arg
679 return m_pFirstChild
->GetNextSibling();
682 // the is the second arg. its true. flag that we hit true and the
683 frame
.boolVal
= true;
688 frame
.boolVal
= false;
693 const List
* Execute(StackFrame
& frame
) const
695 return (frame
.boolVal
? f
->TrueList() : f
->FalseList());
699 class OpOr
: public RuntimeNode
702 const RuntimeNode
* NextNodeToExecute(StackFrame
& frame
, const RuntimeNode
* pPreviousExecuteNode
, const List
* pPreviousValue
) const
704 if (pPreviousValue
->ToBool()) {
705 frame
.boolVal
= true;
709 frame
.boolVal
= false;
710 return pPreviousExecuteNode
->GetNextSibling();
714 const List
* Execute(StackFrame
& frame
) const
716 return (frame
.boolVal
? f
->TrueList() : f
->FalseList());
720 class OpIn
: public RuntimeNode
724 const List
* Execute(StackFrame
& frame
) const
726 const List
& sublist
= frame
.GetArg(0);
727 const List
& list
= frame
.GetArg(1);
729 return (list
.Contains(sublist
) ? f
->TrueList() : f
->FalseList());