set a bunch of svn:executable properties
[couchdbimport.git] / CouchProjects / Fabric / OperatorNodes.h
blob6c6484311c8fbfc2bd2d071dd03ed5a69c5512a0
1 /*
2 Fabric formula engine
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
25 public:
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();
40 if (elements != 0) {
41 nonNilArgs++;
43 if (elements > maxElements) {
44 maxElements = args[i]->size();
48 if (nonNilArgs == 0) {
49 return NULL;
51 else if (nonNilArgs == 1) {
52 // only a single argument is nonnil, so we can't parallel process
53 // find and return it
54 for (size_t i = 0; i < argCount; i++) {
55 if (args[i]->size() != 0) {
56 return args[i];
59 ASSERT(false); //shouldn't be able to hit here
60 return NULL;
62 else {
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));
75 try {
76 pReturnList->Add( ParallelProcessElements(processElements) );
77 } catch(RuntimeException& e) {
78 pReturnList->Add( Element(e));
82 return pReturnList;
86 virtual const Element ParallelProcessElements(fvector<Element>& elements) const
89 fvector<Element>::iterator itor;
90 bool hasText = false;
91 for (itor = elements.begin(); itor != elements.end(); itor++) {
92 if ((*itor)->GetType() == TEXT) {
93 hasText = true;
94 break;
98 if (hasText) {
99 size_t newLen=0;
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];
110 char* p = pBuffer;
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);
122 else {
123 double result = 0;
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
136 public:
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) {
150 return &listA;
153 return NULL;
157 class OpListConcatenate : public RuntimeNode
159 public:
160 const List* Execute(StackFrame& frame) const
162 size_t argCount = frame.ArgCount();
163 const List** args = frame.ArgArray();
164 size_t newSize = 0;
165 size_t nonNilLists = 0;
166 for (size_t i=0; i<argCount; i++) {
167 size_t argSize = args[i]->size();
168 if (argSize) {
169 nonNilLists++;
170 newSize += argSize;
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()) {
178 return args[i];
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);
191 return pNewValue;
197 class OpEqualEqual : public RuntimeNode
199 public:
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
212 public:
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
225 public:
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();
235 // nil handling
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));
250 else {
251 try {
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
263 // no matches
264 return f->FalseList();
268 // this abstract class is for operators which take
269 // two lists and pairwise compares them and return
270 // a single boolean
271 class BinaryComparisonOperator : public RuntimeNode
273 public:
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());
281 int result = 0;
282 for (size_t i=0; i < maxElements; i++) {
283 result = Element::Collate(a[i], b[i]);
284 if (result != 0) {
285 break;
289 if (result == 0) {
290 result = cmp(a.size(), b.size());
293 if (ToBool(result)) {
294 return f->TrueList();
296 else {
297 return f->FalseList();
301 virtual bool ToBool(int result) const = 0;
305 class OpLess : public BinaryComparisonOperator
307 public:
308 bool ToBool(int result) const
310 return result < 0;
314 class OpLessEqual : public BinaryComparisonOperator
316 public:
317 bool ToBool(int result) const
319 return result <= 0;
323 class OpGreater : public BinaryComparisonOperator
325 public:
326 bool ToBool(int result) const
328 return result > 0;
332 class OpGreaterEqual : public BinaryComparisonOperator
334 public:
335 bool ToBool(int result) const
337 return result >= 0;
341 class OpNegate : public SingleListProcess
343 public:
344 const Element ProcessElement(const Element& element) const
346 double anumber = element->ToNumber();
347 return NumberElement(-anumber);
351 class OpNot : public RuntimeNode
353 public:
354 const List* Execute(StackFrame& frame) const
356 const List& list = frame.GetArg(0);
358 if (list.ToBool()) {
359 return f->FalseList();
361 else {
362 return f->TrueList();
368 class OpMultiply : public DualListProcess
370 public:
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) {
383 return &listA;
385 else {
386 return &listB;
392 class OpDivide : public DualListProcess
394 public:
396 virtual const Element ProcessElements(const Element& a, const Element& b) const
398 double a_dbl = a->ToNumber();
399 double b_dbl = b->ToNumber();
400 if (b_dbl != 0) {
401 return NumberElement(a_dbl / b_dbl);
403 else {
404 return NumberElement(std::numeric_limits<float>::quiet_NaN());
408 virtual const List* NilListEvent(const List& listA, const List& listB) const
410 return &listA;
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()) {
424 if (index > 0) {
425 return index - 1;
427 else {
428 return (long)list.size() + index;
431 else {
432 return -1;
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
442 return &list;
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++) {
452 try {
453 long index = FixIndex(indexList[i]->ToLong(), list);
454 if (index != -1) {
455 pReturnList->Add(list[index]);
457 } catch (RuntimeException& e) {
458 pReturnList->Add(e);
462 return pReturnList;
464 else {
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) {
472 startIndex = 1;
475 if (endIndex < startIndex) {
476 return NULL;
479 startIndex = FixIndex(startIndex, list);
481 if (startIndex == -1 ) {
482 // too big
483 return NULL;
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]);
503 return pReturnList;
508 class OpRegExp : public RuntimeNode, Destructable
510 public:
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();
519 int regexpFlags = 0;
521 ASSERT(*pPattern == '/'); // must start with a slash
523 pPattern++;
524 patternLen--;
526 if (pPattern[patternLen - 1] == '/') {
527 patternLen -= 1;
529 else if (pPattern[patternLen - 1] == 'i') {
530 ASSERT(pPattern[patternLen - 2] == '/');
531 regexpFlags = UREGEX_CASE_INSENSITIVE;
532 patternLen -= 2;
534 else {
535 // the token is supposed to end in either "/" or "/i"
536 ASSERT(FALSE);
539 utf8_convert(pPattern, patternLen, patternUni);
541 UParseError pe;
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);
572 if (didMatch) {
573 // return the 1 based offest as the result
574 return new(ma) List(ma, NumberElement(1.0 + i));
577 return f->FalseList();
580 void Destruct()
582 uregex_close(re);
586 class OpFieldConcatenation : public RuntimeNode
588 public:
589 TextElement m_id;
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
610 else {
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);
621 else {
622 long lastFieldIndex = frame.GetArg(0)[0]->ToLong();
624 if (lastFieldIndex < 0 || lastFieldIndex < m_firstFieldIndex) {
625 return NULL;
628 return f->GetDocument().CombineFields(ma, m_id, m_firstFieldIndex, lastFieldIndex);
634 class OpPlusPlus : public SingleListProcess
636 public:
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);
648 return pReturnList;
651 const Element ProcessElement(const Element& element) const
653 return NumberElement(element->ToNumber() + 1.0);
658 class OpMinusMinus : public OpPlusPlus
660 public:
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
672 public:
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();
681 else {
682 // the is the second arg. its true. flag that we hit true and the
683 frame.boolVal = true;
684 return NULL;
687 else {
688 frame.boolVal = false;
689 return NULL;
693 const List* Execute(StackFrame& frame) const
695 return (frame.boolVal ? f->TrueList() : f->FalseList());
699 class OpOr : public RuntimeNode
701 public:
702 const RuntimeNode* NextNodeToExecute(StackFrame& frame, const RuntimeNode* pPreviousExecuteNode, const List* pPreviousValue) const
704 if (pPreviousValue->ToBool()) {
705 frame.boolVal = true;
706 return NULL;
708 else {
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
722 public:
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());