Initial revision
[TestXSLT.git] / libsablot / src / engine / expr.cpp
1 /* 
2  * The contents of this file are subject to the Mozilla Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/MPL/
6  * 
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  * 
12  * The Original Code is the Sablotron XSLT Processor.
13  * 
14  * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15  * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16  * Ginger Alliance Ltd. All Rights Reserved.
17  * 
18  * Contributor(s): Marc Lehmann <pcg@goof.com>
19  * 
20  * Alternatively, the contents of this file may be used under the
21  * terms of the GNU General Public License Version 2 or later (the
22  * "GPL"), in which case the provisions of the GPL are applicable 
23  * instead of those above.  If you wish to allow use of your 
24  * version of this file only under the terms of the GPL and not to
25  * allow others to use your version of this file under the MPL,
26  * indicate your decision by deleting the provisions above and
27  * replace them with the notice and other provisions required by
28  * the GPL.  If you do not delete the provisions above, a recipient
29  * may use your version of this file under either the MPL or the
30  * GPL.
31  */
32
33 #include "expr.h"
34 #include "verts.h"
35 #include <float.h>
36 #include <math.h>
37 #include "context.h"
38 #include "tree.h"
39 #include "utf8.h"
40 #include "domprovider.h"
41 #include "guard.h"
42
43 // GP: clean.....
44
45 // period is not here as it can also start a number
46 char tokenShort[]=  
47 ".. :: // != <= >= "
48 "(  )  [  ]  @  ,  "
49 "|  +  -  =  <  >  "
50 "/  *  "
51 "\x0\x0\x0";
52
53 ExToken tokenShortX[]=
54 {
55     TOK_DPERIOD, TOK_DCOLON, TOK_DSLASH, TOK_NEQ, TOK_LE, TOK_GE,
56         TOK_LPAREN, TOK_RPAREN, TOK_LBRACKET, TOK_RBRACKET, TOK_ATSIGN, TOK_COMMA,
57         TOK_VERT, TOK_PLUS, TOK_MINUS, TOK_EQ, TOK_LT, TOK_GT,
58         TOK_SLASH, TOK_STAR
59 };
60
61 /*================================================================
62     function info table
63 ================================================================*/
64
65 struct FuncInfoItem 
66 {
67     const char *name;
68     ExFunctor func;
69     ExType type;
70
71 funcInfoTable[] =
72 {
73     // XPath functions - NODESET category
74     {"last",EXFF_LAST,EX_NUMBER},
75     {"position",EXFF_POSITION,EX_NUMBER},
76     {"count",EXFF_COUNT,EX_NUMBER},
77     {"id",EXFF_ID,EX_NODESET},
78     {"local-name",EXFF_LOCAL_NAME,EX_STRING},
79     {"namespace-uri",EXFF_NAMESPACE_URI,EX_STRING},
80     {"name",EXFF_NAME,EX_STRING},
81
82     // XPath - STRING category
83     {"string",EXFF_STRING,EX_STRING},
84     {"concat",EXFF_CONCAT,EX_STRING},
85     {"starts-with",EXFF_STARTS_WITH,EX_BOOLEAN},
86     {"contains",EXFF_CONTAINS,EX_BOOLEAN},
87     {"substring-before",EXFF_SUBSTRING_BEFORE,EX_STRING},
88     {"substring-after",EXFF_SUBSTRING_AFTER,EX_STRING},
89     {"substring",EXFF_SUBSTRING,EX_STRING},
90     {"string-length",EXFF_STRING_LENGTH,EX_NUMBER},
91     {"normalize-space",EXFF_NORMALIZE_SPACE,EX_STRING},
92     {"translate",EXFF_TRANSLATE,EX_STRING},
93
94     // XPath - BOOLEAN category
95     {"boolean",EXFF_BOOLEAN,EX_BOOLEAN},
96     {"not",EXFF_NOT,EX_BOOLEAN},
97     {"true",EXFF_TRUE,EX_BOOLEAN},
98     {"false",EXFF_FALSE,EX_BOOLEAN},
99     {"lang",EXFF_LANG,EX_BOOLEAN},
100
101     // XPath - NUMBER category
102     {"number", EXFF_NUMBER, EX_NUMBER},
103     {"sum", EXFF_SUM, EX_NUMBER},
104     {"floor", EXFF_FLOOR, EX_NUMBER},
105     {"ceiling", EXFF_CEILING, EX_NUMBER},
106     {"round", EXFF_ROUND, EX_NUMBER},
107
108     // XSLT core
109     {"document",EXFF_DOCUMENT,EX_NODESET},
110     {"key",EXFF_KEY,EX_NODESET},
111     {"format-number",EXFF_FORMAT_NUMBER, EX_STRING},
112     {"current",EXFF_CURRENT, EX_NODESET},
113     {"unparsed-entity-uri",EXFF_UNPARSED_ENTITY_URI, EX_STRING},
114     {"generate-id",EXFF_GENERATE_ID,EX_STRING},
115     {"system-property",EXFF_SYSTEM_PROPERTY, EX_STRING},
116
117     // XSLT extensions
118     {"function-available",EXFF_FUNCTION_AVAILABLE,EX_BOOLEAN},
119     {"element-available",EXFF_ELEMENT_AVAILABLE,EX_BOOLEAN},
120     {NULL, EXFF_NONE, EX_UNKNOWN}
121 };
122
123 Str getFuncName(ExFunctor functor)
124 {
125     return funcInfoTable[functor - EXF_FUNCTION - 1].name;
126 }
127
128 /**********************************************************
129 TokenItem
130 **********************************************************/
131
132 void TokenItem::speak(DStr &s, SpeakMode mode)
133 {
134     switch(tok)
135     {
136     case TOK_VAR:   // remove leading $
137         s.nadd(firstc + 1, len - 1);
138         break;
139     case TOK_LITERAL:// remove enclosing quotes or dquotes
140         s.nadd(firstc + 1, len - 2);
141         break;
142     default:
143         s.nadd(firstc,len);
144     };
145 //    s += '\0'; - thrown out
146 };
147
148 /**********************************************************
149
150   Tokenizer
151
152 **********************************************************/
153
154 Tokenizer::Tokenizer(Expression &owner_)
155 :
156 items(LIST_SIZE_EXPR_TOKENS), owner(owner_)
157 {
158 };
159
160 Tokenizer::~Tokenizer()
161 {
162     items.freeall(FALSE);
163 }
164
165 eFlag Tokenizer::tokenize(Sit S, const Str &astring)
166 {
167     char *p;
168     TokenItem item;
169     string = astring;
170     p = (char *) string;
171
172     E( getToken(S, p, item, TOK_NONE) );
173     ExToken prevToken = item.tok;
174     while ((item.tok != TOK_END) && (item.tok != TOK_NONE))
175     {
176         items.append(new TokenItem(item));
177         E( getToken(S, p, item, prevToken) );        
178         prevToken = item.tok;
179     };
180     
181     if (item.tok == TOK_NONE)
182     {
183         DStr itemStr;
184         item.speak(itemStr, SM_OFFICIAL);
185         Err1(S, ET_BAD_TOKEN, itemStr);
186     }
187     else
188         items.append(new TokenItem(item));
189
190     return OK;
191 }
192
193 /*================================================================
194 namerTable
195 a table of tokens which have the effect that the following
196 name is recognized as a NCName (rather than operator name) and * is
197 recognized as a wildcard (rather than multiplication operator).
198     The table should end with TOK_NONE (which is of this type too).
199 ================================================================*/
200
201 static ExToken namerTable[] = {
202     TOK_ATSIGN, TOK_DCOLON, TOK_LPAREN, TOK_LBRACKET,
203         // operators:
204     TOK_OR, TOK_AND, TOK_EQ, TOK_NEQ, TOK_LT, TOK_GT, TOK_LE, TOK_GE,
205     TOK_PLUS, TOK_MINUS, TOK_MINUS1, TOK_MULT, TOK_DIV, TOK_MOD, TOK_VERT,
206         // slashes are operators too but not for us
207     TOK_SLASH, TOK_DSLASH, TOK_COMMA,
208         // TOK_NONE (terminator)
209     TOK_NONE};
210
211 /*================================================================
212 Bool isNamer()
213 returns True iff the token is contained in the namerTable table.
214 ================================================================*/
215
216 static Bool isNamer(ExToken tok)
217 {
218     int i;
219     if (tok == TOK_NONE) return TRUE;
220     for (i = 0; (namerTable[i] != tok) && 
221         (namerTable[i] != TOK_NONE); i++);
222     return (Bool) (namerTable[i] == tok);
223 }
224
225 /*================================================================
226 ExToken tryShort()
227 looks up a few characters at p in the tokenShort table containing
228 the (up-to-3-char) symbols
229 RETURNS the token identified, or TOK_NONE if no match is found
230 ================================================================*/
231
232 ExToken Tokenizer::tryShort(char*& p, ExToken prevToken)
233 {
234     int i;
235     char* t;
236     ExToken result;
237     
238     for (i=0, t=tokenShort; *t; i++,t+=3)
239         if (*p==*t)
240         if ((t[1] == ' ') || (t[1] == p[1])) break;
241     if (*t)
242     {
243         p += ((t[1] == ' ')? 1 : 2);
244         result = tokenShortX[i];
245         if (result == TOK_STAR)
246             result = (isNamer(prevToken)? TOK_NAME : TOK_MULT);
247         if ((result == TOK_MINUS) && isNamer(prevToken))
248             result = TOK_MINUS1;
249     }
250     else result = TOK_NONE;
251     return result;
252 }
253
254 /*================================================================
255 eFlag lookToken()
256 sets 'ret' to the following token, but does not change the pointer p
257 ================================================================*/
258
259 eFlag Tokenizer::lookToken(Sit S, ExToken &ret, char* p, ExToken prevToken)
260 {
261     // getToken_() changes p but this is passed by value so
262     // remains unchanged
263
264     E( getToken_(S, ret, p, prevToken) );
265     return OK;
266 }
267
268 /*================================================================
269 Bool findChar
270 sets p to address FOLLOWING the next occurence of c in p
271 (to skip a character reference, use findChar(p,';'))
272 RETURNS false iff the next occurence was not found
273 ================================================================*/
274
275 static Bool findChar(char*& p, char c)
276 {
277     while (*p && (*p != c)) p++;
278     if (*p)
279     {
280         p++;
281         return TRUE;
282     }
283     else
284         return FALSE;
285 }
286
287 /*================================================================
288 findSame
289 sets p to address FOLLOWING the next occurence of *p in p
290 RETURNS false iff another occurence was not found
291 ================================================================*/
292
293 static Bool findSame(char*& p)
294 {
295     char first = *p++;
296     return findChar(p, first);
297 };
298
299 eFlag Tokenizer::getToken_(Sit S, ExToken &ret, char*& p, ExToken prevToken)
300 {
301     ExToken tok;
302     char c;
303     
304     skipWhite(p);
305     if (!*p)
306     {
307         ret = TOK_END; 
308         return OK;
309     }
310     else
311     {
312         // the following may also translate * into TOK_NAME
313         if ((tok = tryShort(p, prevToken)) != TOK_NONE)
314         {
315             ret = tok;
316             return OK;
317         };
318         switch (c = *p)
319         {
320         case '$': 
321             {
322                 // call getName with prevToken=TOK_NONE
323                 // to ensure that e.g. 'and' in '$and' is treated as a name
324                 E( getName(S, ret, ++p, TOK_NONE) );
325                 if (ret != TOK_NONE)
326                     ret = TOK_VAR;
327                 else 
328                     Err(S, ET_BAD_VAR);
329             };
330             break;
331         case '\"':
332         case '\'': 
333             if(!findSame(p))
334                 Err(S, ET_INFINITE_LITERAL)
335             else
336                 ret = TOK_LITERAL;
337             break;
338         case '&': assert(0);  //DBG: do not process entity references so far
339             break;
340         case '.':
341             if (utf8IsDigit(utf8CharCode(p+1)))
342             {
343                 E( getNumber(S, p) );
344                 ret = TOK_NUMBER;
345             }
346             else {
347                 p++; 
348                 ret = TOK_PERIOD;
349             };
350             break;
351         default:
352             {
353                 if (utf8IsDigit(utf8CharCode(p)))
354                 {
355                     E( getNumber(S, p) );
356                     ret = TOK_NUMBER;
357                 }
358                 else
359                 {
360                     if (utf8IsLetter(utf8CharCode(p)) || (*p == '_') || (*p == ':'))
361                     {
362                         // the following call finds TOK_NAME, TOK_FNAME,
363                         // TOK_AXISNAME,
364                         // as well as TOK_AND etc. (based on prev token)
365                         E( getName(S, ret, p, prevToken) ); 
366                     }
367                     else 
368                     {
369                         Str temp;
370                         temp.nset(p, 1);
371                         Err1(S, ET_BAD_TOKEN, temp); //unknown token
372                     }
373                 }
374             };  //default
375         };      //switch
376     };          //else
377     return OK;
378 };              //getToken_
379
380 eFlag Tokenizer::getToken(Sit S, char*& p, TokenItem& item, ExToken prevToken)
381 {
382     ExToken t;
383     skipWhite(p);
384     item.firstc = p;
385     E( getToken_(S, t, p, prevToken) );
386     item.len = (long)(p - item.firstc);
387     item.tok = t;
388     return OK;
389 }
390
391 eFlag Tokenizer::getNumber(Sit S, char*& p)
392 {
393     Bool wasDot = FALSE;
394     while ((*p) && (utf8IsDigit(utf8CharCode(p))) || (*p == '.'))
395     {
396         if (*p == '.')
397             if (wasDot)
398             Err(S, ET_BAD_NUMBER)
399             else wasDot = TRUE;
400         p += utf8SingleCharLength(p);
401     };
402     return OK;
403 };
404
405 /*================================================================
406 getWordOp
407 checks whether the sequence at p of given length is an operator name
408 RETURNS the appropriate token if so; TOK_NONE otherwise
409 ================================================================*/
410
411 static ExToken getWordOp(char *p, int length)
412 {
413     if (length > 3) return TOK_NONE;
414     if (length < 2) length = 2;
415     if (!strncmp(p,"or",length)) return TOK_OR;
416     if (length < 3) length = 3;
417     if (!strncmp(p,"and",length)) return TOK_AND;
418     if (!strncmp(p,"div",length)) return TOK_DIV;
419     if (!strncmp(p,"mod",length)) return TOK_MOD;
420     return TOK_NONE;
421 }
422
423 static Bool isNodeTest(char *p, int length)
424 {
425     const char *q;
426     int qlen;
427     for (int i = 0; (q = exNodeTypeNames[i]) != NULL; i++)
428     {
429         if (!strncmp(q,p,
430             (length < (qlen = strlen(q))? qlen : length)))
431             break;
432     };
433     return (Bool)(q != NULL);
434 }
435
436 #define nameCharExtended(CH, PTR) ((CH = utf8CharCode(PTR))!= 0) &&\
437         (utf8IsNameChar(CH) || strchr(".-_:*",CH))
438
439 int nameLength(char* from)
440 {
441     char *q = from;
442     int length = 0;
443     unsigned long c;
444     while(nameCharExtended(c,q)) {
445        q += utf8SingleCharLength(q);
446        length++;
447     }
448     return length;
449 }
450
451 eFlag Tokenizer::getName(Sit S, ExToken &ret, char*& p, ExToken prevToken)
452 {
453     char *former = p;
454     unsigned long c;
455     BOOL wasColon = FALSE;
456     
457     if ((!utf8IsLetter(utf8CharCode(p))) && (*p != '_'))
458     {
459         ret = TOK_NONE;
460         return OK;
461     }
462
463     while (nameCharExtended(c,p))
464     {
465         if (c == ':')
466         {
467             if (wasColon)
468             {
469                 // identify the bad qname;
470                 Str theName;
471                 theName.nset(former, nameLength(former));
472                 Err1(S, E1_EXTRA_COLON, theName);
473             }
474             else
475             {
476                 switch(*(p+1))
477                 {
478                 case ':':
479                     {
480                         ret = TOK_AXISNAME;
481                         return OK;
482                     };
483                 case '*':
484                     {
485                         ret = TOK_NAME;
486                         p += 2;
487                         return OK;
488                     };
489                 default:
490                     wasColon = TRUE;
491                 };
492             };
493         }
494         else if (c == '*')
495         {
496             if ((p - former) && *(p - 1) != ':')   // the first condition could be dropped
497             {
498                 ret = TOK_NAME;
499                 return OK;
500             }
501         }
502         p += utf8SingleCharLength (p);
503     }
504
505     if (!wasColon && !isNamer(prevToken))
506     {
507         if ((ret = getWordOp(former, (int) (p - former))) != TOK_NONE)
508             return OK;
509     };
510
511     ExToken next;
512
513     // look at following token with prev=TOK_NAME
514     E( lookToken(S, next,p,TOK_NAME) );
515     switch(next)
516     {
517     case TOK_DCOLON:
518         ret = TOK_AXISNAME;
519         break;
520     case TOK_LPAREN:
521         {
522             if (isNodeTest(former, (int) (p - former)))
523                 ret = TOK_NTNAME;
524             else
525                 ret = TOK_FNAME;
526         }; break;
527     default:
528         ret = TOK_NAME;
529     };
530     return OK;
531 };
532
533 /*================================================================
534 int findTop()
535     finds the first top-level occurence of 'token' starting with
536     position 'from'. If there is none, return value points at TOK_END.
537 ================================================================*/
538
539 int Tokenizer::findTop(ExToken token, int from)
540 {
541     int level = 0;
542     ExToken ct;
543     int i;
544     for (i = from; 
545         ((ct = items[i] -> tok) != TOK_END) && (level || (ct != token));
546         i++)
547         {
548             if ((ct == TOK_LPAREN) || (ct == TOK_LBRACKET))
549                 level++;
550             if ((ct == TOK_RPAREN) || (ct == TOK_RBRACKET))
551                 level--;
552         }
553     return i;
554 }
555
556
557 /*================================================================
558 eFlag getDelim()
559 given a position in 'pos', finds the corresponding next token.
560 If the left token is ( or [, looks for the matching right paren/bracket,
561 Otherwise looks for the occurence of the same token. 
562 Returns pos pointing at the matching token, or at TOK_END if there 
563 is none. (In case of failed reverse search, returns -1.)
564 ================================================================*/
565
566 eFlag Tokenizer::getDelim(Sit S, int &pos, Bool reverse /*=FALSE*/)
567 {
568     ExToken first, second, tok;
569     int level = 0,
570         i = pos;
571     
572     switch(first = items[pos] -> tok)
573     {
574     case TOK_LBRACKET: 
575         second = TOK_RBRACKET; 
576         break;
577     case TOK_LPAREN: 
578         second = TOK_RPAREN; 
579         break;
580     case TOK_RBRACKET: 
581         second = TOK_LBRACKET; 
582         break;
583     case TOK_RPAREN: 
584         second = TOK_LPAREN; 
585         break;
586     default: 
587         second = first;
588     }
589     
590     i += reverse? -1 : 1;
591     
592     while ((i >= 0) && ((tok = items[i] -> tok) != TOK_END))
593     {
594         if (tok == second)
595         {
596             if (!level)
597             {
598                 pos = i;
599                 return OK;
600             }
601             else level--;
602         }
603         else if (tok == first) level++;
604         i += reverse? -1 : 1;
605     }
606     pos = i;
607     return OK;
608 }
609
610 /*================================================================
611 stripParens()
612 given the left and right end positions of a tokenizer fragment, 
613 shifts them inwards to strip any outer parentheses.
614 ================================================================*/
615
616 eFlag Tokenizer::stripParens(Sit S, int &left, int &right)
617 {
618     int left0 = left;
619     if (items[right]->tok == TOK_END)
620         right--;
621 //    assert(left <= right);
622     while ((items[left]->tok == TOK_LPAREN)
623         && (items[right]->tok == TOK_RPAREN))
624     {
625         left0 = left;
626         E( getDelim(S, left0) );
627         if (left0 == right)
628         {
629             left++; 
630             right--;
631         }
632         else break;
633     };
634     return OK;
635 }
636
637 void Tokenizer::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
638 {
639     owner.report(S, type, code, arg1, arg2);
640 }
641
642
643 /*****************************************************************
644 *                                                                *
645       L o c S t e p 
646 *                                                                *
647 *****************************************************************/
648
649 LocStep::LocStep(Element& ownerV_, ExAxis _axis       /*=AXIS_NONE*/, 
650                  ExNodeType _ntype  /*=EXNODE_NONE*/)
651 : preds(LIST_SIZE_1), ownerV(ownerV_)
652 {
653     set(_axis, _ntype);
654     positional = FALSE;
655     badPreds = 0;
656 }
657
658 LocStep::~LocStep()
659 {
660     preds.freeall(FALSE);
661 }
662
663 void LocStep::set(ExAxis _axis, ExNodeType _ntype)
664 {
665     ax = _axis;
666     ntype = _ntype;
667 }
668
669 void LocStep::speak(Sit S, DStr &strg, SpeakMode mode)
670 {
671     if (!(mode & SM_CONTENTS)) return;
672     switch(ax)
673     {
674     case AXIS_CHILD:
675 //    case AXIS_DESC_OR_SELF:
676     case AXIS_ROOT:
677         break;
678     case AXIS_ATTRIBUTE:
679         strg += '@';
680         break;
681     default:
682         {
683             strg += axisNames[ax];
684             strg += "::";
685         };
686     };
687     if((ntype != EXNODE_NONE) //&& (ax != AXIS_DESC_OR_SELF)
688         && (ax != AXIS_ROOT))
689     {
690         strg += exNodeTypeNames[ntype];
691         strg += "()";
692     }
693     else
694     {
695             Str fullName;
696             getOwnerElement().getOwner().expandQStr(ntest,fullName);
697         // ntest.speak(S, strg,mode);
698             strg += fullName;
699         }
700     int i, predsNumber = preds.number();
701     for (i = 0; i < predsNumber; i++)
702     {
703         strg += '[';
704         preds[i] -> speak(S, strg,mode);
705         strg += ']';
706     };
707 };
708
709 eFlag LocStep::parse(Sit S, Tokenizer& tokens, int& pos,
710                      Bool defaultToo /* = FALSE */)
711 {
712     int right;
713     int &i = pos;
714     DStr temp;
715     ExToken tok;
716     
717     tok = tokens.items[i++]->tok;
718     if (tok == TOK_END)
719         Err(S, ET_EMPTY_PATT);
720     switch (tok)
721     {
722     case TOK_PERIOD:
723         ax = AXIS_SELF;
724         ntype = EXNODE_NODE;
725         return OK;
726         break;
727     case TOK_DPERIOD:
728         ax = AXIS_PARENT;
729         ntype = EXNODE_NODE;
730         return OK;
731         break;
732     case TOK_ATSIGN:
733         {
734             ax = AXIS_ATTRIBUTE;
735             tok = tokens.items[i++]->tok;
736         };
737         break;
738     case TOK_STAR:
739         {
740             ax = AXIS_CHILD;
741         };
742         break;
743     case TOK_AXISNAME:
744         {
745             tokens.items[i-1] -> speak(temp, SM_OFFICIAL);
746             if ((ax = (ExAxis) lookup(temp, axisNames)) == AXIS_NONE)
747                 Err1(S, E1_UNKNOWN_AXIS, temp);
748             i++;
749             tok = tokens.items[i++] -> tok;
750         };
751         break;
752     case TOK_NAME:
753     case TOK_NTNAME:
754         {
755           //      if (!defaultAx)
756           //  Err(S, ET_EXPR_SYNTAX);
757           ax = AXIS_CHILD;
758         };
759         break;
760     default:
761         Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
762     };
763     
764     // axis has been determined; tok must now be a name or a node-test
765     temp.empty();
766     if ((tok != TOK_NAME) && (tok != TOK_NTNAME))
767         Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
768     tokens.items[i-1] -> speak(temp,SM_OFFICIAL);
769     ntype = EXNODE_NONE;
770
771     if (tok == TOK_NTNAME)
772     {
773         ntype = (ExNodeType) lookup(temp, exNodeTypeNames);
774         if (tokens.items[i++]->tok != TOK_LPAREN)
775             Err(S, ET_LPAREN_EXP);
776         
777         //pi test may hav one literal param
778         if (ntype == EXNODE_PI && tokens.items[i]->tok == TOK_LITERAL) {
779           DStr pilit;
780           tokens.items[i++]->speak(pilit, SM_CONTENTS);
781           piname = pilit;
782         }
783
784         if (tokens.items[i++]->tok != TOK_RPAREN)
785           {
786             Err(S, ET_RPAREN_EXP);
787           }
788     }
789     else
790     {
791         // set the QName from prefix:uri using
792         // the namespace declarations in 'ownerE'
793         // (passed through from the containing attribute)
794         // DEFAULT: without using the default namespace
795         E( getOwnerElement().setLogical(S, ntest, temp, defaultToo) ); 
796         }
797
798     while ((tokens.items[i] -> tok) == TOK_LBRACKET)
799     {
800         badPreds = 0;
801         E( tokens.getDelim(S, right = i) );
802         if (tokens.items[right] -> tok == TOK_END)
803             Err(S, ET_RBRACKET_EXP)
804         else 
805         {
806             GP( Expression ) ex = new Expression(getOwnerElement());
807             E( (*ex).parse(S, tokens,i+1,right-1) );
808             // find out about the use of last() and position()
809             int exPositionalType = (*ex).optimizePositional(0);
810             if (exPositionalType)
811             {
812                 positional = TRUE;
813                 if (exPositionalType == 2)
814                     badPreds++;
815                 // find sufficient position bounds
816                 (*ex).optimizePositionBounds();
817             }
818             preds.append(ex.keep());
819         };
820         i = right + 1;
821     };
822
823     return OK;  // pointing at the first char that does not start a predicate
824
825 };      //  end LocStep::parse()
826
827
828 Bool LocStep::matchesWithoutPreds(Sit S, NodeHandle v)
829 {
830     // removed the following:
831     // assert(v);
832     // (because the parent-of-root calls etc.)
833     if (nhNull(v))
834         return FALSE;
835     // ANDed with VT_BASE for ordinary vertices
836     SXP_NodeType ty = S.dom().getNodeType(v); 
837     switch(ntype)
838     {
839     case EXNODE_NODE:
840         break;
841     case EXNODE_TEXT:
842         if (ty != TEXT_NODE)
843             return FALSE;
844         break;
845     case EXNODE_PI:
846         if (ty != PROCESSING_INSTRUCTION_NODE) 
847             return FALSE;
848         break;
849     case EXNODE_COMMENT:
850         if (ty != COMMENT_NODE)
851             return FALSE;
852         break;
853     case EXNODE_NONE:
854         if ((ty == TEXT_NODE) || (ty == COMMENT_NODE) || 
855             (ty == DOCUMENT_NODE) || (ty == PROCESSING_INSTRUCTION_NODE))
856           return FALSE;
857         break;
858     };
859
860     switch(ax)
861     {
862     case AXIS_ROOT: 
863         return (Bool) (ty == DOCUMENT_NODE); 
864         break;
865     case AXIS_ATTRIBUTE: 
866         if (ty != ATTRIBUTE_NODE) return FALSE; 
867         break;
868     case AXIS_NAMESPACE: 
869         if (ty != NAMESPACE_NODE) return FALSE; 
870         break;
871     case AXIS_CHILD: 
872     case AXIS_DESCENDANT:
873     case AXIS_DESC_OR_SELF:
874     case AXIS_ANCESTOR:
875     case AXIS_ANC_OR_SELF:
876     case AXIS_FOLL_SIBLING:
877     case AXIS_PREC_SIBLING:
878     case AXIS_FOLLOWING:
879     case AXIS_PRECEDING:
880         switch (ty)
881         {
882         case ATTRIBUTE_NODE:
883         case NAMESPACE_NODE:
884             return FALSE;
885         case DOCUMENT_NODE:
886             switch(ax)
887             {
888             case AXIS_DESC_OR_SELF:
889             case AXIS_ANCESTOR:
890             case AXIS_ANC_OR_SELF:
891                 break;
892             default:
893                 return FALSE;
894             };
895         };
896         break;
897     case AXIS_SELF:
898       {
899         if (ntype == EXNODE_NONE && ty != ELEMENT_NODE) return false;
900       }; break;
901     case AXIS_PARENT:
902         break;
903     default: assert(0); //should be handled in parse
904     };
905
906     //if (ntype != EXNODE_NONE)
907     //    return TRUE;
908     switch (ntype) 
909       {
910       case EXNODE_NONE:
911         break;
912       case EXNODE_PI:
913         {
914           if (! S.domExternal(v) && ! (piname == ""))
915             {
916               ProcInstr *pi = toPI(v);
917               EQName ename;
918               pi->getOwner().expandQ(pi->name, ename);
919               return ename.getLocal() == piname;
920             }
921           else
922             return TRUE;
923         };
924       default:
925         return TRUE;
926       }
927
928     //exnode_none are processed here
929     if (S.domExternal(v))
930       {
931         const char *uri = S.dom().getNodeNameURI(v);
932         const char *local = S.dom().getNodeNameLocal(v);
933         Bool ret =  getOwnerElement().getOwner()
934           .cmpQNameStrings(ntest, uri, local);
935         S.dom().freeName(v, (char*)uri);
936         S.dom().freeName(v, (char*)local);
937         return ret;
938       }
939     else
940       {
941         const QName &hisname = toV(v) -> getName();
942         return getOwnerElement().getOwner()
943           .cmpQNamesForeign(ntest, toV(v) -> dict(), hisname);
944       }
945 }
946
947
948 eFlag LocStep::shift(Sit S, NodeHandle &v, NodeHandle baseV)
949 {
950     NodeHandle w = NULL;       // the result
951     switch(ax)
952     {
953     case AXIS_ATTRIBUTE:
954     {
955         assert(S.domExternal(v) || nhNull(v) || 
956                isElement(baseV) && baseV == toV(v) -> parent);
957         // i.e. v==NULL and baseV isn't daddy
958         if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break; 
959         for (w = (!nhNull(v) ? 
960                   S.dom().getNextAttrNS(v) : 
961                   S.dom().getAttributeNo(baseV, 0)); 
962              !nhNull(w) && !matchesWithoutPreds(S, w);
963              w = S.dom().getNextAttrNS(w));
964     }; break;    
965                     
966     case AXIS_CHILD:
967     {
968         assert(S.domExternal(v) || nhNull(v) || 
969                isDaddy(baseV) && baseV == toV(v) -> parent);
970         if (S.dom().getNodeType(baseV) != ELEMENT_NODE &&
971             S.dom().getNodeType(baseV) != DOCUMENT_NODE) break; 
972         for (w = !nhNull(v) ?
973                S.dom().getNextSibling(v) : 
974                S.dom().getFirstChild(baseV);
975              !nhNull(w) && !matchesWithoutPreds(S, w);
976              w = S.dom().getNextSibling(w));
977     }; break;
978         
979     case AXIS_NAMESPACE:
980     {
981         assert(S.domExternal(v) || nhNull(v) || 
982                isElement(baseV) && baseV == toV(v) -> parent);
983         if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break;
984         for (w = !nhNull(v) ? 
985                S.dom().getNextAttrNS(v) : 
986                S.dom().getNamespaceNo(baseV, 0); 
987              !nhNull(w) && !matchesWithoutPreds(S, w);
988              w = S.dom().getNextAttrNS(w));
989     }; break;
990         
991     case AXIS_ROOT:     // technically not an axis
992     {
993         if (nhNull(v))
994         {
995             w = S.dom().getOwnerDocument(baseV);
996             if (nhNull(w)) // w must have been the root itself
997                 w = baseV;
998         }
999     }; break;
1000         
1001     case AXIS_SELF:
1002     {
1003         if (nhNull(v) && matchesWithoutPreds(S, baseV))
1004             w = baseV;
1005     }; break;
1006         
1007     case AXIS_PARENT:
1008     {
1009         if (nhNull(v) && matchesWithoutPreds(S, S.dom().getParent(baseV)))
1010             w = S.dom().getParent(baseV);
1011     }; break;
1012         
1013     case AXIS_ANCESTOR:
1014     case AXIS_ANC_OR_SELF:
1015     {
1016         if (!nhNull(v)) 
1017             w = S.dom().getParent(v);
1018         else
1019             w = (ax == AXIS_ANCESTOR) ? S.dom().getParent(baseV) : baseV;
1020         for (; !nhNull(w) && !matchesWithoutPreds(S, w); 
1021                w = S.dom().getParent(w));
1022     }; break;
1023         
1024     case AXIS_FOLL_SIBLING:
1025     {
1026         switch(S.dom().getNodeType(baseV))
1027         {
1028         case DOCUMENT_NODE:
1029         case NAMESPACE_NODE:
1030         case ATTRIBUTE_NODE:
1031             break;
1032         default:
1033             for (w = S.dom().getNextSibling(!nhNull(v) ? v : baseV);
1034                  !nhNull(w) && !matchesWithoutPreds(S, w);
1035                  w = S.dom().getNextSibling(w));                                
1036         }
1037     }; break;
1038         
1039     case AXIS_PREC_SIBLING:
1040     {
1041         switch(S.dom().getNodeType(baseV))
1042         {
1043         case DOCUMENT_NODE:
1044         case NAMESPACE_NODE:
1045         case ATTRIBUTE_NODE:
1046             break;
1047         default:
1048             for (w = S.dom().getPreviousSibling(!nhNull(v) ? v : baseV);
1049                  !nhNull(w) && !matchesWithoutPreds(S, w);
1050                  w = S.dom().getPreviousSibling(w));                            
1051         }
1052     }; break;
1053         
1054     case AXIS_DESCENDANT:
1055     case AXIS_DESC_OR_SELF:
1056     {
1057         if (nhNull(v))
1058         {
1059             if (ax == AXIS_DESC_OR_SELF && matchesWithoutPreds(S, baseV))
1060             {
1061                 w = baseV;
1062                 break;
1063             }
1064             else
1065                 v = baseV;
1066         }
1067             
1068         // find next descendant
1069         do
1070         {
1071             if ((S.dom().getNodeType(v) == ELEMENT_NODE ||
1072                  (S.dom().getNodeType(v) == DOCUMENT_NODE)) &&
1073                 S.dom().getChildCount(v))
1074               v = S.dom().getFirstChild(v);
1075             else
1076             {
1077                 while (v != baseV)
1078                 {
1079                     SXP_Node v0 = v;
1080                     v = S.dom().getNextSibling(v);
1081                     if (!nhNull(v))
1082                         break;
1083                     else
1084                         v = S.dom().getParent(v0);
1085                 }
1086             };
1087             if (v != baseV && matchesWithoutPreds(S, v))
1088             {
1089                 w = v;
1090                 break;
1091             };
1092         }
1093         while(v != baseV);
1094     }; break;
1095     case AXIS_FOLLOWING:
1096     {
1097       do {
1098         if (!nhNull(v) && S.dom().getChildCount(v))
1099           w = S.dom().getChildNo(v,0);
1100         else
1101           {
1102             if (nhNull(v)) 
1103               v = baseV;
1104             while (!nhNull(v) && nhNull(S.dom().getNextSibling(v)))
1105               v = S.dom().getParent(v);
1106             if (!nhNull(v))
1107               w = S.dom().getNextSibling(v);
1108             else
1109               w = NULL;
1110           }
1111         v = w;
1112       } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1113     }; break;
1114     case AXIS_PRECEDING:
1115     {
1116       do {
1117         v = !nhNull(v) ? v : baseV;
1118         w = S.dom().getPreviousSibling(v);
1119         if (!nhNull(w)) {
1120           int childCount;
1121           while (0 != (childCount = S.dom().getChildCount(w)))
1122             w = S.dom().getChildNo(w,childCount - 1);
1123         } else {
1124           w = S.dom().getParent(v);
1125           //eliminate ancestors of baseV
1126           for (v = S.dom().getParent(baseV); 
1127                !nhNull(v) && v != w; 
1128                v = S.dom().getParent(v));
1129           //we're in the ancestor line
1130           if (!nhNull(v)) {
1131             while (!nhNull(v) && !(S.dom().getPreviousSibling(v)))
1132               v = S.dom().getParent(v);
1133             if (!nhNull(v)) 
1134               w = S.dom().getPreviousSibling(v);
1135             else
1136               w = NULL;
1137           }
1138         }
1139         v = w;
1140       } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1141     }; break;
1142     default:
1143         assert(0); 
1144     };
1145     v = w;
1146     return OK;
1147 };
1148
1149 void LocStep::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
1150 {
1151     ownerV.report(S, type, code, arg1, arg2);
1152 }
1153
1154
1155
1156 /**********************************************************
1157 N u m b e r
1158 **********************************************************/
1159
1160 Number::Number()
1161 {
1162     *this = 0.0;
1163 }
1164
1165 Number::Number(double y)
1166 {
1167     *this = y;
1168 }
1169
1170 Number& 
1171 Number::operator= (double y)
1172 {
1173     x = y;
1174     return *this;
1175 };
1176
1177 Number& 
1178 Number::operator= (const Str &s)
1179 {
1180     char *endptr, *startptr = s;
1181     skipWhite(startptr);
1182     if (*startptr)
1183     {
1184         x = strtod(startptr, &endptr);
1185         if (endptr) {
1186           skipWhite(endptr);
1187           if (*endptr)
1188             setNaN();
1189         }
1190     }
1191     else
1192         setNaN();
1193     return *this;
1194 };
1195
1196 Number::operator double() const
1197 {
1198     return x;
1199 }
1200
1201 Bool Number::operator== (double y)
1202 {
1203     if (isNaN() || isnan__(y))
1204         return FALSE;
1205     if (isInf() || isinf__(y))
1206         return isInf() && isinf__(y) && !((x > 0) ^ (y > 0));
1207     double d = x - y;
1208     return (Bool)((d < EPS) && (d > -EPS));
1209 }
1210
1211 Bool Number::operator== (Number& y)
1212 {
1213     if (isNaN()) return FALSE;
1214     return (Bool)(operator== ((double) y));
1215 }
1216
1217
1218 Bool Number::operator< (double y) 
1219 {
1220     return (Bool)(x < y);
1221 }
1222
1223 Bool Number::operator< (Number& y)
1224 {
1225     return (Bool)(x < (double) y);
1226 }
1227
1228 Bool Number::operator> (double y)
1229 {
1230     return (Bool)(x > y);
1231 }
1232
1233 Bool Number::operator> (Number& y)
1234 {
1235     return (Bool)(x > (double) y);
1236 }
1237
1238 Bool Number::isNaN()
1239 {
1240     return isnan__(x);
1241 }
1242
1243 Bool Number::isInf()
1244 {
1245     return isinf__(x);
1246 };
1247
1248 void Number::setNaN()
1249 {
1250     // divide by zero using a variable, to fool too clever compilers
1251     // 'volatile' suggested by Dirk Siebnich
1252     volatile int zero = 0;
1253     x = 0.0 / zero;
1254 }
1255
1256 int Number::round()
1257 {
1258     if (isNaN() || isInf())
1259         return 0;
1260     else return (int)(floor(x + 0.5)); // FIXME: ignoring the 'negative zero'
1261 }
1262
1263
1264 //________________________________________________________________
1265
1266 /*****************************************************************
1267 |                                                                |
1268     JS - External
1269 |                                                                |
1270 *****************************************************************/
1271
1272 #ifdef ENABLE_JS
1273 External::External(void *prv, void *val) 
1274 {
1275   priv = new JSExternalPrivate(prv, val);
1276 }
1277
1278 External::External(External& other)
1279   : priv(NULL)
1280 {
1281   assign(other);
1282 }
1283
1284
1285 External::~External() 
1286
1287   decref(); 
1288 }
1289
1290 void* External::getValue() 
1291
1292   return priv->value; 
1293 };
1294
1295 void External::assign(const External& other)
1296 {
1297   if (priv) decref(); 
1298   priv = other.priv; priv->refcnt++;
1299 }
1300
1301 void External::decref() 
1302 {
1303   if (priv) 
1304     {
1305       priv->refcnt--; 
1306       if (!priv->refcnt) 
1307         { 
1308           delete priv; 
1309           priv = NULL;
1310         }
1311     }
1312 }
1313 #endif
1314
1315 /*****************************************************************
1316 |                                                                |
1317     E x p r e s s i o n
1318 |                                                                |
1319 *****************************************************************/
1320
1321 Expression::Expression(Element &owner_,
1322                        ExFunctor _functor   /* = EXF_NONE   */
1323                        )
1324 : args(1), owner(owner_)
1325 {
1326     functor = _functor;
1327     switch(functor)
1328     {
1329     case EXF_LOCSTEP:
1330         {
1331             step = new LocStep(owner_);
1332             type = EX_NODESET;
1333         }; break;
1334     case EXF_LOCPATH:
1335         {
1336             type = EX_NODESET;
1337         }; break;
1338     case EXF_STRINGSEQ:
1339         type = EX_STRING;
1340         break;
1341     default:
1342         {
1343             type = EX_UNKNOWN;
1344         }; break;
1345     };
1346     hasPath = FALSE;
1347     isPattern = FALSE;
1348     pTree = NULL;
1349     // the following sets patomnodeset, note that e.g. patomnumber
1350     // is with it in a union
1351     patomnodeset = NULL;
1352     usesLast = FALSE;
1353     positional = FALSE;
1354     optimizePositionFrom = optimizePositionTo = 0; // see header
1355 }
1356
1357 void Expression::setLS(ExAxis _axis, ExNodeType _ntype)
1358 {
1359     assert(functor == EXF_LOCPATH);
1360     Expression *ls = new Expression(getOwnerElement(), EXF_LOCSTEP); // GP: OK
1361     args.append(ls);
1362     ls -> step -> set(_axis, _ntype);    
1363 }
1364
1365 //
1366 //  Expression destructor
1367 //  
1368 Expression::~Expression()
1369 {
1370     clearContent();
1371 }
1372
1373 #define deleteZ( PTR )    { cdelete( PTR ); } 
1374
1375 //
1376 //  clearContent()
1377 //  called when setting the expression to a new value
1378 //  to dispose of any existing contents.
1379 //
1380 void Expression::clearContent()
1381 {
1382     args.freeall(FALSE);
1383     switch(functor)
1384     {
1385     case EXF_ATOM:
1386         {
1387             switch(type)
1388             {
1389             case EX_NODESET:
1390                 deleteZ ( patomnodeset );
1391                 break;
1392             case EX_STRING:
1393                 deleteZ ( patomstring );
1394                 break;
1395             case EX_NUMBER:
1396                 deleteZ ( patomnumber );
1397                 break;
1398 #ifdef ENABLE_JS
1399             case EX_EXTERNAL:
1400                 deleteZ( patomexternal);
1401                 break;
1402 #endif
1403             };
1404         }; break;
1405     case EXF_LOCSTEP:
1406         deleteZ ( step );
1407         break;
1408     case EXF_VAR:
1409     case EXF_OTHER_FUNC:
1410         deleteZ ( pName );
1411         break;
1412     }
1413     cdelete( pTree );
1414 };
1415
1416
1417
1418 /*================================================================
1419 speak
1420     writes the expression to string 'strg'. Formatting is specified
1421     by 'mode'.
1422 ================================================================*/
1423
1424 eFlag Expression::speak(Sit S, DStr &strg, SpeakMode mode)
1425 {
1426     int i, argsNumber = args.number();
1427     switch(functor)
1428     {
1429     case EXF_ATOM:
1430         {
1431                 Str temp;
1432                     E( tostring(S, temp) );
1433             strg += temp;
1434         }; break;
1435     case EXF_LOCSTEP:
1436         {
1437             step -> speak(S, strg, mode);
1438         }; break;
1439     case EXF_LOCPATH:
1440         {
1441             for(i = 0; i < argsNumber; i++)
1442             {
1443                 args[i] -> speak(S, strg, mode);
1444                 if (i < argsNumber-1) 
1445                     strg += "/";
1446                 else if ((argsNumber == 1) && 
1447                     (args[0] -> step -> ax == AXIS_ROOT))
1448                     strg += "/";
1449             }
1450         }; break;
1451     default:
1452         {
1453             strg += (DStr("\nfunctor ") + (int) functor + "\n--------ARGS:\n");
1454             for (i = 0; i < argsNumber; i++)
1455             {
1456                 strg += DStr("(") + (i+1) + ")   ";
1457                 args[i] -> speak(S, strg, mode);
1458                 strg += "\n";
1459             };
1460             strg += "--------ARGS end\n";
1461         }
1462     };
1463     return OK;
1464 }
1465
1466 /*================================================================
1467 matches
1468     returns TRUE iff the current vertex of c satisfies the
1469     Expression's condition.
1470     PROBLEM:
1471     perhaps this should also return an error in case the expression is
1472     not of type nodeset?
1473 ================================================================*/
1474
1475 eFlag Expression::matchesPattern(Sit S, Context *c, Bool &result)
1476 {
1477     assert(type == EX_NODESET);
1478     if (functor == EXF_LOCPATH)
1479     {
1480         E(matchesSinglePath(S, c -> current(), args.number() - 1, result));
1481         return OK;
1482     }
1483     if (functor == EXFO_UNION)
1484     {
1485         int j, argsNumber = args.number();
1486         for (j = 0; j < argsNumber; j++)
1487         {
1488             E( args[j] -> matchesPattern(S, c, result) );
1489             if (result)
1490                 RetOK( result, TRUE );
1491         };
1492     }
1493     RetOK(result, FALSE);
1494 }
1495
1496 eFlag Expression::trueFor(Sit S, Context *c, Bool& result)
1497 {
1498     Expression ex(getOwnerElement());
1499     E( eval(S, ex, c) );
1500     switch(ex.type)
1501     {
1502     case EX_NUMBER:
1503         result = (Bool) (ex.tonumber(S) == (double) (c -> getPosition() + 1));
1504         break;
1505     default:
1506         result = ex.tobool();
1507     }
1508     return OK;
1509 }
1510
1511
1512 Bool Expression::tobool()
1513 {
1514     assert(functor == EXF_ATOM);
1515     switch(type)
1516     {
1517     case EX_NUMBER:
1518         return (Bool) !(*patomnumber == 0.0 || patomnumber -> isNaN());
1519         break;
1520     case EX_STRING:
1521         return (Bool) !(patomstring -> isEmpty());
1522         break;
1523     case EX_BOOLEAN:
1524         return atombool;
1525         break;
1526     case EX_NODESET:
1527         return (Bool) !!(patomnodeset -> getSize());
1528         break;
1529     default: assert(0);
1530     };
1531     return FALSE;   //just to return something
1532 }
1533
1534 eFlag Expression::tostring(Sit S, Str& strg)
1535 {
1536     assert(functor == EXF_ATOM);
1537     switch(type)
1538     {
1539     case EX_NUMBER:
1540         if (patomnumber -> isNaN())
1541             strg = (char*)"NaN";
1542         else
1543         {
1544             if (!patomnumber -> isInf())
1545                 strg = (double)(*patomnumber);
1546             else if (*patomnumber > 0.0)
1547                 strg = (char*)"+Infinity";
1548             else strg = (char*)"-Infinity";
1549         }
1550         break;
1551     case EX_STRING:
1552         strg = *patomstring;
1553         break;
1554     case EX_BOOLEAN:
1555         strg = (atombool ? (char *)"true" : (char *)"false");
1556         break;
1557     case EX_NODESET:
1558         if (!patomnodeset -> getSize())
1559             strg = (char*)"";
1560         else 
1561         {
1562             DStr temp;
1563             S.dom().constructStringValue(patomnodeset -> current(), temp);
1564             strg = temp;
1565         }
1566         break;
1567     case EX_EXTERNAL:
1568       {
1569         strg = (char*)"[External Object]";
1570       }; break;
1571     default: assert(0);
1572     };
1573     return OK;
1574 }
1575
1576 const Str& Expression::tostringRef() const
1577 {
1578     assert((functor == EXF_ATOM) && (type == EX_STRING));
1579     return (*NZ(patomstring));
1580 }
1581
1582 Number Expression::tonumber(Sit S)
1583 {
1584     assert(functor == EXF_ATOM);
1585     Number n;
1586     switch(type)
1587     {
1588     case EX_NUMBER:
1589         n = *patomnumber;
1590         break;
1591     case EX_STRING:
1592         n = *patomstring;
1593         break; 
1594     case EX_BOOLEAN:
1595         n = (atombool ? 1.0 : 0.0);
1596         break;
1597     case EX_NODESET:
1598       {
1599             // to avoid the following, tostring() must return const Str&:
1600             Str strg;
1601                 tostring(S, strg);
1602             n = strg;
1603             // but note that changing it to n = tostringRef() failed
1604       };
1605         break;
1606     default: assert(0);
1607     };
1608     return n;
1609 }
1610
1611 #ifdef ENABLE_JS
1612 External Expression::toexternal(Sit S)
1613 {
1614     assert(functor == EXF_ATOM);
1615     External e;
1616     switch(type)
1617     {
1618     case EX_EXTERNAL:
1619       {
1620         e.assign(*patomexternal);
1621         //e.setValue(patomexternal -> getValue());
1622       }; break;
1623     default: assert(0);
1624     };
1625     return e;
1626 }
1627 #endif
1628
1629 Context& Expression::tonodeset()
1630 {
1631     assert((functor == EXF_ATOM) && (type == EX_NODESET));
1632     return *(patomnodeset -> copy());
1633 }
1634
1635 const Context& Expression::tonodesetRef()
1636 {
1637     assert((functor == EXF_ATOM) && (type == EX_NODESET));
1638     return *patomnodeset;
1639 }
1640
1641 eFlag Expression::patternOK(Sit S)
1642 {
1643     int i,
1644         argsNumber = args.number();
1645     // assert(functor == EXFO_UNION || functor == EXF_LOCPATH);
1646
1647     if ( containsFunctor(EXFF_CURRENT) )
1648       Err(S, E_BAD_PATTERN);
1649
1650     switch(functor)
1651     {
1652     case EXF_LOCPATH:
1653         {
1654             for (i = 0; i < argsNumber; i++)
1655             {
1656                 LocStep *ls = args[i] -> step;
1657                 switch (ls -> ax)
1658                 {
1659                 case AXIS_CHILD:
1660                 case AXIS_ATTRIBUTE:
1661                 case AXIS_ROOT:
1662                     break;
1663                 case AXIS_DESC_OR_SELF:
1664                     if (ls -> ntype != EXNODE_NODE)
1665                         Err(S, E_BAD_PATTERN);
1666                     break;
1667                 default:
1668                     Err(S, E_BAD_AXIS_IN_PATTERN);
1669                 }
1670             }
1671         }; break;
1672     case EXFO_UNION:
1673         {
1674             for (i=0; i < argsNumber; i++)
1675                 E(args[i] -> patternOK(S));
1676         }; break;
1677     default:
1678         Err(S, E_BAD_PATTERN);
1679         // assert(!"patternOK()");
1680     };
1681     return OK;
1682 }
1683
1684 eFlag Expression::parse(Sit S, const DStr &string,
1685                         Bool _isPattern /* = FALSE  */,
1686                         Bool defaultToo /* = FALSE  */)
1687 {
1688     isPattern = _isPattern;
1689     Tokenizer t(*this);
1690     E( t.tokenize(S, string) );
1691     E( parse(S, t, 0, t.items.number() - 1, defaultToo) );
1692     if (_isPattern)
1693         E( patternOK(S) );
1694     return OK;
1695 }
1696
1697 /*================================================================
1698 Bool isOp()
1699 returns True if the given token is an operator, in which case
1700 'precedence' is set to its precedence
1701 ================================================================*/
1702
1703 Bool Expression::isOp(ExToken token, int &precedence)
1704 {
1705     Bool is = TRUE;
1706     switch(token)
1707     {
1708     case TOK_OR:
1709         precedence = 0;
1710         break;
1711     case TOK_AND:
1712         precedence = 1;
1713         break;
1714     case TOK_EQ:
1715     case TOK_NEQ:
1716         precedence = 2;
1717         break;
1718     case TOK_LT:
1719     case TOK_GT:
1720     case TOK_LE:
1721     case TOK_GE:
1722         precedence = 3;
1723         break;
1724     case TOK_PLUS:
1725     case TOK_MINUS:
1726         precedence = 4;
1727         break;
1728     case TOK_MULT:
1729     case TOK_DIV:
1730     case TOK_MOD:
1731         precedence = 5;
1732         break;
1733     case TOK_MINUS1:
1734         precedence = 6;
1735         break;
1736     case TOK_VERT:
1737         precedence = 7;
1738         break;
1739     default:
1740         {
1741             is = FALSE;
1742             precedence = -1;
1743         };
1744     };
1745     return is;
1746 }
1747
1748 /*================================================================
1749 void getFunctionInfo()
1750 returns function code and type for the function with given name
1751     if no such builtin function, returns EXFF_NONE
1752 ================================================================*/
1753
1754 void getFunctionInfo(const Str &s, ExFunctor &code, ExType &type)
1755 {
1756     char *p = (char *) s;
1757     int i;
1758
1759     for (i = 0; funcInfoTable[i].name; i++)
1760     {
1761         if (!strcmp(funcInfoTable[i].name,p))
1762             break;
1763     };
1764     code = funcInfoTable[i].func;
1765     type = funcInfoTable[i].type;
1766 }
1767
1768 struct OpItem
1769 {
1770     ExFunctor fu;
1771     ExType ty;
1772     int arity;
1773
1774 opTable[] =
1775 {
1776     {EXFO_OR, EX_BOOLEAN, 3},
1777     {EXFO_AND, EX_BOOLEAN, 3},
1778     {EXFO_EQ, EX_BOOLEAN, 2},
1779     {EXFO_NEQ, EX_BOOLEAN, 2},
1780     {EXFO_LT, EX_BOOLEAN, 2},
1781     {EXFO_GT, EX_BOOLEAN, 2},
1782     {EXFO_LE, EX_BOOLEAN, 2},
1783     {EXFO_GE, EX_BOOLEAN, 2},
1784     {EXFO_PLUS, EX_NUMBER, 2},
1785     {EXFO_MINUS2, EX_NUMBER, 2},
1786     {EXFO_MULT, EX_NUMBER, 2},
1787     {EXFO_MOD, EX_NUMBER, 2},
1788     {EXFO_DIV, EX_NUMBER, 2},
1789     {EXFO_MINUS1, EX_NUMBER, 1},
1790     {EXFO_UNION, EX_NODESET, 3}
1791 };
1792
1793 /*================================================================
1794 eFlag parseLP()
1795 ================================================================*/
1796
1797 eFlag Expression::parseLP(Sit S, Tokenizer& tokens, int &pos, 
1798                      Bool dropRoot, Bool defaultToo /*=FALSE*/)
1799 {
1800     assert(functor == EXF_LOCPATH);
1801     ExToken tok;
1802     BOOL getaway = FALSE;
1803     Expression *ls; // GP: OK (immediately appended)
1804     int& i = pos;
1805     Bool 
1806       slashPending = FALSE,
1807       nameWas = FALSE,
1808       nameRecent = FALSE;
1809     
1810     tok = tokens.items[i] -> tok;
1811     if (tok == TOK_END)
1812         Err(S, ET_EMPTY_PATT);
1813     if ((tok == TOK_SLASH) || (tok== TOK_DSLASH))
1814     {
1815         if (!dropRoot)
1816         {
1817             args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1818             ls -> step -> set(AXIS_ROOT,EXNODE_NODE);
1819         }
1820         if (tok == TOK_SLASH)
1821             i++;
1822     }
1823     
1824     while (!getaway)
1825     {
1826         tok = tokens.items[i] -> tok;
1827         switch(tok)
1828         {
1829         case TOK_NAME:
1830         case TOK_NTNAME:
1831         case TOK_AXISNAME:
1832         case TOK_ATSIGN:
1833         case TOK_PERIOD:
1834         case TOK_DPERIOD:
1835             {
1836               if (nameRecent)
1837                 Err(S, ET_EXPR_SYNTAX);
1838               args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1839               E( ls -> step -> parse(S, tokens, i, defaultToo) );
1840               slashPending = FALSE;
1841               nameWas = TRUE;
1842               //nameRecent = (tok == TOK_NAME || tok == TOK_NTNAME)
1843               nameRecent = TRUE;
1844             };
1845             break;
1846         case TOK_DSLASH:
1847             {
1848                 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1849                 ls -> step -> set(AXIS_DESC_OR_SELF, EXNODE_NODE);
1850             };
1851             // no break here?
1852         case TOK_SLASH:
1853             {
1854                 if (slashPending)
1855                     Err(S, ET_EXPR_SYNTAX);
1856                 slashPending = TRUE;
1857                 i++;
1858                 if (tokens.items[i] -> tok == TOK_END) 
1859                     Err(S, ET_EMPTY_PATT);
1860                 nameRecent = FALSE;
1861             };
1862             break;
1863         case TOK_VERT:
1864         case TOK_END:
1865         default: getaway = TRUE;
1866         };
1867     };
1868     if ((slashPending && nameWas) || !args.number())
1869         Err(S, ET_EMPTY_PATT);
1870
1871     return OK;
1872 }
1873
1874
1875 /*================================================================
1876 eFlag parseBasic()
1877     parses the basic expression in tokenizer 't' between positions
1878     'from' and 'to' inclusive. The basic expression is guaranteed
1879     to contain no operators (except for / and //) nor outer 
1880     parentheses.
1881 ================================================================*/
1882
1883 eFlag Expression::parseBasic(Sit S, Tokenizer &t, int from, int to,
1884     Bool defaultToo /* = FALSE */)
1885 {
1886     GP( Expression ) e, lp;
1887     // find the start of the filtering predicates
1888     int fstart, fright, fleft;
1889     ExToken tok;
1890
1891     switch(t.items[from] -> tok)
1892     {
1893     case TOK_VAR:
1894     case TOK_LITERAL:
1895     case TOK_NUMBER:
1896         fstart = from + 1;
1897         break;
1898     case TOK_FNAME:
1899         {
1900             t.getDelim(S, fstart = from + 1);
1901             fstart++;
1902         };
1903         break;
1904     case TOK_LPAREN:
1905         {
1906             t.getDelim(S, fstart = from);
1907             fstart++;
1908         };
1909         break;
1910     default:
1911         fstart = -1;
1912     };
1913
1914 //#pragma Msg("adding '+1':")
1915     if ((fstart != -1) && (fstart <= to))
1916     {
1917         switch(t.items[fstart] -> tok)
1918         {
1919         case TOK_LBRACKET:
1920         case TOK_SLASH:
1921         case TOK_DSLASH:
1922             {
1923                 // parse the filtered expression into args[0]
1924                 e = new Expression(getOwnerElement()); // is a GP
1925                 E( (*e).parse(S, t, from, fstart - 1));
1926                 args.append(e.keep());
1927                 //
1928                 functor = EXF_FILTER;
1929                 type = EX_NODESET;
1930                 fleft = fstart;
1931                 while (t.items[fleft] -> tok == TOK_LBRACKET)
1932                 {
1933                     t.getDelim(S, fright = fleft);
1934                     if ((t.items[fright] -> tok == TOK_END) || (fright > to))
1935                         Err(S, ET_RBRACKET_EXP);
1936                     if (fleft + 1 == fright)
1937                         Err(S, ET_EXPR_SYNTAX);
1938                     E( (e = new Expression(getOwnerElement())) -> parse(S, t,
1939                         fleft + 1, fright - 1, defaultToo) );
1940                     args.append(e.keep());
1941                     fleft = fright + 1;
1942                 };
1943                 if (((tok = t.items[fleft] -> tok) == TOK_SLASH)
1944                     || (tok == TOK_DSLASH))
1945                 {
1946                     E( (lp = new Expression(getOwnerElement(), EXF_LOCPATH)) -> parseLP(
1947                         S, t, fleft, TRUE, defaultToo) );
1948                     hasPath = TRUE;
1949                     args.append(lp.keep());
1950                 };
1951                 if (fleft != to + 1)
1952                     Err(S, ET_EXPR_SYNTAX);
1953                 return OK;
1954             };
1955             break;
1956         }
1957     };
1958
1959     DStr temp;
1960     tok = t.items[from] -> tok;
1961     t.items[from] -> speak(temp,SM_OFFICIAL);
1962     if ((tok == TOK_VAR) || (tok == TOK_LITERAL)
1963         || (tok == TOK_NUMBER))
1964     {
1965         switch(t.items[from] -> tok)
1966         {
1967         case TOK_VAR:
1968             {
1969                 functor = EXF_VAR;
1970                 type = EX_UNKNOWN;
1971                 // GP: OK (member)
1972                 E( getOwnerElement().setLogical(S, 
1973                             *(pName = new QName), temp, FALSE) );
1974             }; break;
1975         case TOK_LITERAL:
1976             {
1977                 functor = EXF_ATOM;
1978                 type = EX_STRING;
1979                 patomstring = new Str(temp);
1980             }; break;
1981         case TOK_NUMBER:
1982             {
1983                 functor = EXF_ATOM;
1984                 type = EX_NUMBER;
1985                 *(patomnumber = new Number) = temp;
1986             }; break;
1987         };
1988         if (to != from)
1989             Err(S, ET_EXPR_SYNTAX);
1990     }
1991     else
1992     {
1993         if (tok == TOK_FNAME)
1994         {
1995             ExFunctor funcNo;
1996             ExType funcType;
1997             getFunctionInfo(temp,funcNo,funcType);
1998             if (funcNo != EXFF_NONE)
1999             {
2000                 functor = funcNo;
2001                 type = funcType;
2002             }
2003             else
2004             {
2005                 functor = EXF_OTHER_FUNC;
2006                 E( getOwnerElement().setLogical(S, 
2007                             *(pName = new QName), temp, FALSE) );
2008                 type = EX_UNKNOWN;
2009             };
2010             int i = from+1,
2011                 j;
2012             assert(t.items[i] -> tok == TOK_LPAREN);
2013             i++;
2014             // original loop test:
2015             // while (t.items[j = t.findTop(TOK_COMMA,i)] -> tok != TOK_END)
2016             while (((j = t.findTop(TOK_COMMA,i)) <= to) && (t.items[j] -> tok != TOK_END))
2017             {
2018                 switch(t.items[j-1] -> tok)
2019                 {   
2020                 case TOK_COMMA:
2021                 case TOK_LPAREN:
2022                     Err(S, ET_EXPR_SYNTAX);
2023                 };
2024                 args.append(e = new Expression(getOwnerElement()));
2025                 e.keep();
2026                 E( (*e).parse(S, t,i,j-1, defaultToo) );
2027                 i = j+1;
2028             };
2029
2030             if ((t.items[j = t.findTop(TOK_RPAREN,i)]->tok == TOK_END) || 
2031                 (j > to))
2032               {
2033                 Err(S, ET_RPAREN_EXP);
2034               }
2035             if(t.items[j-1] -> tok == TOK_COMMA)
2036                 Err(S, ET_EXPR_SYNTAX);
2037             if (j > i)  // if any args
2038             {
2039                 args.append(e = new Expression(getOwnerElement()));
2040                 e.keep();
2041                 E( (*e).parse(S, t,i,j-1, defaultToo) );
2042             }
2043             if (to != j)
2044                 Err(S, ET_EXPR_SYNTAX);
2045         } // end "tok == TOK_FNAME"
2046         else
2047         {   // it must be a LocPath
2048             type = EX_NODESET;
2049             functor = EXF_LOCPATH;
2050             int howfar = from;
2051             E( parseLP(S, t, howfar, FALSE, defaultToo) );
2052             if (howfar != to + 1)
2053                 Err(S, ET_EXPR_SYNTAX);
2054         }
2055     }
2056     return OK;
2057 }
2058
2059 /*================================================================
2060 eFlag parse()
2061     translates the given token list into an expression (a tree of
2062     'Expression' objects plus some leaves).
2063 INPUT
2064     t           a tokenizer whose tokenize() method has been called
2065     from,to     first and last position in the token list the parsing
2066                 applies to (i.e. a complex expression will parse the
2067                 subexpressions with the same tokenizer but different
2068                 limits)
2069 ================================================================*/
2070
2071 eFlag Expression::parse(Sit S, Tokenizer& t, int from, int to,
2072     Bool defaultToo /* = FALSE */)
2073 // isOp, skipParens
2074 {
2075     int i;
2076     ExToken 
2077         token, 
2078         mintoken = TOK_NONE;
2079     int precedence,
2080         minprec = 999,
2081         minndx = -1,
2082         leftmost = 0,
2083         arity;
2084
2085     if (from > to)
2086         Err(S, ET_EXPR_SYNTAX);
2087
2088     t.stripParens(S, from,to);
2089     // search from right to left (left-associativity)
2090     for (i = to; i >= from; i--)
2091     {
2092         switch(token = t.items[i] -> tok)
2093         {
2094         case TOK_RPAREN:
2095         case TOK_RBRACKET:
2096             {
2097                 // reverse search:
2098                 E( t.getDelim(S, i,TRUE) ); // i is decremented at loop end
2099                 if (i == -1)
2100                     Err(S, ET_LPARCKET_EXP);
2101             };
2102             break;
2103         default: 
2104             {
2105                 if (isOp(token, precedence) && (precedence < minprec))
2106                 {
2107                     minprec = precedence;
2108                     minndx = leftmost = i;
2109                     mintoken = token;
2110 //                    if (token == TOK_OR) break;
2111                 }
2112                 else 
2113                     if (token == mintoken)
2114                         leftmost = i;
2115             };
2116         };
2117     };
2118
2119     //minndx now points to the rightmost lowest-precedence operator
2120     // leftmost points at its leftmost occurence
2121
2122     if (minndx == -1)
2123         E( parseBasic(S, t, from, to, defaultToo) )
2124     else 
2125     {
2126         int tablendx = t.items[minndx] -> tok - TOKGROUP_OPERATORS;
2127         functor = opTable[tablendx].fu;
2128         type = opTable[tablendx].ty;
2129         arity = opTable[tablendx].arity;
2130         Expression *e = new Expression(getOwnerElement()); // GP: OK
2131
2132         args.append(e);
2133         switch(arity)
2134         {
2135         case 1: 
2136             {
2137             if (minndx != from)
2138                 Err(S, ET_EXPR_SYNTAX)
2139             else
2140                 E( e -> parse(S, t,from+1,to, defaultToo) );
2141             };
2142             break;
2143         case 2:
2144             {
2145                 E( e -> parse(S, t,from,minndx - 1, defaultToo) );
2146                 args.append(e = new Expression(getOwnerElement())); // GP: OK
2147                 E( e -> parse(S, t,minndx + 1, to, defaultToo) );
2148             };
2149             break;
2150         default:
2151             {
2152                 E( e -> parse(S, t,from,leftmost - 1, defaultToo) );
2153                 int another = leftmost, 
2154                     lastone = leftmost;
2155                 // tom 24-10-00
2156                 // the following fails for "x and not(x and x)"
2157                 // t.getDelim(another);
2158                 // changing it to:
2159                 another = t.findTop(t.items[another]->tok, another+1);
2160                 while((another <= to) && (t.items[another]->tok != TOK_END))
2161                 {
2162                     args.append(e = new Expression(getOwnerElement())); // GP: OK
2163                     E( e -> parse(S, t, lastone + 1, another - 1, defaultToo));
2164                     lastone = another;
2165
2166                     // tom 14-11-00
2167                     // t.getDelim(another);     failed too, for "x and x and (x and x)"
2168                     another = t.findTop(t.items[another]->tok, another+1);
2169                 };
2170                 args.append(e = new Expression(getOwnerElement())); // GP: OK
2171                 E( e -> parse(S, t,lastone + 1, to, defaultToo) );
2172             };
2173         };
2174     }
2175     return OK;
2176 }
2177
2178
2179 void Expression::setAtom(Context *c)
2180 {
2181     clearContent();
2182     functor = EXF_ATOM;
2183     type = EX_NODESET;
2184     patomnodeset = c;
2185 }
2186
2187 void Expression::setAtom(const Number& n)
2188 {
2189     clearContent();
2190     functor = EXF_ATOM;
2191     type = EX_NUMBER;
2192     *(patomnumber = new Number) = (Number&) n;
2193 }
2194
2195 void Expression::setAtom(Bool b)
2196 {
2197     clearContent();
2198     functor = EXF_ATOM;
2199     type = EX_BOOLEAN;
2200     atombool = b;
2201 }
2202
2203 void Expression::setAtom(const DStr &s)
2204 {
2205     clearContent();
2206     functor = EXF_ATOM;
2207     type = EX_STRING;
2208     patomstring = new Str(s);
2209 }
2210
2211 #ifdef ENABLE_JS
2212 void Expression::setAtom(const External &ext)
2213 {
2214     clearContent();
2215     functor = EXF_ATOM;
2216     type = EX_EXTERNAL;
2217     patomexternal = new External();
2218     patomexternal -> assign(ext);
2219 }
2220 #endif
2221
2222 /*================================================================
2223 setFragment
2224     sets the expression to point to a 'result tree fragment' - a newly
2225     constructed tree - whose address it returns
2226 ================================================================*/
2227
2228 Tree* Expression::setFragment()
2229 {
2230     functor = EXF_FRAGMENT;
2231     type = EX_NODESET;
2232     return pTree = new Tree("RTF", FALSE); // not an XSL tree
2233 }
2234
2235 #define funcIsOperator(f) ((EXFO_OR <= f) && (f <= EXFO_Z))
2236 #define funcIsBuiltin(f) ((EXF_FUNCTION <= f) && (f <= EXFF_NONE))
2237
2238 eFlag Expression::eval(Sit S, Expression &retxpr, Context *c, Bool resolvingGlobals /* = FALSE */)
2239 {
2240     assert(!isPattern && "evaluating pattern!");
2241     GP( Context ) newc;
2242
2243     switch(functor)
2244     {
2245     case EXF_ATOM:
2246     {
2247         //cannot use retxpr = *this !!!
2248         switch(type)
2249         {
2250         case EX_STRING:
2251             retxpr.setAtom(*patomstring);
2252             break;
2253         case EX_NUMBER:
2254             retxpr.setAtom(*patomnumber);
2255             break;
2256         case EX_NODESET:
2257             retxpr.setAtom(patomnodeset -> copy());
2258             break;
2259         case EX_BOOLEAN:
2260             retxpr.setAtom(atombool);
2261             break;
2262 #ifdef ENABLE_JS
2263         case EX_EXTERNAL:
2264             retxpr.setAtom(*patomexternal);
2265             break;
2266 #endif
2267         default: assert(0);
2268         }
2269     }; break;
2270     case EXF_VAR:
2271     {
2272         Expression *ex = NZ(S.getProcessor()) -> getVarBinding(*pName);
2273         if (!ex)
2274         {
2275             // if we're resolving globals, this may mean a forward reference
2276             if (resolvingGlobals)
2277               {
2278                 E( S.getProcessor() -> resolveGlobal(S, c, *pName) );
2279                 ex = S.getProcessor() -> getVarBinding(*pName);
2280               }
2281             else
2282               {
2283                 Str fullName;
2284                 getOwnerTree().expandQStr(*pName, fullName);
2285                 Err1(S, E1_VAR_NOT_FOUND, fullName);
2286               }
2287         };
2288     E( ex -> eval(S, retxpr, c) );
2289     }; break;
2290     case EXF_LOCPATH:
2291     case EXFO_UNION:   
2292     {
2293         assert(c && "context is null!");
2294         newc.assign(c);
2295         E( createContext(S, newc) );
2296         newc.unkeep();
2297         // assign newc directly without copying
2298         retxpr.setAtom((*newc).copy()); 
2299         newc.del(); 
2300     }; break;
2301     case EXF_OTHER_FUNC: // other function
2302         {
2303           Str ret;
2304           Str uri = getOwnerTree().expand(pName -> getUri());
2305           Str name = getOwnerTree().expand(pName -> getLocal());
2306           
2307           if (! S.getProcessor() -> supportsFunction(uri, name) )
2308             {
2309               Str fullName;
2310               getOwnerTree().expandQStr(*pName, fullName);
2311               Err1(S, ET_FUNC_NOT_SUPPORTED,fullName);
2312             }
2313           int i, 
2314             argsNumber = args.number();
2315           // ExprList atoms(LIST_SIZE_1);
2316           GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2317           atoms.autodelete();
2318           GP( Expression ) ex;
2319           for (i = 0; i < argsNumber; i++)
2320             {
2321               ex = new Expression(getOwnerElement());
2322               E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2323               (*atoms).append(ex.keep());
2324             };
2325
2326 #ifdef ENABLE_JS          
2327           E( S.getProcessor()->callJSFunction(S, c, uri, name, *atoms, retxpr) );
2328 #endif
2329         }; break;
2330     case EXF_FILTER:
2331     {
2332         assert(c && "context is null!");
2333         newc.assign(c);
2334         E( createContext(S, newc, c -> getPosition()) );
2335         newc.unkeep();
2336         retxpr.setAtom((*newc).copy());
2337         newc.del();
2338     }; break;
2339     case EXF_STRINGSEQ:
2340     {
2341         DStr result;
2342         Expression temp(getOwnerElement());
2343         int i,
2344             argsNumber = args.number();
2345         for (i = 0; i < argsNumber; i++)
2346         {
2347             E( args[i] -> eval(S, temp, c, resolvingGlobals) );
2348             Str tempStr;
2349             E( temp.tostring(S, tempStr) );
2350             result += tempStr;
2351         };
2352         retxpr.setAtom(result);
2353     }; break;
2354     case EXF_FRAGMENT:
2355     {
2356         newc = new Context(NULL); //current nde not needed _cn_
2357         (*newc).set(&(pTree -> getRoot()));
2358         retxpr.setAtom((*newc).copy());
2359         newc.del();
2360     }; break;
2361     default: 
2362     {
2363         int i, 
2364             argsNumber = args.number();
2365         // ExprList atoms(LIST_SIZE_1);
2366         GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2367         atoms.autodelete();
2368         GP( Expression ) ex;
2369         for (i = 0; i < argsNumber; i++)
2370         {
2371             ex = new Expression(getOwnerElement());
2372             E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2373             (*atoms).append(ex.keep());
2374         };
2375         if (funcIsOperator(functor))
2376           E( callOp(S, retxpr, *atoms) )           //an operator
2377             else 
2378               if (funcIsBuiltin(functor))
2379                 E( callFunc(S, retxpr, *atoms, c) ) //a core XPath function
2380                   else
2381                     {
2382                       Str fullName;
2383                       getOwnerTree().expandQStr(*pName, fullName);
2384                       Err1( S, ET_FUNC_NOT_SUPPORTED, fullName );
2385                     }
2386         // atoms autodeleted
2387     };
2388     };
2389     return OK;
2390 }
2391
2392
2393
2394 template<class T>
2395 Bool hardCompare(ExFunctor op, T b1, T b2)
2396 {
2397         Str p,q;
2398     switch(op)
2399     {
2400     case EXFO_EQ: return (Bool) (b1 == b2); break;
2401     case EXFO_NEQ: return (Bool) !(b1 == b2); break;
2402     case EXFO_LT: return (Bool) (b1 < b2); break;
2403     case EXFO_GT: return (Bool) (b2 < b1); break;
2404     case EXFO_LE: return (Bool) ((b1 < b2) || (b1 == b2)); break;
2405     case EXFO_GE: return (Bool) ((b2 < b1) || (b1 == b2)); break;
2406     default: assert(0);
2407     }
2408     return FALSE; //just to return something
2409 }
2410
2411 ExFunctor _invertOp(ExFunctor op) 
2412 {
2413     switch(op)
2414     {
2415     case EXFO_EQ: return EXFO_EQ; break;
2416     case EXFO_NEQ: return EXFO_NEQ; break;
2417     case EXFO_LT: return EXFO_GT; break;
2418     case EXFO_GT: return EXFO_LT; break;
2419     case EXFO_LE: return EXFO_GE; break;
2420     case EXFO_GE: return EXFO_LE; break;
2421     default: assert(!"_invertOp"); return EXF_NONE; // to return something
2422     }
2423 }
2424
2425 Bool atomicCompare(ExFunctor op, const Str& str1, const Str& str2, 
2426                    Number* num2)
2427 {
2428   switch(op) {
2429   case EXFO_EQ:
2430   case EXFO_NEQ:
2431     {
2432       return hardCompare(op, str1, str2);
2433     }; break;
2434   case EXFO_LT:
2435   case EXFO_GT:
2436   case EXFO_LE:
2437   case EXFO_GE:
2438       {
2439         Number n1, n2;
2440         n1 = str1;
2441         if (num2) n2 = *num2;
2442         else n2 = str2;
2443         return hardCompare(op, n1, n2);
2444       }; break;
2445   default:
2446     {
2447       assert(!"atomicCompare");
2448     }
2449   }
2450   return FALSE; //just to return something
2451 }
2452
2453 Bool Expression::compareCC(Sit S, ExFunctor op, const Context &c1, const Context &c2)
2454 {
2455     DStr str1, str2;
2456     GP( Context )
2457         c1prime = ((Context&) c1).copy(),
2458         c2prime = ((Context&) c2).copy();
2459     Bool resulting = FALSE;
2460     (*c1prime).reset();
2461     while ((*c1prime).current())
2462     {
2463         str1.empty();
2464         S.dom().constructStringValue((*c1prime).current(), str1); 
2465         (*c2prime).reset();
2466         while((*c2prime).current())
2467         {
2468             str2.empty();
2469             S.dom().constructStringValue((*c2prime).current(), str2);
2470             if ( atomicCompare(op, str1, str2, NULL) )
2471             {
2472                 resulting = TRUE;
2473                 break;
2474             }
2475             (*c2prime).shift();
2476         };
2477         (*c1prime).shift();
2478     };
2479     // would be done automatically:
2480     c1prime.del();
2481     c2prime.del();
2482     return resulting;
2483 }
2484
2485 Bool Expression::compareCS(Sit S, ExFunctor op, const Context &c1, const Str &str2)
2486 {
2487     DStr str1;
2488     Bool resulting = FALSE;
2489     GP( Context ) c = ((Context&) c1).copy();
2490     //some optimization on str to num conversion
2491     Number* num2 = NULL;
2492     if (op != EXFO_EQ && op != EXFO_NEQ)
2493       {
2494         num2 = new Number();
2495         *num2 = str2;
2496       }
2497     
2498     (*c).reset();
2499     while((*c).current())
2500     {
2501         str1.empty();
2502         S.dom().constructStringValue((*c).current(), str1);
2503         if ( atomicCompare(op, str1, str2, num2) )
2504         {
2505             resulting = TRUE;
2506             break;
2507         }
2508         (*c).shift();
2509     };
2510     c.del();
2511     if (num2) delete num2;
2512     return resulting;
2513 }
2514
2515 Bool Expression::compareCN(Sit S, ExFunctor op, const Context &c1, const Number& n2)
2516 {
2517     Number n1;
2518     DStr s1;
2519     GP( Context ) c = ((Context&) c1).copy();
2520     Bool resulting = FALSE;
2521
2522     (*c).reset();
2523     while((*c).current())
2524     {
2525         s1.empty();
2526         S.dom().constructStringValue((*c).current(), s1);
2527         n1 = s1;
2528         if (hardCompare(op, n1, (Number)n2))
2529         {
2530             resulting = TRUE;
2531             break;
2532         }
2533         (*c).shift();
2534     };
2535     c.del();
2536     return resulting;
2537 }
2538
2539 eFlag Expression::compare(Sit S, Bool &result, Expression &other, ExFunctor op)
2540 // Both *this and other are assumed to be ATOMS.
2541 {
2542   assert(functor == EXF_ATOM);
2543   assert(other.functor == EXF_ATOM);
2544   
2545   ExType histype = other.type;
2546
2547   //check external object
2548   if (type == EX_EXTERNAL || histype == EX_EXTERNAL)
2549     {
2550       Err(S, E_INVALID_OPERAND_TYPE);
2551     }
2552
2553   //
2554   if (type == EX_NODESET)
2555     {
2556       if (other.type == EX_BOOLEAN)
2557         result = hardCompare(op, tobool(), other.tobool());
2558       else
2559         {
2560           Context& mynodeset = tonodeset(); // GP: OK
2561           switch(other.type)
2562             {
2563             case EX_NODESET:
2564               result = compareCC(S, op, mynodeset, other.tonodesetRef());
2565               break;
2566             case EX_STRING:
2567               {
2568                 Str temp;
2569                 E( other.tostring(S, temp) );
2570                 result = compareCS(S, op, mynodeset, temp);
2571               }; break;
2572             case EX_NUMBER:
2573               result = compareCN(S, op, mynodeset, other.tonumber(S));
2574               break;
2575             default: assert(0);
2576             };
2577           delete &mynodeset;
2578         }
2579     }
2580   else 
2581     {
2582       if (histype == EX_NODESET)
2583         {
2584           E( other.compare(S, result, *this, _invertOp(op)) );
2585         }
2586       else
2587         {   // none of the two are nodesets
2588           switch (op) {
2589           case EXFO_EQ:
2590           case EXFO_NEQ:
2591             {
2592               if (type == EX_BOOLEAN || histype == EX_BOOLEAN)
2593                 result = hardCompare(op, tobool(), other.tobool());
2594               else
2595                 {
2596                   if (type == EX_NUMBER || histype == EX_NUMBER)
2597                     result = hardCompare(op, tonumber(S), other.tonumber(S));
2598                   else
2599                     {
2600                       if (type == EX_STRING || histype == EX_STRING)
2601                         {
2602                           Str temp, otherTemp;
2603                           E( tostring(S, temp) );
2604                           E( other.tostring(S, otherTemp) );   
2605                           result = hardCompare(op, temp, otherTemp);
2606                         }
2607                       else
2608                         assert(0);
2609                     }
2610                 }
2611             }; break;
2612           case EXFO_LT:
2613           case EXFO_GT:
2614           case EXFO_LE:
2615           case EXFO_GE:
2616             {
2617               result = hardCompare(op, tonumber(S), other.tonumber(S));
2618             }; break;
2619           }
2620         }
2621     }
2622   return OK;
2623 }
2624
2625 eFlag Expression::callOp(Sit S, Expression& retxpr, ExprList& atoms)
2626 {
2627     int i,
2628         atomsNumber = atoms.number();
2629     switch(functor)
2630     {
2631     case EXFO_OR:
2632     case EXFO_AND:
2633         {
2634             assert(atomsNumber > 1);
2635             Bool result;
2636             result = atoms[0] -> tobool();
2637             for (i = 1; i < atomsNumber; i++)
2638             {
2639                 if (functor == EXFO_OR)
2640                 {
2641                     if (atoms[i] -> tobool())
2642                     {
2643                         result = TRUE;
2644                         break;
2645                     }
2646                 }
2647                 else    //EXFO_AND
2648                 {
2649                     if (!atoms[i] -> tobool())
2650                     {
2651                         result = FALSE;
2652                         break;
2653                     }
2654                 };
2655             };
2656             retxpr.setAtom(result);
2657         }; break;
2658     case EXFO_EQ:
2659     case EXFO_NEQ:
2660     case EXFO_LT:
2661     case EXFO_LE:
2662     case EXFO_GT:
2663     case EXFO_GE:
2664         {
2665             assert(atomsNumber == 2);
2666             Bool result;
2667             E( atoms[0]->compare(S, result,*(atoms[1]),functor) );
2668             retxpr.setAtom(result);
2669         }; break;
2670     case EXFO_PLUS:
2671     case EXFO_MINUS2:
2672     case EXFO_MULT:
2673     case EXFO_DIV:
2674     case EXFO_MOD:
2675         {
2676             assert(atomsNumber > 1);
2677             double result;
2678             result = (double) (atoms[0] -> tonumber(S));
2679             for (i = 1; i < atomsNumber; i++)
2680             {
2681                 switch(functor)
2682                 {
2683                 case EXFO_PLUS:
2684                     result += atoms[i] -> tonumber(S);
2685                     break;
2686                 case EXFO_MINUS2:
2687                     result -= atoms[i] -> tonumber(S);
2688                     break;
2689                 case EXFO_MULT:
2690                     result *= atoms[i] -> tonumber(S);
2691                     break;
2692                 case EXFO_DIV:
2693                     result /= atoms[i] -> tonumber(S);
2694                     break;
2695                 case EXFO_MOD:
2696                     {
2697                       double d = atoms[i] -> tonumber(S);
2698                       double aux = result/d;
2699                       aux = aux > 0 ? floor(aux) : ceil(aux);
2700                       result =  result - (aux * d);
2701                     };
2702                     break;
2703                 };
2704             };
2705             //eliminate the -0 effect
2706             if (!result) result = 0;
2707             retxpr.setAtom(Number(result));
2708         }; break;
2709     case EXFO_MINUS1:
2710         {
2711             assert(atomsNumber == 1);
2712             retxpr.setAtom(Number(-(double)(atoms[0] -> tonumber(S))));
2713         }; break;
2714     };
2715     return OK;
2716 }
2717
2718 // this appends the list of nodes with given ID, possibly with multiplicities
2719 void appendNodesWithID(Sit S, Str& ids, Context *c, Context& result)
2720 {
2721     char *p = (char*) ids;
2722     Str singleID;
2723     NodeHandle singleNode;
2724     int tokenLen;
2725     for (skipWhite(p); *p; skipWhite(p))
2726     {
2727         singleID.nset(p, tokenLen = strcspn(p,theWhitespace));
2728         SXP_Document doc = S.dom().getOwnerDocument(c -> current());
2729         singleNode = S.dom().getNodeWithID(doc, singleID);
2730         if (singleNode)
2731             result.append(singleNode);
2732         p += tokenLen;
2733     };
2734 }
2735
2736 /*================================================================
2737 callFunc
2738     calls the built-in XPath or XSLT function as determined by 
2739     'this -> functor'. Returns an expression in 'retxpr'.
2740     'atoms' is a list of atomized arguments, 'c' is the current context
2741     necessary for certain functions.
2742 ================================================================*/
2743
2744 #define checkArgsCount(x) if (atomsNumber != x)\
2745     Err(S, ET_BAD_ARGS_N);
2746 #define checkArgsCountMax(x) if (atomsNumber > x)\
2747     Err(S, ET_BAD_ARGS_N);
2748 #define checkArgsCountMin(x) if (atomsNumber < x)\
2749     Err(S, ET_BAD_ARGS_N);
2750 #define checkArgsCountBetween(x,y) if ((atomsNumber < x) || \
2751     (atomsNumber > y)) Err(S, ET_BAD_ARGS_N);
2752
2753 // only check for being a nodeset
2754 #define checkIsNodeset(x) if (atoms[x] -> type != EX_NODESET)\
2755     Err(S, ET_BAD_ARG_TYPE);
2756
2757 // Everything is a string, in a cosmic sense
2758 #define checkIsString(x)
2759 #define checkIsString2(x,y) 
2760 #define checkIsNumber(x)
2761
2762 /*................
2763 firstOccurence
2764 Finds the first complete occurence of q in p; returns the 0-based starting
2765 position, or -1 if not found
2766 Now works for UTF-8 strings.
2767 ................*/
2768
2769 int firstOccurence(char *p, char *q)
2770 {
2771     int i = 0,
2772         iCurr = 0,
2773         j = 0,
2774             pos = 0,
2775             charlen;
2776     while(p[i] && q[j])
2777     {
2778         charlen = utf8SingleCharLength(p + i);
2779         if (!strncmp(p + i, q + j, charlen))
2780         {
2781             i += charlen;
2782             j += charlen;
2783         }
2784         else
2785         {
2786             i = (iCurr += utf8SingleCharLength(p + iCurr));
2787             pos++;
2788             j = 0;
2789         }
2790     };
2791     if (q[j]) 
2792         return -1;
2793     else
2794         return pos;
2795 }
2796
2797 /*................
2798 getBetween
2799 Returns in s the portion of the source string between from and to inclusive.
2800 If to == -1 then copies the whole rest of the string.
2801 Now works for multibyte UTF-8 characters.
2802 ................*/
2803
2804 void getBetween(Str& s, char *source, int from, int to)
2805 {
2806     assert(source);
2807     char *start = NULL;
2808     int i;
2809     if (from < 0) from = 0;
2810     for (i = 0; *source && (i <= to || to == -1); i++)
2811     {
2812             if (i == from)
2813             {
2814                 start = source;
2815                     if (to == -1)
2816                         // no need to go through the rest of the string
2817                         break;
2818         }
2819         if (!(*source & 0x80))
2820                 // optimize for ASCII chars
2821             source++;
2822             else
2823             source += utf8SingleCharLength(source);
2824         }
2825         if (!start)
2826             s.empty();
2827         else
2828         {
2829             if (to == -1)
2830                 s = start;
2831                 else
2832                 s.nset(start,(int)(source - start));
2833         }
2834 }
2835
2836 /*................
2837 getCurrValue
2838 Returns the string value of the current node
2839 ................*/
2840
2841 eFlag getCurrValue(Sit S, Str &s, Context *c)
2842 {
2843     NodeHandle v;
2844     DStr temp;
2845     if (!!(v = c -> current()))
2846         S.dom().constructStringValue(v, temp);
2847     else
2848         s.empty();
2849     s = temp;
2850     return OK;
2851 }
2852
2853 // An auxiliary function which retrieves the document from a given URI,
2854 // regardless of whether DOM is external or not. Should be moved into S.dom().
2855
2856 // new model for processing on external documents: first if have the
2857 // processor and we're running on external, the DOMProvider is asked
2858
2859 eFlag Expression::getDocument_(Sit S, NodeHandle& newroot, 
2860                                const Str& location, const Str& baseUri,
2861                                Processor* proc)
2862 {
2863   newroot = NULL;
2864   if ( proc && proc -> processingExternal() )
2865     {
2866       /* FIXME: this is incomplete */
2867       newroot = S.dom().retrieveDocument(location, baseUri);
2868     }
2869   //external not active or failed
2870   if ( nhNull(newroot) )
2871     {
2872       if ( proc ) 
2873         {
2874           Tree *newtree;
2875           Str uri, locBase;
2876           if (baseUri == (const char*)"")
2877               locBase = proc -> baseForVertex(S, &getOwnerElement());
2878           else
2879               locBase = baseUri;
2880           makeAbsoluteURI(S, location, locBase, uri);
2881
2882           //deny document fragments for the 'file' scheme
2883           const char* aux = (char*)uri;
2884           const char *colon = strchr(aux, ':');
2885           if ( colon
2886                && ( ((colon - aux == 4) && !strncmp(aux, "file", 4)) ||
2887                     ((colon - aux == 3) && !strncmp(aux, "arg", 3))))
2888             {
2889               if ( strchr((const char*)uri, '#') )
2890                 Err1(S, E_DOC_FRAGMENT, (char*)uri);
2891             }
2892           //if we found no colon, it's bad, but we do not care right here
2893
2894           Bool iserr = 
2895             proc -> readTreeFromURI(S, newtree, uri, 
2896                                     proc ->
2897                                     baseForVertex(S, &getOwnerElement()), 
2898                                     FALSE, 
2899                                     S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND));
2900           if (!iserr) {
2901             newroot = (NodeHandle) &(newtree -> getRoot());
2902             //strip spaces
2903             proc -> stripTree(S, *(newtree));
2904           } else if (! S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND))
2905             return NOT_OK; //propagate an error
2906         }
2907       else
2908         {
2909           Err1(S, E1_URI_OPEN, location);
2910         }
2911     }
2912   return OK;
2913 }                   
2914
2915
2916
2917 eFlag Expression::callFunc(Sit S, Expression &retxpr, ExprList &atoms, Context *c)
2918 {
2919     NodeHandle v;
2920     int atomsNumber = atoms.number();
2921     switch(functor)
2922     {
2923     case EXFF_LAST:
2924     {
2925         checkArgsCount(0);
2926         retxpr.setAtom( Number(c -> getSize()) );
2927     }; break;
2928     case EXFF_POSITION:
2929     {
2930         checkArgsCount(0);
2931         retxpr.setAtom( Number(c -> getPosition() + 1) );
2932     }; break;
2933     case EXFF_COUNT:
2934     {
2935         checkArgsCount(1);
2936         retxpr.setAtom( 
2937             Number(atoms[0] -> tonodesetRef().getSize()) );
2938     }; break;
2939     case EXFF_LOCAL_NAME:
2940     case EXFF_NAMESPACE_URI:
2941     case EXFF_NAME:
2942     {
2943         checkArgsCountMax(1);
2944         Str strg;
2945         if (!atomsNumber)
2946             v = (c -> isFinished()) ? NULL : c -> current();
2947         else
2948         {
2949             checkIsNodeset(0);
2950             const Context& newc = atoms[0] -> tonodesetRef();
2951             v = (newc.isVoid()? NULL : newc.current());
2952         };
2953         if (v)
2954         {
2955             if (!S.domExternal(v))
2956               {
2957                 const QName& q = toV(v) -> getName();
2958                 switch(functor)
2959                 {
2960                 case EXFF_NAME:
2961                     toV(v) -> getOwner().expandQStr(q, strg); break;
2962                 case EXFF_LOCAL_NAME:
2963                     strg = toV(v) -> getOwner().expand(q.getLocal()); break;
2964                 case EXFF_NAMESPACE_URI:
2965                     strg = toV(v) -> getOwner().expand(q.getUri()); break;
2966                 }
2967               }
2968             else
2969               {
2970                 const char *aux;
2971                 switch(functor) {
2972                 case EXFF_NAME:
2973                   {
2974                     strg = (aux = S.dom().getNodeName(v));
2975                     S.dom().freeName(v, (char*)aux);
2976                   } break;
2977                 case EXFF_LOCAL_NAME:
2978                   {
2979                     strg = (aux = S.dom().getNodeNameLocal(v));
2980                     S.dom().freeName(v, (char*)aux);
2981                   } break;
2982                 case EXFF_NAMESPACE_URI:
2983                   {
2984                     strg = (aux = S.dom().getNodeNameURI(v));
2985                     S.dom().freeName(v, (char*)aux);
2986                   } break;
2987                 }
2988               }
2989         };
2990         retxpr.setAtom(strg);
2991     };
2992     break;
2993
2994     case EXFF_STRING:
2995     {
2996         Str string;
2997         checkArgsCountMax(1);
2998         if (!atomsNumber)
2999             E( getCurrValue(S, string, c) )
3000                 else
3001                     E( atoms[0] -> tostring(S, string) );
3002         retxpr.setAtom(string);
3003     }; break;
3004
3005     case EXFF_CONCAT:
3006     {
3007         checkArgsCountMin(2);
3008         DStr strg;
3009         for (int k = 0; k < atomsNumber; k++)
3010         {
3011             checkIsString(k);
3012             Str atomStr;
3013             E( atoms[k] -> tostring(S, atomStr) );
3014             strg += atomStr;
3015         };
3016         retxpr.setAtom(strg);
3017     }; break;
3018
3019     case EXFF_STARTS_WITH:
3020     {
3021         checkArgsCount(2);
3022         checkIsString2(0,1);
3023         Str a0Str, a1Str;
3024         E( atoms[0] -> tostring(S, a0Str) );
3025         E( atoms[1] -> tostring(S, a1Str) );
3026         retxpr.setAtom((Bool) !firstOccurence(
3027             a0Str, a1Str));
3028     }; break;
3029
3030     case EXFF_CONTAINS:
3031     {
3032         checkArgsCount(2);
3033         checkIsString2(0,1);
3034         Str a0Str, a1Str;
3035         E( atoms[0] -> tostring(S, a0Str) );
3036         E( atoms[1] -> tostring(S, a1Str) );
3037         retxpr.setAtom((Bool) (firstOccurence(
3038             a0Str, a1Str) != -1));
3039     }; break;
3040
3041     case EXFF_SUBSTRING_BEFORE:
3042     case EXFF_SUBSTRING_AFTER:
3043     {
3044         Str strg;
3045         Str theBigger, theSmaller;
3046         E( atoms[0] -> tostring(S, theBigger) );
3047         E( atoms[1] -> tostring(S, theSmaller) );
3048         checkArgsCount(2);
3049         checkIsString2(0,1);
3050         int where = firstOccurence(theBigger,theSmaller);
3051         if (where == -1)
3052             strg.empty();
3053         else
3054         {
3055             if (functor == EXFF_SUBSTRING_BEFORE)
3056             {
3057                 if (where == 0)
3058                     strg.empty();
3059                 else
3060                     getBetween(strg, theBigger, 0, where-1);
3061             }
3062             else
3063                 getBetween(strg, theBigger, 
3064                            where + utf8StrLength(theSmaller), -1);
3065         };
3066         retxpr.setAtom(strg);
3067     }; break;
3068
3069     case EXFF_SUBSTRING:
3070     {
3071         checkArgsCountBetween(2,3); 
3072         checkIsString(0); checkIsNumber(1);
3073         /* useless test causing a warning in MSVC:
3074
3075            if (atomsNumber == 3)
3076            checkIsNumber(2);
3077         */
3078         Str strg;
3079         Number from_ = atoms[1] -> tonumber(S) - 1;
3080         if (!from_.isNaN() && !from_.isInf())
3081         {
3082             int from = from_.round(), 
3083                 to = -1;
3084             if (atomsNumber > 2)
3085             {
3086                 // use length in 3rd argument
3087                 Number len = atoms[2] -> tonumber(S);
3088                 if (len <= 0 || len.isNaN())
3089                     to = -2;
3090                 else if (!len.isInf())
3091                     to = from + len.round() - 1;  // otherwise it remains -1
3092             }
3093             Str a0Str;
3094             E( atoms[0] -> tostring(S, a0Str) );
3095             getBetween(strg, a0Str, from, to);
3096         }
3097         retxpr.setAtom(strg);
3098     }; break;
3099
3100     case EXFF_STRING_LENGTH:
3101     {
3102         checkArgsCountBetween(0,1);
3103         if (atomsNumber)
3104         {
3105             checkIsString(0);
3106             Str a0Str;
3107             E( atoms[0] -> tostring(S, a0Str) );
3108             retxpr.setAtom(Number(utf8StrLength(a0Str)));
3109         }
3110         else
3111         {
3112             Str string;
3113             E( getCurrValue(S, string, c) );
3114             retxpr.setAtom(Number(utf8StrLength(string)));
3115         }
3116     }; break;
3117
3118     case EXFF_NORMALIZE_SPACE:
3119     {
3120         checkArgsCountBetween(0,1);
3121         Str string;
3122         if (atomsNumber)
3123         {
3124             checkIsString(0);
3125             E( atoms[0] -> tostring(S, string) );
3126         }
3127         else
3128             E( getCurrValue(S, string, c) );
3129         char *p = (char*) string;
3130         DStr stripped;
3131         skipWhite(p);
3132         while(*p)
3133         {
3134             if (isWhite(*p))
3135             {
3136                 skipWhite(p);
3137                 if (*p)
3138                     stripped += ' ';
3139                 p--;
3140             }
3141             else
3142                 stripped += *p;
3143             p++;
3144         }
3145         retxpr.setAtom(stripped);
3146     }; break;
3147
3148     case EXFF_TRANSLATE:
3149     {
3150         checkArgsCount(3);
3151         checkIsString2(0,1);
3152         checkIsString(2);
3153
3154         DStr resulting;
3155         Str baseStr, srcStr, destStr;
3156         int pos;
3157         E( atoms[0] -> tostring(S, baseStr) );
3158         E( atoms[1] -> tostring(S, srcStr) );
3159         E( atoms[2] -> tostring(S, destStr) );
3160         char
3161             // changing tostringCharPtr() to tostring():
3162             *p = baseStr,
3163             *src = srcStr,
3164             *dest = destStr,
3165             *destchar;
3166         while(*p)
3167         {
3168             pos = utf8Strchr(src, p);
3169             if (pos == -1) {
3170               destchar = p;
3171             } 
3172             else if ((destchar = utf8StrIndex(dest,pos)) == NULL) {
3173               p += utf8SingleCharLength(p);
3174               continue;
3175             }
3176             resulting.nadd(destchar, utf8SingleCharLength(destchar));
3177             p += utf8SingleCharLength(p);
3178         };
3179         retxpr.setAtom(resulting);
3180     }; break;
3181
3182     case EXFF_BOOLEAN:
3183     {
3184         checkArgsCount(1);
3185         retxpr.setAtom(atoms[0] -> tobool());
3186     }; break;
3187
3188     case EXFF_NOT:
3189     {
3190         checkArgsCount(1);
3191         retxpr.setAtom((Bool)!(atoms[0] -> tobool()));
3192     }; break;
3193
3194     case EXFF_TRUE:
3195     {
3196         checkArgsCount(0);
3197         retxpr.setAtom(TRUE);
3198     }; break;
3199
3200     case EXFF_FALSE:
3201     {
3202         checkArgsCount(0);
3203         retxpr.setAtom(FALSE);
3204     }; break;
3205
3206     case EXFF_LANG:
3207     {
3208         checkArgsCount(1);
3209         checkIsString(0);
3210         // get the argument
3211         Str langQuery;
3212         E( atoms[0] -> tostring(S, langQuery) );
3213         NodeHandle w, att = NULL;
3214         int attCount, i;
3215         const char* langValue = NULL;
3216         for (w = c -> current(); w && !langValue; w = S.dom().getParent(w))
3217         {
3218             // find whether w has an xml:lang attribute
3219           if (!S.domExternal(w)) {
3220             QName searchName;
3221             searchName.setUri(getOwnerTree().unexpand(theXMLNamespace));
3222             searchName.setLocal(getOwnerTree().unexpand("lang"));
3223             int idx = toE(w) -> atts.findNdx(searchName);
3224             if (idx != -1) {
3225               langValue = toA( toE(w) -> atts[idx]) -> cont;
3226             }
3227           } else {
3228             attCount = S.dom().getAttributeCount(w);
3229             for (i = 0; i < attCount && !langValue; i++)
3230               {
3231                 att = S.dom().getAttributeNo(w, i);
3232                 const char *uri = S.dom().getNodeNameURI(att);
3233                 const char *local = S.dom().getNodeNameLocal(att);
3234                 if (!strcmp(uri, theXMLNamespace)
3235                     && !strcmp(local, "lang"))
3236                     langValue = S.dom().getNodeValue(att);
3237                 S.dom().freeName(att, (char*)uri);
3238                 S.dom().freeName(att, (char*)local);
3239               }
3240           }
3241         }
3242         if (langValue)
3243         {
3244           char *langQPtr = (char*) langQuery;
3245             int qlen;
3246             
3247             // if strings equal except for possibly a suffix starting with -
3248             // then return TRUE
3249             if (!strncasecmp(langQPtr, langValue, qlen = langQuery.length())
3250                 && (langValue[qlen] == 0 || langValue[qlen] == '-'))
3251               retxpr.setAtom(TRUE);
3252             else
3253               retxpr.setAtom(FALSE);
3254             if (att)
3255               S.dom().freeValue(att, (char*)langValue);
3256         }
3257         else
3258             retxpr.setAtom(FALSE);
3259     }; break;
3260
3261     case EXFF_NUMBER:
3262     {
3263         checkArgsCountMax(1);
3264         Number n;
3265         if (!atomsNumber)
3266         {
3267             Str string;
3268             E( getCurrValue(S, string, c) );
3269             n = string;
3270         }
3271         else
3272             n = atoms[0] -> tonumber(S);
3273         retxpr.setAtom(n);
3274     }; break;
3275
3276     case EXFF_SUM:
3277     {
3278         DStr string;
3279         Number n, sum = 0;
3280         checkArgsCount(1);
3281         checkIsNodeset(0);
3282         GP( Context ) newc = &(atoms[0] -> tonodeset());
3283         (*newc).reset();
3284         while (!(*newc).isFinished())
3285         {
3286             string.empty();
3287             S.dom().constructStringValue((*newc).current(), string);
3288             n = string;
3289             if (n.isNaN())
3290             {
3291                 sum.setNaN(); 
3292                 break;
3293             };
3294             sum = sum + n;
3295             (*newc).shift();
3296         };
3297         newc.del();
3298         retxpr.setAtom(sum);
3299     }; break;
3300
3301     case EXFF_FLOOR:
3302     case EXFF_CEILING:
3303     case EXFF_ROUND:
3304     {
3305         checkArgsCount(1);
3306         checkIsNumber(0);
3307         Number n = atoms[0] -> tonumber(S);
3308         switch(functor)
3309         {
3310         case EXFF_FLOOR:
3311             n = floor((double)n); break;
3312         case EXFF_CEILING:
3313             n = ceil((double)n); break;
3314         case EXFF_ROUND:
3315             n = floor((double)n + .5); break;
3316         };
3317         retxpr.setAtom(n);
3318     }; break;
3319
3320     case EXFF_DOCUMENT:
3321     {
3322         checkArgsCountMin(1);
3323         checkArgsCountMax(2);
3324         checkIsString(0);
3325         DStr location;
3326         Str baseUri;
3327         if (atomsNumber == 2)
3328           {
3329             checkIsNodeset(1);
3330             const Context& aux = atoms[1] -> tonodesetRef();
3331             if ( ! aux.isVoid() ) 
3332               {
3333                 NodeHandle n = aux[0];
3334                 if (! S.domExternal(n) ) 
3335                   {
3336                     baseUri = NZ(toV(n) -> subtree) -> getBaseURI();
3337                   } else {
3338                     baseUri = "";
3339                   }
3340               } else {
3341                 baseUri = "";
3342               }
3343             //atoms[1] -> tostring(S, baseUri);
3344           }
3345         else
3346           baseUri = "";
3347         NodeHandle newroot = NULL;
3348         Processor *proc = S.getProcessor();
3349         //assert(S.domExternal((void*)1) || proc); //_ph_ sxp
3350         GP( Context ) newc = new Context(c->getCurrentNode());
3351
3352         // Current node doesn't change
3353         //(*newc).setCurrentNode (c->getCurrentNode());
3354
3355         // GP: the context doesn't autodelete anything on error
3356         // since readTreeFromURI adds the trees to datalines list
3357         // All datalines removed on error (in cleanupAfterRun())
3358
3359         if (atoms[0] -> type == EX_NODESET)
3360         {
3361             const Context& ctxt = atoms[0] -> tonodesetRef();
3362             int ctxtNumber = ctxt.getSize();
3363             for (int k = 0; k < ctxtNumber; k++)
3364             {
3365                 S.dom().constructStringValue(ctxt[k], location);
3366                 E( getDocument_(S, newroot, location, baseUri, proc) );
3367                 // check for duplicities and correct URI sorting!
3368                 if (! nhNull(newroot) ) 
3369                   {
3370                     (*newc).append(newroot);
3371                     if (proc && !S.domExternal(newroot))  // _ph_ spx
3372                       E( proc -> makeKeysForDoc(S, newroot) );
3373                   }
3374             };
3375         }
3376         else
3377         {
3378             E( atoms[0] -> tostring(S, location) );
3379             E( getDocument_(S, newroot, location, baseUri, proc) );
3380             if (! nhNull(newroot) )
3381               {
3382                 (*newc).append(newroot);
3383                 if (proc && !S.domExternal(newroot)) //_ph_ spx
3384                   E( proc -> makeKeysForDoc(S, newroot) );
3385               }
3386         }
3387         retxpr.setAtom(newc.keep());
3388     }; break;
3389
3390     case EXFF_GENERATE_ID:
3391     {
3392         DStr s;
3393         switch(atomsNumber)
3394         {
3395         case 0:
3396             v = (c -> isFinished() ? NULL : c -> current());
3397             break;
3398         case 1:
3399         {
3400             checkIsNodeset(0);
3401             const Context& newc = atoms[0] -> tonodesetRef();
3402             v = (newc.isVoid()? NULL : newc.current());
3403         }; break;
3404         default:
3405             Err(S, ET_BAD_ARGS_N);
3406         };
3407         if (v)
3408         {
3409             s = "i__";
3410             if (S.domExternal(v))
3411               {
3412                 // need cast to long then to int to avoid
3413                 // compiler error on IBM AIX
3414                 s += (int) (long) v;
3415               }
3416             else 
3417               {
3418                 s += (int) (long) &(toV(v) -> getOwner());
3419                 s += "_";
3420                 s += toV(v) -> stamp;
3421               }
3422         }
3423         retxpr.setAtom(s);
3424     }; break;
3425
3426     case EXFF_SYSTEM_PROPERTY:
3427     {
3428         checkArgsCount(1);
3429         checkIsString(0);
3430         QName q;
3431         Str a0Str;
3432         E( atoms[0] -> tostring(S, a0Str) );
3433         E( getOwnerElement().setLogical(S, q, a0Str, FALSE) );
3434         if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_XSL_NAMESPACE))
3435           {
3436             const Str& localStr = getOwnerTree().expand(q.getLocal());
3437             if (localStr == (const char*) "version")
3438               retxpr.setAtom(Number(1.0));
3439             else if (localStr == (const char*) "vendor")
3440               retxpr.setAtom(Str("Ginger Alliance"));
3441             else if (localStr == (const char*) "vendor-url")
3442               retxpr.setAtom(Str("www.gingerall.com"));
3443             else
3444               retxpr.setAtom(Str(""));
3445         }
3446         else if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_SABEXT_NAMESPACE))
3447           {
3448             const Str& localStr = getOwnerTree().expand(q.getLocal());
3449             if (localStr == (const char*) "version")
3450               retxpr.setAtom(Str(SAB_VERSION));
3451             else
3452               retxpr.setAtom(Str(""));
3453           }
3454         else
3455             retxpr.setAtom(Str(""));
3456     }; break;
3457
3458     case EXFF_CURRENT:
3459     {
3460         Context *newc = new Context(NULL);  //_cn_ no need for cn
3461         newc -> set ( NZ(c -> getCurrentNode()));
3462         //Context *origc = getOwnerElement().getOrigContext();
3463         //assert(origc);
3464         //newc -> set ( origc -> current());
3465         retxpr.setAtom (newc);
3466     }; break;
3467     case EXFF_KEY:
3468     {
3469         checkArgsCount(2);
3470         checkIsString(0);
3471         QName q;
3472         Str a0Str;
3473         E( atoms[0] -> tostring(S, a0Str) );
3474         getOwnerElement().setLogical(S, q, a0Str, FALSE);
3475         EQName ename;
3476         getOwnerTree().expandQ(q, ename);
3477         Processor *proc = NZ(S.getProcessor());
3478         GP( Context ) newc = new Context(NULL); //_cn_
3479         GP( Context ) newc2;
3480         Context auxContext(NULL);
3481         DStr oneString;
3482         if (atoms[1] -> type == EX_NODESET)
3483         {
3484             const Context& ctxt = atoms[1] -> tonodesetRef();
3485             int ctxtNumber = ctxt.getSize();
3486             for (int k = 0; k < ctxtNumber; k++)
3487             {
3488                 S.dom().constructStringValue(ctxt[k], oneString);
3489                 E( proc -> getKeyNodes(S, ename, 
3490                                        oneString, auxContext, 
3491                                        S.dom().getOwnerDocument(c -> current())) );
3492                 newc2 = (*newc).swallow(S, &auxContext);
3493                 newc.del();
3494                 newc.assign(newc2.keep());
3495             };
3496         }
3497         else
3498         {
3499             E( atoms[1] -> tostring(S, oneString) );
3500             E( proc -> getKeyNodes(S, ename, 
3501                                    oneString, *newc, 
3502                                    S.dom().getOwnerDocument(c -> current())) );
3503         }
3504         retxpr.setAtom(newc.keep());                                    
3505     }; break;
3506     case EXFF_FORMAT_NUMBER:
3507     {
3508         checkArgsCountBetween(2,3);
3509         Number num = atoms[0] -> tonumber(S);
3510         Str fmt;
3511         E( atoms[1] -> tostring(S, fmt) );
3512         EQName ename;
3513         if (atomsNumber == 3)
3514         {
3515             Str nameStr;
3516             E( atoms[2] -> tostring(S, nameStr) );
3517             QName q;
3518             getOwnerElement().setLogical(S, q, nameStr, FALSE);
3519             getOwnerTree().expandQ(q, ename);
3520         }
3521         Str result;
3522         E( NZ(S.getProcessor()) -> decimals().format(S, ename, num, fmt, result) );
3523         retxpr.setAtom(result); 
3524     }; break;
3525
3526     case EXFF_ID:
3527     {
3528         checkArgsCount(1);
3529         GP( Context ) result = new Context(NULL);
3530         DStr ids;
3531         if (atoms[0] -> type == EX_NODESET)
3532         {
3533             const Context& ctxt = atoms[0] -> tonodesetRef();
3534             int ctxtNumber = ctxt.getSize();
3535             for (int k = 0; k < ctxtNumber; k++)
3536             {
3537                 S.dom().constructStringValue(ctxt[k], ids);
3538                 appendNodesWithID(S, ids, c, *result);
3539             };
3540         }
3541         else
3542         {
3543             E( atoms[0] -> tostring(S, ids) );
3544             appendNodesWithID(S, ids, c, *result);
3545         };
3546         E( (*result).sort(S) );
3547         (*result).uniquize();
3548         retxpr.setAtom(result.keep());
3549     }; break;
3550
3551     case EXFF_FUNCTION_AVAILABLE:
3552       {
3553         checkArgsCount(1);
3554         Str nameStr;
3555         QName funcName;
3556         E( atoms[0] -> tostring(S, nameStr));
3557         E( getOwnerElement().setLogical(S, funcName, nameStr, FALSE) );
3558         Str uri = getOwnerTree().expand(funcName.getUri());
3559         Str name = getOwnerTree().expand(funcName.getLocal());
3560         retxpr.setAtom(S.getProcessor() -> supportsFunction(uri, name));
3561       }; break;
3562       
3563     case EXFF_ELEMENT_AVAILABLE:
3564       {
3565         checkArgsCount(1);
3566         Str nameStr;
3567         QName elName;
3568         E( atoms[0] -> tostring(S, nameStr));
3569         E( getOwnerElement().setLogical(S, elName, nameStr, FALSE) );
3570         Bool ret = ExtensionElement::elementAvailable(getOwnerTree(), elName);
3571         retxpr.setAtom(ret);
3572       }; break;
3573     case EXFF_UNPARSED_ENTITY_URI:
3574       {
3575         checkArgsCount(1);
3576         NodeHandle curr;
3577         NZ( curr = c -> current() );
3578         if (S.domExternal(curr))
3579           {
3580             retxpr.setAtom(""); //not supported for external docs
3581             //add check for SXPF_SUPPORTS_UNPARSED_ENTITIES
3582           }
3583         else
3584           {
3585             Str name;
3586             E( atoms[0] -> tostring(S, name) );
3587             Str *uri = toV(curr) -> getOwner().getUnparsedEntityUri(name);
3588             if (uri) 
3589               retxpr.setAtom(*uri);
3590             else
3591               retxpr.setAtom("");
3592           }
3593       }; break;
3594
3595     default:
3596         Err1(S, ET_FUNC_NOT_SUPPORTED, getFuncName(functor));
3597     }
3598     return OK;
3599 }
3600
3601 eFlag Expression::createLPContext(Sit S, Context *&c, int baseNdx, NodeHandle givenGlobalCurrent /* = NULL */)
3602 {
3603     assert(functor == EXF_LOCPATH);
3604     GP( Context ) theResult = new Context(c->getCurrentNode()); //_cn_ result has no cn
3605     Context info(givenGlobalCurrent ? givenGlobalCurrent : c -> getCurrentNode()); //_cn_
3606     //info.setCurrentNode(givenGlobalCurrent ? givenGlobalCurrent : c -> current());
3607     //(*theResult).setCurrentNode(c -> getCurrentNode());
3608     E( createLPContextLevel(S, 0, args.number(), c -> current(), info, theResult) );
3609     E( (*theResult).sort(S) );
3610     (*theResult).uniquize();
3611     c = theResult.keep();
3612     return OK;
3613 }
3614
3615 /*
3616  *  createLPContextLevel
3617  *  ranges over all nodes that satisfy the stepLevel-th step, calling self
3618  *  recursively until the last one is reached. The vertices satisfying the last
3619  *  step are added to theResult. 
3620  *  Base is passed from the preceding step and used for expression
3621  *  evaluation. info holds the 'globally current' vertex.
3622  *  The purpose of this routine is to generate a context without having to
3623  *  also generate the intermediate contexts for each step. Also, some of the predicates
3624  *  may be known to use last(), in which case we first have to compute the number
3625  *  of nodes that reach such a predicate at all. 
3626  */
3627
3628 eFlag Expression::createLPContextLevel(Sit S, 
3629     int stepLevel, int stepsCount, NodeHandle base,
3630     Context &info, Context *theResult)
3631 {
3632     // GP: theResult will be freed on error since the caller (createLPContext) holds it in a GP
3633
3634     assert(functor == EXF_LOCPATH);
3635     int i, j, init,
3636         predsCount = args[stepLevel] -> step -> preds.number(),
3637         lastBad = -1;     // last bad predicate, or the step itself
3638
3639
3640     // keep a stack of positions, one for each predicate IN THIS STEP
3641     List<int> reached(predsCount),  // serves as position for next pred
3642         totalReached(predsCount);   // serves as size for next (bad) pred
3643
3644     // there will be as many dry (size-counting) runs as there are bad preds
3645     Bool dryRun = TRUE,
3646         quitThisRound = FALSE, quitThisVertex = FALSE;
3647
3648     // i ranges over predicates. Value i==predsCount is the special last run
3649     for (i = 0; i <= predsCount; i++)
3650     {
3651         if (i == predsCount)
3652             // the last run, not a dry-run
3653             dryRun = FALSE;
3654         // if this is the last run, or if the current pred uses last(), compute
3655         // the context size
3656         if (!dryRun || args[stepLevel] -> step -> preds[i] -> usesLast)
3657         {
3658             // initialize the size arrays: 
3659             // append base values for preds past the last bad one, 
3660             // up to this bad one (incl.)
3661             for (init = 0; init <= lastBad; init++)
3662                 reached[init] = 0;
3663             for (init = lastBad + 1; init <= i; init++)
3664             {
3665                 reached.append(0);
3666                 totalReached.append(-1);     // -1 just for safety
3667             };
3668
3669             // locally current vertex 
3670             NodeHandle locCurr = NULL;
3671
3672             quitThisRound = FALSE;
3673             do
3674             {
3675                 // shift the locally current vertex
3676               //_speed_ this should be replaced with smart context 
3677               //created with createContext on local step argument
3678                 E( args[stepLevel] -> step -> shift(S, locCurr, base) );
3679                 if (!nhNull(locCurr))
3680                 {
3681                     if ((lastBad < 0) || !dryRun) ++reached[0];
3682                     quitThisVertex = FALSE;
3683                     for (j = 0; j < i; j++)
3684                     {
3685                         Bool satisfies;
3686                         info.deppendall();
3687                         // set locCurr as current at position reached[j]-1 in the context
3688                         info.setVirtual(locCurr, reached[j] - 1, totalReached[j]);
3689
3690                         Expression *thisPred = 
3691                             args[stepLevel] -> step -> preds[j];
3692                         // find whether we're in position bounds for this pred
3693                         switch(thisPred -> inBounds(reached[j] - 1))
3694                         {
3695                         case 0:
3696                             // within bounds
3697                             {
3698                                 E( thisPred -> trueFor(S, &info, satisfies) );
3699                                 if (satisfies)
3700                                     ++reached[j + 1];
3701                                 else
3702                                     quitThisVertex = TRUE;
3703                             }; break;
3704                         case -1:
3705                             // before start, move to another vertex
3706                             quitThisVertex = TRUE;
3707                             break;
3708                         case 1:
3709                             // past the end, bail out
3710                             quitThisRound = TRUE;
3711                             break;
3712                         };
3713                         if (quitThisVertex || quitThisRound)
3714                             break;
3715                     };
3716                     if (j == i && !dryRun)     // passed all preds
3717                     {
3718                         if (stepLevel < stepsCount - 1)
3719                             E( createLPContextLevel(S, 
3720                                 stepLevel + 1, stepsCount,
3721                                 locCurr, info, theResult))
3722                         else
3723                             theResult -> append(locCurr);
3724
3725                     }   // if ! dryRun
3726                 }       // if locCurr
3727             } while (!nhNull(locCurr) && !quitThisRound);
3728             // move all data collected to safe places
3729             for (init = lastBad + 1; init <= i; init++)
3730                 totalReached[init] = reached[init];
3731             lastBad = i;
3732         }               // if bad predicate
3733     }                   // for, over all preds
3734     return OK;
3735 }
3736
3737
3738 eFlag Expression::createLPContextSum(Sit S, Context *&c, NodeHandle globalCurrent /* = NULL */)
3739 {
3740     assert(functor == EXF_LOCPATH);
3741     GP( Context ) newc = new Context(c->getCurrentNode()); //_cn_ needed?
3742     Context 
3743         *newc2, *returnedc;
3744     int cNumber = c -> getSize();
3745     for (int j = 0; j < cNumber; j++)
3746     {
3747         E( createLPContext(S, returnedc = c, j, globalCurrent) );
3748         newc2 = (*newc).swallow(S, returnedc);
3749         newc.del();
3750         newc = newc2;
3751         delete returnedc;
3752         /* tom 01/11/25 */
3753         c -> shift();
3754     }
3755     c = newc.keep();
3756     return OK;
3757 }
3758
3759
3760 /*................................................................
3761 createContext()
3762     creates a context for this expression, based on its functor.
3763 ................................................................*/
3764
3765 // GP: createContext is clean
3766 // if unsuccessful, returns NULL in c and performs no allocations
3767
3768
3769 eFlag Expression::createContext(Sit S, Context *& c, int baseNdx /* = -1 */)
3770 {
3771     GP( Context ) newc; // newc gets assigned c ONLY IN THE END
3772     Context *c_orig = c;
3773     newc.assign(c);
3774     c = NULL;
3775
3776     int i, j, 
3777         argsNumber = args.number();
3778     if (baseNdx == -1)
3779         baseNdx = (*newc).getPosition();
3780     switch(functor)
3781     {
3782     case EXF_VAR:
3783         {
3784           Expression *deref = NULL;
3785           if (S.getProcessor())
3786             deref = S.getProcessor() -> getVarBinding(*pName);
3787           if (!deref)
3788           {
3789               Str fullName;
3790               getOwnerTree().expandQStr(*pName, fullName);
3791               Err1(S, E1_VAR_NOT_FOUND, fullName);
3792           }
3793           NodeHandle current_node = (*newc).getCurrentNode();
3794           E( deref -> createContext(S, newc, baseNdx) );
3795           newc.unkeep();
3796           (*newc).setCurrentNode(current_node);
3797         };
3798         break;
3799     case EXF_ATOM:
3800         {
3801             if (type != EX_NODESET)
3802                 Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3803             newc = patomnodeset -> copy();
3804             newc.unkeep();
3805         }; break;
3806     case EXFO_UNION:
3807         {
3808             assert(baseNdx != -1);  // meaningful only for a locpath
3809             GP( Context ) csummand;
3810             Context *newc2;     // GP: OK
3811             assert(argsNumber);
3812             E( args[0] -> createContext(S, newc, baseNdx) );
3813             newc.unkeep();
3814             for (i = 1; i < argsNumber; i++)
3815             {
3816                 csummand.assign(c_orig);
3817                 E( args[i] -> createContext(S, csummand, baseNdx) );
3818                 newc2 = (*newc).swallow(S, csummand);
3819                 csummand.del();
3820                 newc.del();
3821                 newc = newc2;
3822             }
3823             // clean up newc!
3824             (*newc).reset();
3825         }
3826         break;
3827     case EXF_LOCPATH:
3828         {
3829             E( createLPContext(S, newc, baseNdx) );
3830             newc.unkeep();
3831         }
3832         break;
3833     case EXF_FILTER:
3834         {
3835             assert(baseNdx != -1);  // meaningful only for a locpath
3836             NodeHandle wasCurrent = (*newc).getCurrentNode();
3837             E( args[0] -> createContext(S, newc, baseNdx) );
3838             newc.unkeep();
3839             (*newc).setCurrentNode(wasCurrent);
3840
3841             GP( Context ) filteredc;
3842             for (i = 1; i < argsNumber - (int) hasPath; i++)
3843             {
3844                 filteredc = new Context(c_orig -> getCurrentNode());
3845                 (*newc).reset();
3846                 Bool istrue;
3847                 int newcNumber = (*newc).getSize();
3848                 for (j = 0; j < newcNumber; j++)
3849                 {
3850                     E(args[i] -> trueFor(S, newc, istrue));
3851                     if (istrue)
3852                         (*filteredc).append((*newc)[j]);
3853                     (*newc).shift();
3854                 };
3855                 newc.del();
3856                 newc = filteredc.keep();
3857                 if (!(*newc).getSize()) break;
3858             };
3859             if (hasPath)
3860             {
3861                 filteredc.assign(newc);
3862                 filteredc = newc;  // a patch due to SGI MIPSpro compiler
3863                 E( args[argsNumber-1] -> createLPContextSum(S, filteredc, (*newc).getCurrentNode()) );
3864                 newc.del();
3865                 newc = filteredc.keep();
3866             }
3867         }
3868         break;
3869     case EXF_LOCSTEP:
3870         {
3871             assert(step);
3872             assert(baseNdx != -1);  // meaningful only for a locpath
3873
3874             /////////
3875             // E( step -> createContextNoPreds(newc = c, baseNdx) );    - done as follows:
3876             GP( Context ) newc2 = new Context(c_orig -> getCurrentNode());
3877             NodeHandle curr = NULL;
3878             do
3879             {
3880               //_speed_ here we should test the axis type
3881               // and use smart context if possible
3882                 E( step -> shift(S, curr, (*newc)[baseNdx]) );
3883                 if (!nhNull(curr))
3884                     (*newc2).append(curr);
3885             } 
3886             while (!nhNull(curr));
3887             /////////
3888
3889             GP( Context ) filteredc;
3890             int stepPredsNumber = step -> preds.number();
3891             for (i = 0; i < stepPredsNumber; i++)
3892             {
3893                 filteredc = new Context(c_orig -> getCurrentNode());
3894                 (*newc2).reset();
3895                 Bool istrue;
3896                 int newc2Number = (*newc2).getSize();
3897                 for (j = 0; j < newc2Number; j++)
3898                 {
3899                     E( step -> preds[i] -> trueFor(S, newc2,istrue) );
3900                     if (istrue)
3901                         (*filteredc).append((*newc2)[j]);
3902                     (*newc2).shift();
3903                 };
3904                 newc2.del();
3905                 newc2 = filteredc.keep();
3906                 if (!(*newc2).getSize()) break;
3907             };
3908             newc = newc2.keep();
3909         };
3910         break;
3911     case EXF_OTHER_FUNC:
3912         {
3913           Expression resolved(getOwnerElement());
3914           E( eval(S, resolved, newc) );
3915           E( resolved.createContext(S, newc, baseNdx) );
3916           newc.unkeep();
3917         }; break;
3918     default:
3919       if (funcIsBuiltin(functor))
3920         {
3921             Expression resolved(getOwnerElement());
3922             E( eval(S, resolved, newc) );
3923             E( resolved.createContext(S, newc, baseNdx) );
3924             newc.unkeep();
3925         }
3926         else
3927         Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3928     };
3929     c = newc.keep();
3930     return OK;
3931 }
3932
3933
3934 eFlag Expression::matchesSingleStep(Sit S, NodeHandle v, Bool &result)
3935 {
3936     assert(functor == EXF_LOCSTEP);
3937     if (!NZ(step) -> matchesWithoutPreds(S, v))
3938         RetOK(result, FALSE);
3939     if (!step -> preds.number())
3940         RetOK(result, TRUE);
3941     if (!S.dom().getParent(v))
3942         RetOK(result, FALSE);
3943     if (!step -> positional)
3944     {
3945       GP( Context ) c = new Context(NULL); //_cn_ current() is not allowed in patterns
3946         (*c).set(v);
3947         Bool stillOK = TRUE;
3948         for (int i = 0; i < step -> preds.number() && stillOK; i++)
3949             E(step -> preds[i] -> trueFor(S, c,stillOK));
3950         c.del();
3951         RetOK(result,stillOK);
3952     }
3953     else // positional case
3954     {
3955         GP( Context ) c = new Context(NULL);  //_cn_ current() is not allowed in patterns
3956         Context *newc; // GP: OK
3957         (*c).set(S.dom().getParent(v));
3958         E( createContext(S, newc = c+0, 0) );
3959         result = (newc -> contains(v));
3960         c.del();
3961         delete newc;
3962     }
3963     return OK;
3964 }
3965
3966
3967 eFlag Expression::matchesSinglePath(Sit S, NodeHandle v, int lastIndex, Bool& result)
3968 {
3969     assert(functor == EXF_LOCPATH);
3970     int i;
3971     NodeHandle w = v;
3972     for (i = lastIndex; i >= 0; i--)
3973     {
3974         if (!w) 
3975             RetOK(result, FALSE);
3976         switch(args[i] -> step -> ax)
3977         {
3978         case AXIS_ROOT:
3979                 {
3980                 if (i)
3981                     assert(!"root not first");
3982                 E( args[i] -> matchesSingleStep(S, w, result) );
3983                 if (!result) RetOK(result, FALSE);
3984                     };
3985             break;
3986         case AXIS_CHILD:
3987         case AXIS_ATTRIBUTE:
3988             {
3989                 E( args[i] -> matchesSingleStep(S, w, result) );
3990                 if (!result) RetOK(result, FALSE);
3991                 w = S.dom().getParent(w);
3992             };
3993             break;
3994         case AXIS_DESC_OR_SELF:
3995             {
3996                 E( args[i] -> matchesSingleStep(S, w, result) );
3997                 if (!result) RetOK(result, FALSE);
3998                 NodeHandle previous = w;
3999                 while (previous)
4000                 {
4001                     E(matchesSinglePath(S, previous, i-1, result));
4002                     if (result)
4003                         return OK;
4004                     else
4005                         previous = S.dom().getParent(previous);
4006                 };
4007                 RetOK( result, FALSE );
4008             }
4009             break;
4010         default:
4011             assert(!"bad axis in pattern");
4012         }
4013     }
4014     result = TRUE;
4015     return OK;
4016 }
4017
4018 /*
4019  *  optimizePositional()
4020  *  called for a predicate of a locstep
4021  *  returns 2 if the predicate uses the last() function
4022  *    so the size of the context has to be determined
4023  *  returns 1 if the predicate only uses position()
4024  *          0 if neither
4025  */
4026
4027 int Expression::optimizePositional(int level)
4028 {
4029     int result = 0;
4030
4031     switch(functor)
4032     {
4033     case EXFF_LAST:
4034         result = 2; break;
4035     case EXFF_POSITION:
4036         result = 1; break;
4037     case EXF_ATOM:
4038     case EXF_VAR:
4039     case EXF_LOCPATH:
4040         /* result = 0; */ break;
4041     case EXF_STRINGSEQ:
4042     case EXF_FRAGMENT:
4043     case EXF_LOCSTEP:
4044         assert(!"invalid predicate type");
4045         break;
4046     case EXF_FILTER: // can be e.g. "current()/@attr"
4047     default: // all the functions and operators, including EXF_OTHER_FUNC
4048         {
4049             int sub = 0;
4050             for (int i = 0; i < args.number(); i++)
4051             {
4052                 if (!!(sub = args[i] -> optimizePositional(level + 1)))
4053                 {
4054                     result = sub;
4055                     if (result == 2) break;
4056                 }
4057             }
4058         }
4059     }
4060     //PH: when we're called w/o recursion, 
4061     //we might be called for situation like foo[45], 
4062     //so we should return 1, if the expression 
4063     //is of the type of EX_NUMBER
4064     if (!level && type == EX_NUMBER && !result) result = 1;
4065
4066     usesLast = (result == 2);
4067     positional = (result >= 1);
4068     return result; 
4069 }
4070
4071 /*
4072  *  optimizePositionBounds()
4073  *  called for a predicate of a locstep
4074  *  returns the range of positions that need to be examined for a context
4075  *  e.g. the predicate in foo[1] will return both set to 1.
4076  *  the positions returned are 1-based, 0 means "no restriction"
4077  */
4078
4079 void Expression::optimizePositionBounds()
4080 {
4081     int from = 0, to = 0;
4082     switch(functor)
4083     {
4084     case EXF_ATOM:
4085         {
4086             if (type == EX_NUMBER)
4087                 from = to = NZ(patomnumber) -> round();    // bad values like NaN return 0 which is OK.
4088         }; break;
4089     case EXFO_EQ:
4090     case EXFO_LE:
4091     case EXFO_LT:
4092     case EXFO_GE:
4093     case EXFO_GT:
4094         {
4095             if (args[0] -> functor == EXFF_POSITION && 
4096                 args[1] -> functor == EXF_ATOM && args[1] -> type == EX_NUMBER)
4097             {
4098                 int bound = args[1] -> patomnumber -> round();
4099                 switch(functor)
4100                 {
4101                 case EXFO_EQ: from = to = bound; break;
4102                 case EXFO_LE: to = bound; break;
4103                 case EXFO_LT: to = bound - 1; break;
4104                 case EXFO_GE: from = bound; break;
4105                 case EXFO_GT: from = bound + 1; break;
4106                 }
4107             }
4108         }; break;
4109     }
4110     optimizePositionFrom = from;
4111     optimizePositionTo = to;
4112 }
4113
4114 int Expression::inBounds(int position) const
4115 {
4116     if (optimizePositionTo && position > optimizePositionTo-1)
4117         return 1;
4118     if (optimizePositionFrom && position < optimizePositionFrom-1)
4119         return -1;
4120     return 0;
4121 }
4122
4123 Element& Expression::getOwnerElement() const
4124 {
4125     return owner;
4126 };
4127
4128 Tree& Expression::getOwnerTree() const
4129 {
4130     return owner.getOwner();
4131 }
4132
4133
4134 void Expression::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
4135 {
4136     getOwnerElement().report(S,type,code,arg1,arg2);
4137 }
4138
4139 Bool Expression::containsFunctor(ExFunctor func)
4140 {
4141   if (functor == func)
4142     return TRUE;
4143   else if (functor == EXF_LOCSTEP)
4144     {
4145       for (int i = 0; i < step -> preds.number(); i++)
4146         {
4147           if (step -> preds[i] -> containsFunctor(func))
4148             return TRUE;
4149         }
4150     }
4151
4152   for (int i = 0; i < args.number(); i++)
4153     {
4154       if (args[i] -> containsFunctor(func))
4155         return TRUE;
4156     }
4157   return FALSE;
4158 }