/* 
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Sablotron XSLT Processor.
 * 
 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
 * Portions created by Ginger Alliance are Copyright (C) 2000 Ginger
 * Alliance Ltd. All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

#include "expr.h"
#include "verts.h"
#include <float.h>
#include <math.h>
#include "context.h"
#include "tree.h"
//#include "proc.h"
//#include <ctype.h>

// period is not here as it can also start a number
char tokenShort[]=  
".. :: // != <= >= "
"(  )  [  ]  @  ,  "
"|  +  -  =  <  >  "
"/  *  "
"\x0\x0\x0";

ExToken tokenShortX[]=
{
    TOK_DPERIOD, TOK_DCOLON, TOK_DSLASH, TOK_NEQ, TOK_LE, TOK_GE,
        TOK_LPAREN, TOK_RPAREN, TOK_LBRACKET, TOK_RBRACKET, TOK_ATSIGN, TOK_COMMA,
        TOK_VERT, TOK_PLUS, TOK_MINUS, TOK_EQ, TOK_LT, TOK_GT,
        TOK_SLASH, TOK_STAR
};

/*================================================================
    function info table
================================================================*/

struct FuncInfoItem 
{
    char *name;
    ExFunctor func;
    ExType type;
} 
funcInfoTable[] =
{
    // XPath functions - NODESET category
    {"last",EXFF_LAST,EX_NUMBER},
    {"position",EXFF_POSITION,EX_NUMBER},
    {"count",EXFF_COUNT,EX_NUMBER},
    {"id",EXFF_NONE,EX_NODESET},
    {"local-name",EXFF_LOCAL_NAME,EX_STRING},
    {"namespace-uri",EXFF_NAMESPACE_URI,EX_STRING},
    {"name",EXFF_NAME,EX_STRING},

    // XPath - STRING category
    {"string",EXFF_STRING,EX_STRING},
    {"concat",EXFF_CONCAT,EX_STRING},
    {"starts-with",EXFF_STARTS_WITH,EX_BOOLEAN},
    {"contains",EXFF_CONTAINS,EX_BOOLEAN},
    {"substring-before",EXFF_SUBSTRING_BEFORE,EX_STRING},
    {"substring-after",EXFF_SUBSTRING_AFTER,EX_STRING},
    {"substring",EXFF_SUBSTRING,EX_STRING},
    {"string-length",EXFF_STRING_LENGTH,EX_NUMBER},
    {"normalize-space",EXFF_NORMALIZE_SPACE,EX_STRING},
    {"translate",EXFF_TRANSLATE,EX_STRING},

	// Upfront - String category
	{"url-encode",EXFF_URL_ENCODE, EX_STRING},

    // XPath - BOOLEAN category
    {"boolean",EXFF_BOOLEAN,EX_BOOLEAN},
    {"not",EXFF_NOT,EX_BOOLEAN},
    {"true",EXFF_TRUE,EX_BOOLEAN},
    {"false",EXFF_FALSE,EX_BOOLEAN},
    {"lang",EXFF_LANG,EX_BOOLEAN},

    // XPath - NUMBER category
    {"number", EXFF_NUMBER, EX_NUMBER},
    {"sum", EXFF_SUM, EX_NUMBER},
    {"floor", EXFF_FLOOR, EX_NUMBER},
    {"ceiling", EXFF_CEILING, EX_NUMBER},
    {"round", EXFF_ROUND, EX_NUMBER},

    // XSLT core
    {"document",EXFF_DOCUMENT,EX_NODESET},
    {"key",EXFF_KEY,EX_NODESET},
    {"format-number",EXFF_FORMAT_NUMBER, EX_STRING},
    {"current",EXFF_CURRENT, EX_NODESET},
    {"unparsed-entity-uri",EXFF_UNPARSED_ENTITY_URI, EX_STRING},
    {"generate-id",EXFF_GENERATE_ID,EX_STRING},
    {"system-property",EXFF_SYSTEM_PROPERTY, EX_STRING},
    {NULL, EXFF_NONE, EX_UNKNOWN}
};

Str getFuncName(ExFunctor functor)
{
    return funcInfoTable[functor - EXF_FUNCTION - 1].name;
}

/**********************************************************
TokenItem
**********************************************************/

void TokenItem::speak(DStr &s, SpeakMode mode)
{
    switch(tok)
    {
    case TOK_VAR:   // remove leading $
        s.nadd(firstc + 1, len - 1);
        break;
    case TOK_LITERAL:// remove enclosing quotes or dquotes
        s.nadd(firstc + 1, len - 2);
        break;
    default:
        s.nadd(firstc,len);
    };
//    s += '\0'; - thrown out
};

/**********************************************************

  Tokenizer

**********************************************************/

Tokenizer::Tokenizer()
:
items(LIST_SIZE_EXPR_TOKENS)
{};

Tokenizer::~Tokenizer()
{
    items.freeall(FALSE);
}

eFlag Tokenizer::tokenize(const Str &astring)
{
    char *p;
    TokenItem item;
    string = astring;
    p = (char *) string;

    E( getToken(p, item, TOK_NONE) );
    ExToken prevToken = item.tok;
    while ((item.tok != TOK_END) && (item.tok != TOK_NONE))
    {
        items.append(new TokenItem(item));
        E( getToken(p, item, prevToken) );        
        prevToken = item.tok;
    };
    
    if (item.tok == TOK_NONE)
    {
        DStr itemStr;
        item.speak(itemStr, SM_OFFICIAL);
        Err1(ET_BAD_TOKEN,itemStr);
    }
    else
        items.append(new TokenItem(item));

    return OK;
}

void Tokenizer::speak(DStr &s, SpeakMode mode)
{
    if (mode & SM_DESCRIBE)
        s += "[tokenizer]";
    if (!(mode & SM_CONTENTS))
        return;
    int i, itemsNumber = items.number();
    for (i = 0; i < itemsNumber; i++)
    {
        s += "token ";
        s += (int) items[i] -> tok;
        s += " ";
        items[i] -> speak(s,mode);
        s += " len=";
        s += items[i] -> len;
        s += "\n";
    };
};

/*================================================================
namerTable
a table of tokens which have the effect that the following
name is recognized as a NCName (rather than operator name) and * is
recognized as a wildcard (rather than multiplication operator).
    The table should end with TOK_NONE (which is of this type too).
================================================================*/

static ExToken namerTable[] = {
    TOK_ATSIGN, TOK_DCOLON, TOK_LPAREN, TOK_LBRACKET,
        // operators:
    TOK_OR, TOK_AND, TOK_EQ, TOK_NEQ, TOK_LT, TOK_GT, TOK_LE, TOK_GE,
    TOK_PLUS, TOK_MINUS, TOK_MINUS1, TOK_MULT, TOK_DIV, TOK_MOD, TOK_VERT,
        // slashes are operators too but not for us
    TOK_SLASH, TOK_DSLASH, TOK_COMMA,
        // TOK_NONE (terminator)
    TOK_NONE};

/*================================================================
Bool isNamer()
returns True iff the token is contained in the namerTable table.
================================================================*/

static Bool isNamer(ExToken tok)
{
    int i;
    if (tok == TOK_NONE) return TRUE;
    for (i = 0; (namerTable[i] != tok) && 
        (namerTable[i] != TOK_NONE); i++);
    return (Bool) (namerTable[i] == tok);
}

/*================================================================
ExToken tryShort()
looks up a few characters at p in the tokenShort table containing
the (up-to-3-char) symbols
RETURNS the token identified, or TOK_NONE if no match is found
================================================================*/

ExToken Tokenizer::tryShort(char*& p, ExToken prevToken)
{
    int i;
    char* t;
    ExToken result;
    
    for (i=0, t=tokenShort; *t; i++,t+=3)
        if (*p==*t)
        if ((t[1] == ' ') || (t[1] == p[1])) break;
    if (*t)
    {
        p += ((t[1] == ' ')? 1 : 2);
        result = tokenShortX[i];
        if (result == TOK_STAR)
            result = (isNamer(prevToken)? TOK_NAME : TOK_MULT);
        if ((result == TOK_MINUS) && isNamer(prevToken))
            result = TOK_MINUS1;
    }
    else result = TOK_NONE;
    return result;
}

/*================================================================
eFlag lookToken()
sets 'ret' to the following token, but does not change the pointer p
================================================================*/

eFlag Tokenizer::lookToken(ExToken &ret, char* p, ExToken prevToken)
{
    // getToken_() changes p but this is passed by value so
    // remains unchanged

    E( getToken_(ret, p, prevToken) );
    return OK;
}

/*================================================================
Bool findChar
sets p to address FOLLOWING the next occurence of c in p
(to skip a character reference, use findChar(p,';'))
RETURNS false iff the next occurence was not found
================================================================*/

static Bool findChar(char*& p, char c)
{
    while (*p && (*p != c)) p++;
    if (*p)
    {
        p++;
        return TRUE;
    }
    else
        return FALSE;
}

/*================================================================
findSame
sets p to address FOLLOWING the next occurence of *p in p
RETURNS false iff another occurence was not found
================================================================*/

static Bool findSame(char*& p)
{
    char first = *p++;
    return findChar(p, first);
};

eFlag Tokenizer::getToken_(ExToken &ret, char*& p, ExToken prevToken)
{
    ExToken tok;
    char c;
    
    skipWhite(p);
    if (!*p)
    {
        ret = TOK_END; 
        return OK;
    }
    else
    {
        // the following may also translate * into TOK_NAME
        if ((tok = tryShort(p, prevToken)) != TOK_NONE)
        {
            ret = tok;
            return OK;
        };
        switch (c = *p)
        {
        case '$': 
            {
                // call getName with prevToken=TOK_NONE
                // to ensure that e.g. 'and' in '$and' is treated as a name
                E( getName(ret,++p,TOK_NONE) );
                if (ret != TOK_NONE)
                    ret = TOK_VAR;
                else 
                    Err(ET_BAD_VAR);
            };
            break;
        case '\"':
        case '\'': 
            if(!findSame(p))
                Err(ET_INFINITE_LITERAL)
            else
                ret = TOK_LITERAL;
            break;
        case '&': assert(0);  //DBG: do not process entity references so far
            break;
        case '.':
            if (Str::isdigit_(*(p+1)))
            {
                E( getNumber(p) );
                ret = TOK_NUMBER;
            }
            else {
                p++; 
                ret = TOK_PERIOD;
            };
            break;
        default:
            {
                if (Str::isdigit_(*p))
                {
                    E( getNumber(p) );
                    ret = TOK_NUMBER;
                }
                else
                {
                    if (Str::namechar(*p) || (*p == '_'))
                    {
                        // the following call finds TOK_NAME, TOK_FNAME,
                        // TOK_AXISNAME,
                        // as well as TOK_AND etc. (based on prev token)
                        E( getName(ret, p, prevToken) ); 
                    }
                    else 
                    {
                        Str temp;
                        temp.nset(p, 1);
                        Err1(ET_BAD_TOKEN,temp); //unknown token
                    }
                }
            };  //default
        };      //switch
    };          //else
    return OK;
};              //getToken_

eFlag Tokenizer::getToken(char*& p, TokenItem& item, ExToken prevToken)
{
    ExToken t;
    skipWhite(p);
    item.firstc = p;
    E( getToken_(t, p, prevToken) );
    item.len = (long)(p - item.firstc);
    item.tok = t;
    return OK;
}

eFlag Tokenizer::getNumber(char*& p)
{
    Bool wasDot = FALSE;
    while ((*p) && (Str::isdigit_(*p) || (*p == '.')))
    {
        if (*p == '.')
            if (wasDot)
            Err(ET_BAD_NUMBER)
            else wasDot = TRUE;
        p++;
    };
    return OK;
};

/*================================================================
getWordOp
checks whether the sequence at p of given length is an operator name
RETURNS the appropriate token if so; TOK_NONE otherwise
================================================================*/

static ExToken getWordOp(char *p, int length)
{
    if (length > 3) return TOK_NONE;
    if (length < 2) length = 2;
    if (!strncmp(p,"or",length)) return TOK_OR;
    if (length < 3) length = 3;
    if (!strncmp(p,"and",length)) return TOK_AND;
    if (!strncmp(p,"div",length)) return TOK_DIV;
    if (!strncmp(p,"mod",length)) return TOK_MOD;
    return TOK_NONE;
}

static Bool isNodeTest(char *p, int length)
{
    const char *q;
    int qlen;
    for (int i = 0; (q = exNodeTypeNames[i]) != NULL; i++)
    {
        if (!strncmp(q,p,
            (length < (qlen = strlen(q))? qlen : length)))
            break;
    };
    return (Bool)(q != NULL);
}

#define nameCharExtended(CH, PTR) ((CH = *PTR)!= 0) &&\
        (Str::namechar(CH) || strchr(".-_:*",CH))

void errorExtractName(MsgCode e, char* from)
{
    char *q = from, c;
    while(nameCharExtended(c,q)) q++;
    char was = *q;
    *q = 0;
    situation.error(E_EXTRA_COLON,from,theEmptyString);
    *q = was;
}

eFlag Tokenizer::getName(ExToken &ret, char*& p, ExToken prevToken)
{
    char *former = p,
        *_former = p;
    char c;
    BOOL wasColon = FALSE;
    
    if ((!Str::namechar(*p)) && (*p != '_'))
    {
        ret = TOK_NONE;
        return OK;
    };
    while (nameCharExtended(c,p))
    {
        if (c == ':')
        {
            if (wasColon)
            {
                // identify the bad qname;
                errorExtractName(E_EXTRA_COLON,_former);
                return NOT_OK;
            }
            else
            {
                switch(*(p+1))
                {
                case ':':
                    {
                        ret = TOK_AXISNAME;
                        return OK;
                    };
                case '*':
                    {
                        ret = TOK_NAME;
                        p += 2;
                        return OK;
                    };
                default:
                    wasColon = TRUE;
                };
            };
        }
        p++;
    }

    if (!wasColon && !isNamer(prevToken))
    {
        if ((ret = getWordOp(former, (int) (p - former))) != TOK_NONE)
            return OK;
    };

    ExToken next;

    // look at following token with prev=TOK_NAME
    E( lookToken(next,p,TOK_NAME) );
    switch(next)
    {
    case TOK_DCOLON:
        ret = TOK_AXISNAME;
        break;
    case TOK_LPAREN:
        {
            if (isNodeTest(former, (int) (p - former)))
                ret = TOK_NTNAME;
            else
                ret = TOK_FNAME;
        }; break;
    default:
        ret = TOK_NAME;
    };
    return OK;
};

/*================================================================
int findTop()
    finds the first top-level occurence of 'token' starting with
    position 'from'. If there is none, return value points at TOK_END.
================================================================*/

int Tokenizer::findTop(ExToken token, int from)
{
    int level = 0;
    ExToken ct;
    int i;
    for (i = from; 
        ((ct = items[i] -> tok) != TOK_END) && (level || (ct != token));
        i++)
        {
            if ((ct == TOK_LPAREN) || (ct == TOK_LBRACKET))
                level++;
            if ((ct == TOK_RPAREN) || (ct == TOK_RBRACKET))
                level--;
        }
    return i;
}


/*================================================================
eFlag getDelim()
given a position in 'pos', finds the corresponding next token.
If the left token is ( or [, looks for the matching right paren/bracket,
Otherwise looks for the occurence of the same token. 
Returns pos pointing at the matching token, or at TOK_END if there 
is none. (In case of failed reverse search, returns -1.)
================================================================*/

eFlag Tokenizer::getDelim(int &pos, Bool reverse /*=FALSE*/)
{
    ExToken first, second, tok;
    int level = 0,
        i = pos;
    
    switch(first = items[pos] -> tok)
    {
    case TOK_LBRACKET: 
        second = TOK_RBRACKET; 
        break;
    case TOK_LPAREN: 
        second = TOK_RPAREN; 
        break;
    case TOK_RBRACKET: 
        second = TOK_LBRACKET; 
        break;
    case TOK_RPAREN: 
        second = TOK_LPAREN; 
        break;
    default: 
        second = first;
    }
    
    i += reverse? -1 : 1;
    
    while ((i >= 0) && ((tok = items[i] -> tok) != TOK_END))
    {
        if (tok == second)
        {
            if (!level)
            {
                pos = i;
                return OK;
            }
            else level--;
        }
        else if (tok == first) level++;
        i += reverse? -1 : 1;
    }
    pos = i;
    return OK;
}

/*================================================================
stripParens()
given the left and right end positions of a tokenizer fragment, 
shifts them inwards to strip any outer parentheses.
================================================================*/

void Tokenizer::stripParens(int &left, int &right)
{
    int left0 = left;
    if (items[right]->tok == TOK_END)
        right--;
//    assert(left <= right);
    while ((items[left]->tok == TOK_LPAREN)
        && (items[right]->tok == TOK_RPAREN))
    {
        left0 = left;
        getDelim(left0);
        if (left0 == right)
        {
            left++; 
            right--;
        }
        else break;
    };
}

/*****************************************************************
*                                                                *
      L o c S t e p 
*                                                                *
*****************************************************************/

LocStep::LocStep(ExAxis _axis       /*=AXIS_NONE*/, 
                 ExNodeType _ntype  /*=EXNODE_NONE*/)
: preds(LIST_SIZE_1)
{
    set(_axis, _ntype);
}

LocStep::~LocStep()
{
    preds.freeall(FALSE);
}

void LocStep::set(ExAxis _axis, ExNodeType _ntype)
{
    ax = _axis;
    ntype = _ntype;
}

void LocStep::speak(DStr &s, SpeakMode mode)
{
    if (!(mode & SM_CONTENTS)) return;
    switch(ax)
    {
    case AXIS_CHILD:
//    case AXIS_DESC_OR_SELF:
    case AXIS_ROOT:
        break;
    case AXIS_ATTRIBUTE:
        s += '@';
        break;
    default:
        {
            s += axisNames[ax];
            s += "::";
        };
    };
    if((ntype != EXNODE_NONE) //&& (ax != AXIS_DESC_OR_SELF)
        && (ax != AXIS_ROOT))
    {
        s += exNodeTypeNames[ntype];
        s += "()";
    }
    else
        ntest.speak(s,mode);
    int i, predsNumber = preds.number();
    for (i = 0; i < predsNumber; i++)
    {
        s += '[';
        preds[i] -> speak(s,mode);
        s += ']';
    };
};

eFlag LocStep::parse(Tokenizer& tokens, int& pos, Element *ownerV)
{
    int right;
    int &i = pos;
    DStr temp;
    ExToken tok;
    
    tok = tokens.items[i++]->tok;
    if (tok == TOK_END)
        Err(ET_EMPTY_PATT);
    switch (tok)
    {
    case TOK_PERIOD:
        ax = AXIS_SELF;
        ntype = EXNODE_NODE;
        return OK;
        break;
    case TOK_DPERIOD:
        ax = AXIS_PARENT;
        ntype = EXNODE_NODE;
        return OK;
        break;
    case TOK_ATSIGN:
        {
            ax = AXIS_ATTRIBUTE;
            tok = tokens.items[i++]->tok;
        };
        break;
    case TOK_STAR:
        {
            ax = AXIS_CHILD;
        };
        break;
    case TOK_AXISNAME:
        {
            tokens.items[i-1] -> speak(temp, SM_OFFICIAL);
            if ((ax = (ExAxis) lookup(temp, axisNames)) == AXIS_NONE)
                Err(ET_UNKNOWN_AXIS);
            i++;
            tok = tokens.items[i++] -> tok;
        };
        break;
    case TOK_NAME:
    case TOK_NTNAME:
        {
            ax = AXIS_CHILD;
        };
        break;
    default:
        Err(ET_EXPR_SYNTAX); // axis name or node-test expected
    };
    
    // axis has been determined; tok must now be a name or a node-test
    temp.empty();
    if ((tok != TOK_NAME) && (tok != TOK_NTNAME))
        Err(ET_EXPR_SYNTAX); // axis name or node-test expected
    tokens.items[i-1] -> speak(temp,SM_OFFICIAL);
    ntype = EXNODE_NONE;

    if (tok == TOK_NTNAME)
    {
        ntype = (ExNodeType) lookup(temp, exNodeTypeNames);
        if (tokens.items[i++]->tok != TOK_LPAREN)
            Err(ET_LPAREN_EXP);
        if (tokens.items[i++]->tok != TOK_RPAREN)
            Err(ET_RPAREN_EXP);
    }
    else
        // set the QName from prefix:uri using
        // the namespace declarations in 'ownerV'
        // (passed through from the containing attribute)
        // without using the default namespace
        E( ntest.setLogical(temp, &(NZ(ownerV) -> namespaces), FALSE) ); 

    while ((tokens.items[i] -> tok) == TOK_LBRACKET)
    {
        E( tokens.getDelim(right = i) );
        if (tokens.items[right] -> tok == TOK_END)
            Err(ET_RBRACKET_EXP)
        else 
        {
            Expression *ex = new Expression(ownerV);
            E( ex -> parse(tokens,i+1,right-1) );
            preds.append(ex);
        };
        i = right + 1;
    };

    return OK;  // pointing at the first char that does not start a predicate

};      //  end LocStep::parse()


Bool LocStep::matches(Vertex *v)
{
    // removed the following:
    // assert(v);
    // (because the parent-of-root calls etc.)
    if (!v)
        return FALSE;
    VTYPE ty = (VTYPE) ((v -> vt) & VT_BASE);
    switch(ntype)
    {
    case EXNODE_NODE:
//        if (!((ty == VT_ATTRIBUTE) || (ty == VT_ROOT)))
//            return TRUE;
        break;
    case EXNODE_TEXT:
        if (ty != VT_TEXT)
            return FALSE;
        break;
    case EXNODE_PI:
        if (ty != VT_PI) 
            return FALSE;
        break;
    case EXNODE_COMMENT:
        if (ty != VT_COMMENT)
            return FALSE;
        break;
    case EXNODE_NONE:
        if ((ty == VT_TEXT) || (ty == VT_COMMENT) || (ty == VT_ROOT))
            return FALSE;
        break;
    };

    switch(ax)
    {
    case AXIS_ROOT: 
        return (Bool) (ty == VT_ROOT); 
        break;
    case AXIS_ATTRIBUTE: 
        if (ty != VT_ATTRIBUTE) return FALSE; 
        break;
    case AXIS_CHILD: 
    case AXIS_DESCENDANT:
    case AXIS_DESC_OR_SELF:
    case AXIS_ANCESTOR:
    case AXIS_ANC_OR_SELF:
        switch (ty)
        {
        case VT_ATTRIBUTE:
        case VT_NAMESPACE:
            return FALSE;
        case VT_ROOT:
            switch(ax)
            {
            case AXIS_DESC_OR_SELF:
            case AXIS_ANCESTOR:
            case AXIS_ANC_OR_SELF:
                break;
            default:
                return FALSE;
            };
        };
        break;
    case AXIS_PARENT:
    case AXIS_SELF:
        break;
    default: assert(0); //should be handled in parse
    };

    if (ntype != EXNODE_NONE)
        return TRUE;

    QName &hisname = v -> name;
    
    //compare names; no predicates so far
    return (proc -> cmpQNames(ntest,hisname));
}

eFlag LocStep::createContextNoPreds(Context *&c, int baseIndex)
{
    int i;
    Vertex 
        *baseV = (*c)[baseIndex],
        *v;
    c = new Context;

    switch(ax)
    {
    case AXIS_ATTRIBUTE:
        if (isElement(baseV))
        {
            Element *baseE = cast(Element*, baseV);
            int attsNumber = baseE -> atts.number(); 
            for (i = 0; i < attsNumber; i++)
                if (matches(v = baseE -> atts[i]))
                    c -> append(v);
        };
        break;
    case AXIS_CHILD:
        if (isDaddy(baseV))
        {
            Daddy *baseD = cast(Daddy*, baseV);
            int contentsNumber = baseD -> contents.number();
            for (i = 0; i < contentsNumber; i++)
                if (matches(v = baseD -> contents[i]))
                   c -> append(v);
        };
        break;
    case AXIS_ROOT: // technically not an axis
        c -> append(&(proc -> input -> root));
        break;
    case AXIS_SELF:
        if (matches(baseV))
            c -> append(baseV);
        break;
    case AXIS_PARENT:
        if (matches(baseV -> parent))
            c -> append(baseV -> parent);
        break;
    case AXIS_ANCESTOR:
    case AXIS_ANC_OR_SELF:
        for (v = (ax == AXIS_ANCESTOR ? baseV -> parent : baseV); v; v = v -> parent)
            if (matches(v))
                c -> append(v);
        break;
    case AXIS_DESCENDANT:
    case AXIS_DESC_OR_SELF:
        {
            if ((ax == AXIS_DESC_OR_SELF) && matches(baseV))
                c -> append(baseV);                    
            // find next descendant
            // curr = findDescendant(curr, expr -> step); or something like that
            v = baseV;
            do
            {
                if (isDaddy(v) && cast(Daddy*,v) -> contents.number())
                    v = cast(Daddy*,v) -> contents[0];
                else
                {
                    while (v -> stamp != baseV -> stamp)
                    {
                        i = v -> ordinal;
                        v = v -> parent;
                        if (i < cast(Daddy*,v) -> contents.number()-1)
                        {
                            v = cast(Daddy*,v) -> contents[i+1];
                            break;
                        }
                    }
                };
                if (v -> stamp == baseV -> stamp)
                    break;
                else
                    if (matches(v))
                        c -> append(v);
            }
            while(TRUE);
        }; break;
    default: assert(!"axes following-sibling, preceding-sibling, following, preceding, namespace: not yet implemented"); 
    };

    // revert some axes:
    switch(ax)
    {
    case AXIS_ANCESTOR:
    case AXIS_PREC_SIBLING:
    case AXIS_PRECEDING:
    case AXIS_ANC_OR_SELF:
        {
            int n = c -> number();
            for (i = 0; i < n / 2; i++)
                c->swap(i, n-i-1);
        }
    }
    return OK;
}

/**********************************************************
N u m b e r
**********************************************************/

Number::Number()
{
    *this = 0.0;
    _NaN = FALSE;
}

Number::Number(double y)
{
    *this = y;
    _NaN = FALSE;
}

Number& 
Number::operator= (double y)
{
    x = y;
    _NaN = FALSE;
    return *this;
};

Number& 
Number::operator= (const Str &s)
{
    char *endptr;
    x = strtod((char*) s, &endptr);
    _NaN = FALSE;
    if (*endptr)
        setNaN();
    return *this;
};

Number::operator double() 
{
    return x;
}

Bool Number::operator== (double y)
{
    double d;
    return (Bool)(((d = x - y) < EPS) && (d > -EPS));
}

Bool Number::operator== (Number& y)
{
    return (Bool)(x == (double) y);
}


Bool Number::operator< (double y) 
{
    return (Bool)(x < y);
}

Bool Number::operator< (Number& y)
{
    return (Bool)(x < (double) y);
}

Bool Number::operator> (double y)
{
    return (Bool)(x > y);
}

Bool Number::operator> (Number& y)
{
    return (Bool)(x > (double) y);
}

Bool Number::isNaN()
{
#if defined(__linux__) || defined(__unix)
    return (Bool) (_NaN || isnan(x));
#elif defined(WIN32)
    return (Bool) (_NaN || _isnan(x));
#else
    return(_NaN);
#endif
}

Bool Number::isInf()
{
    return FALSE;
};

void Number::setNaN()
{
    _NaN = TRUE;
    x = -12345;
}


//________________________________________________________________


/*****************************************************************
|                                                                |
    E x p r e s s i o n
|                                                                |
*****************************************************************/

Expression::Expression(Element *_ownerV,
                       ExFunctor _functor   /* = EXF_NONE   */
                       )
:args(1)
{
    functor = _functor;
    ownerV = _ownerV;
    switch(functor)
    {
    case EXF_LOCSTEP:
        {
            step = new LocStep;
            type = EX_NODESET;
        }; break;
    case EXF_LOCPATH:
        {
            type = EX_NODESET;
        }; break;
    case EXF_STRINGSEQ:
        type = EX_STRING;
        break;
    case EXF_NONE:
        {
            type = EX_UNKNOWN;
        }; break;
    };
    hasPath = FALSE;
    isPattern = FALSE;
    pTree = NULL;
    // the following sets patomnodeset, note that e.g. patomnumber
    // is with it in a union
    patomnodeset = NULL;
}

void Expression::setLS(ExAxis _axis, ExNodeType _ntype)
{
    assert(functor == EXF_LOCPATH);
    Expression *ls = new Expression(ownerV, 
        EXF_LOCSTEP);
    args.append(ls);
    ls -> step -> set(_axis, _ntype);    
}

//
//  Expression destructor
//  
Expression::~Expression()
{
    clearContent();
}

//
//  deleteZ
//  this macro is only used in Expression::clearContent()
//  deletes a pointer, assert-checking it is non-NULL, setting it to a NULL
//
//    commenting this out because of harmless assertion faults, BUT
//    in processing Sablot-0-33.xml, there are number Expr's with 
//    NULL patomnumber - why?
// #define deleteZ( PTR ) { delete NZ( PTR ); PTR = NULL; }
//    setting it to cdelete (delete if nonzero and set to 0)
#define deleteZ( PTR )    { cdelete( PTR ); } 


//
//  clearContent()
//  called when setting the expression to a new value
//  to dispose of any existing contents.
//
void Expression::clearContent()
{
    args.freeall(FALSE);
    switch(functor)
    {
    case EXF_ATOM:
        {
            switch(type)
            {
            case EX_NODESET:
                deleteZ ( patomnodeset );
                break;
            case EX_STRING:
                deleteZ ( patomstring );
                break;
            case EX_NUMBER:
                deleteZ ( patomnumber );
                break;
            };
        }; break;
    case EXF_LOCSTEP:
        deleteZ ( step );
        break;
    case EXF_VAR:
        deleteZ ( pName );
        break;
    }
    cdelete( pTree );
};



/*================================================================
speak
    writes the expression to string 's'. Formatting is specified
    by 'mode'.
================================================================*/

void Expression::speak(DStr &s, SpeakMode mode)
{
    int i, argsNumber = args.number();
    switch(functor)
    {
    case EXF_ATOM:
        {
            s += tostring();
            return;
        }; break;
    case EXF_LOCSTEP:
        {
            step -> speak(s, mode);
        }; break;
    case EXF_LOCPATH:
        {
            for(i = 0; i < argsNumber; i++)
            {
                args[i] -> speak(s, mode);
                if (i < argsNumber-1) 
                    s += "/";
                else if ((argsNumber == 1) && 
                    (args[0] -> step -> ax == AXIS_ROOT))
                    s += "/";
            }
        }; break;
    default:
        {
            s += (DStr("\nfunctor ") + (int) functor + "\n--------ARGS:\n");
            for (i = 0; i < argsNumber; i++)
            {
                s += DStr("(") + (i+1) + ")   ";
                args[i] -> speak(s,mode);
                s += "\n";
            };
            s += "--------ARGS end\n";
        }
    }
}

/*================================================================
matches
    returns TRUE iff the current vertex of c satisfies the
    Expression's condition.
    PROBLEM:
    perhaps this should also return an error in case the expression is
    not of type nodeset?
================================================================*/

Bool Expression::matches(Context *c)
{
    assert(type == EX_NODESET);
    if (functor == EXF_LOCPATH)
        return(matchesLP(c -> current(), args.number() - 1));
    if (functor == EXFO_UNION)
    {
        int j, argsNumber = args.number();
        for (j = 0; j < argsNumber; j++)
            if (args[j] -> matches(c)) return TRUE;
    }
    return FALSE;
}

eFlag Expression::trueFor(Context *c, Bool& result)
{
    Expression ex(ownerV);
    E( eval(ex,c) );
    switch(ex.type)
    {
    case EX_NUMBER:
        result = (Bool) (ex.tonumber() == (double) (c -> position+1));
        break;
    default:
        result = ex.tobool();
    }
    return OK;
}

Bool Expression::tobool()
{
    assert(functor == EXF_ATOM);
    switch(type)
    {
    case EX_NUMBER:
        return (Bool) !(*patomnumber == 0.0 || patomnumber -> isNaN());
        break;
    case EX_STRING:
        return (Bool) !(patomstring -> isEmpty());
        break;
    case EX_BOOLEAN:
        return atombool;
        break;
    case EX_NODESET:
        return (Bool) !!(patomnodeset -> number());
        break;
    default: assert(0);
    };
    return FALSE;   //just to return something
}

Str Expression::tostring()
{
    assert(functor == EXF_ATOM);
    switch(type)
    {
    case EX_NUMBER:
        if (patomnumber -> isNaN())
            return "NaN";
        else
        {
            if (!patomnumber -> isInf())
            {
                Str s;
                s = (double)(*patomnumber);
                return s;
            }
            else if (*patomnumber > 0.0)
                return "+Infinity";
            else return "-Infinity";
        }
        break;
    case EX_STRING:
        return *patomstring;
        break;
    case EX_BOOLEAN:
        return (atombool ? (char *)"true" : (char *)"false");
        break;
    case EX_NODESET:
        if (!patomnodeset -> number())
            return "";
        else 
        {
            // !!! the following should be checked for error (wrapped in E):
            DStr s;
            patomnodeset -> current() -> value(s, patomnodeset);
            return s;
        }
        break;
    default: assert(0);
    };
    return "";
}

char *Expression::tostringCharPtr()
{
    assert((functor == EXF_ATOM) && (type == EX_STRING));
    return (char*)(*NZ(patomstring));
}

const Str& Expression::tostringRef() const
{
    assert((functor == EXF_ATOM) && (type == EX_STRING));
    return (*NZ(patomstring));
}

Number Expression::tonumber()
{
    assert(functor == EXF_ATOM);
    Number n;
    switch(type)
    {
    case EX_NUMBER:
        n = *patomnumber;
        break;
    case EX_STRING:
        n = *patomstring;
        break; 
    case EX_BOOLEAN:
        n = (atombool ? 1.0 : 0.0);
        break;
    case EX_NODESET:
        {
            // to avoid the following, tostring() must return const Str&:
            Str s = tostring();
            n = s;
        }
        break;
    default: assert(0);
    };
    return n;
}

Context& Expression::tonodeset()
{
    assert((functor == EXF_ATOM) && (type == EX_NODESET));
    return *(patomnodeset -> copy());
}

const Context& Expression::tonodesetRef()
{
    assert((functor == EXF_ATOM) && (type == EX_NODESET));
    return *patomnodeset;
}

Bool Expression::patternOK()
{
    int i,
        argsNumber = args.number();
    if ((functor != EXFO_UNION) && (functor != EXF_LOCPATH))
        return FALSE;
    switch(functor)
    {
    case EXF_LOCPATH:
        {
            for (i = 0; i < argsNumber; i++)
            {
                LocStep *ls = args[i] -> step;
                switch (ls -> ax)
                {
                case AXIS_CHILD:
                case AXIS_ATTRIBUTE:
                case AXIS_ROOT:
                    break;
                case AXIS_DESC_OR_SELF:
                    if (ls -> ntype != EXNODE_NODE)
                        return FALSE;
                    break;
                default:
                    return FALSE;
                }
            }
        }; break;
    case EXFO_UNION:
        {
            for (i=0; i < argsNumber; i++)
            {
                if (!args[i] -> patternOK())
                    return FALSE;
            }; break;
        }
    default:
        return FALSE;
    };
    return TRUE;
}

eFlag Expression::parse(const DStr &s,
                        Bool _isPattern /* = FALSE  */)
{
    isPattern = _isPattern;
    Tokenizer t;
    E( t.tokenize(s) );
    E( parse(t, 0, t.items.number() - 1) );
    if (isPattern && (!patternOK()))
        Err(ET_BAD_PATTERN);
    return OK;
}

/*================================================================
Bool isOp()
returns True if the given token is an operator, in which case
'precedence' is set to its precedence
================================================================*/

Bool Expression::isOp(ExToken token, int &precedence)
{
    Bool is = TRUE;
    switch(token)
    {
    case TOK_OR:
        precedence = 0;
        break;
    case TOK_AND:
        precedence = 1;
        break;
    case TOK_EQ:
    case TOK_NEQ:
        precedence = 2;
        break;
    case TOK_LT:
    case TOK_GT:
    case TOK_LE:
    case TOK_GE:
        precedence = 3;
        break;
    case TOK_PLUS:
    case TOK_MINUS:
        precedence = 4;
        break;
    case TOK_MULT:
    case TOK_DIV:
    case TOK_MOD:
        precedence = 5;
        break;
    case TOK_MINUS1:
        precedence = 6;
        break;
    case TOK_VERT:
        precedence = 7;
        break;
    default:
        {
            is = FALSE;
            precedence = -1;
        };
    };
    return is;
}

/*================================================================
void getFunctionInfo()
returns function code and type for the function with given name
    if no such builtin function, returns EXFF_NONE
================================================================*/

void getFunctionInfo(const Str &s, ExFunctor &code, ExType &type)
{
    char *p = (char *) s;
    int i;
    for (i = 0; funcInfoTable[i].name; i++)
    {
        if (!strcmp(funcInfoTable[i].name,p))
            break;
    };
    code = funcInfoTable[i].func;
    type = funcInfoTable[i].type;
}

struct OpItem
{
    ExFunctor fu;
    ExType ty;
    int arity;
} 
opTable[] =
{
    {EXFO_OR, EX_BOOLEAN, 3},
    {EXFO_AND, EX_BOOLEAN, 3},
    {EXFO_EQ, EX_BOOLEAN, 2},
    {EXFO_NEQ, EX_BOOLEAN, 2},
    {EXFO_LT, EX_BOOLEAN, 2},
    {EXFO_GT, EX_BOOLEAN, 2},
    {EXFO_LE, EX_BOOLEAN, 2},
    {EXFO_GE, EX_BOOLEAN, 2},
    {EXFO_PLUS, EX_NUMBER, 2},
    {EXFO_MINUS2, EX_NUMBER, 2},
    {EXFO_MULT, EX_NUMBER, 2},
    {EXFO_MOD, EX_NUMBER, 2},
    {EXFO_DIV, EX_NUMBER, 2},
    {EXFO_MINUS1, EX_NUMBER, 1},
    {EXFO_UNION, EX_NODESET, 3}
};

/*================================================================
eFlag parseLP()
================================================================*/

eFlag Expression::parseLP(Tokenizer& tokens, int &pos, 
                     Bool dropRoot /*=FALSE*/)
{
    assert(functor == EXF_LOCPATH);
    ExToken tok;
    BOOL getaway = FALSE;
    Expression *ls;
    int& i = pos;
    Bool slashPending = FALSE,
        nameWas = FALSE;
    
    tok = tokens.items[i] -> tok;
    if (tok == TOK_END)
        Err(ET_EMPTY_PATT);
    if ((tok == TOK_SLASH) || (tok== TOK_DSLASH))
    {
        if (!dropRoot)
        {
            args.append(ls = new Expression(ownerV,EXF_LOCSTEP));
            ls -> step -> set(AXIS_ROOT,EXNODE_NODE);
        }
        if (tok == TOK_SLASH)
            i++;
    }
    
    while (!getaway)
    {
        tok = tokens.items[i] -> tok;
        switch(tok)
        {
        case TOK_NAME:
        case TOK_NTNAME:
        case TOK_AXISNAME:
        case TOK_ATSIGN:
        case TOK_PERIOD:
        case TOK_DPERIOD:
            {
                args.append(ls = new Expression(ownerV,EXF_LOCSTEP));
                E( ls -> step -> parse(tokens,i,ownerV) );
                slashPending = FALSE;
                nameWas = TRUE;
            };
            break;
        case TOK_DSLASH:
            {
                args.append(ls = new Expression(ownerV,EXF_LOCSTEP));
                ls -> step -> set(AXIS_DESC_OR_SELF, EXNODE_NODE);
            };
            // no break here?
        case TOK_SLASH:
            {
                if (slashPending)
                    Err(ET_EXPR_SYNTAX);
                slashPending = TRUE;
                i++;
                if (tokens.items[i] -> tok == TOK_END) 
                    Err(ET_EMPTY_PATT);
            };
            break;
        case TOK_VERT:
        case TOK_END:
        default: getaway = TRUE;
        };
    };
    if (slashPending && nameWas)
        Err(ET_EMPTY_PATT);
    return OK;
}


/*================================================================
eFlag parseBasic()
    parses the basic expression in tokenizer 't' between positions
    'from' and 'to' inclusive. The basic expression is guaranteed
    to contain no operators (except for / and //) nor outer 
    parentheses.
================================================================*/

eFlag Expression::parseBasic(Tokenizer &t, int from, int to)
{
    Expression *e, *lp;
    // find the start of the filtering predicates
    int fstart, fright, fleft;
    ExToken tok;

    switch(t.items[from] -> tok)
    {
    case TOK_VAR:
    case TOK_LITERAL:
    case TOK_NUMBER:
        fstart = from + 1;
        break;
    case TOK_FNAME:
        {
            t.getDelim(fstart = from + 1);
            fstart++;
        };
        break;
    case TOK_LPAREN:
        {
            t.getDelim(fstart = from);
            fstart++;
        };
        break;
    default:
        fstart = -1;
    };

//#pragma Msg("adding '+1':")
    if ((fstart != -1) && (fstart <= to))
    {
        switch(t.items[fstart] -> tok)
        {
        case TOK_LBRACKET:
        case TOK_SLASH:
        case TOK_DSLASH:
            {
                // parse the filtered expression into args[0]
                e = new Expression(ownerV);
                E( e -> parse(t, from, fstart - 1));
                args.append(e);
                //
                functor = EXF_FILTER;
                type = EX_NODESET;
                // e = new Expression(ownerV);

                fleft = fstart;
                while (t.items[fleft] -> tok == TOK_LBRACKET)
                {
                    t.getDelim(fright = fleft);
                    if ((t.items[fright] -> tok == TOK_END) || (fright > to))
                        Err(ET_RBRACKET_EXP);
                    if (fleft + 1 == fright)
                        Err(ET_EXPR_SYNTAX);
                    E( (e = new Expression(ownerV)) -> parse(t,
                        fleft + 1, fright - 1) );
                    args.append(e);
                    fleft = fright + 1;
                };
                if (((tok = t.items[fleft] -> tok) == TOK_SLASH)
                    || (tok == TOK_DSLASH))
                {
                    E( (lp = new Expression(ownerV, EXF_LOCPATH)) -> parseLP(
                        t,fleft,TRUE) );
                    hasPath = TRUE;
                    args.append(lp);
                };
                if (fleft != to + 1)
                    Err(ET_EXPR_SYNTAX);
                return OK;
            };
            break;
        }
    };

    DStr temp;
    tok = t.items[from] -> tok;
    t.items[from] -> speak(temp,SM_OFFICIAL);
    if ((tok == TOK_VAR) || (tok == TOK_LITERAL)
        || (tok == TOK_NUMBER))
    {
        switch(t.items[from] -> tok)
        {
        case TOK_VAR:
            {
                functor = EXF_VAR;
                type = EX_UNKNOWN;
                E( (pName = new QName) -> setLogical(temp, 
                    &(NZ(ownerV) -> namespaces), FALSE) );
            }; break;
        case TOK_LITERAL:
            {
                functor = EXF_ATOM;
                type = EX_STRING;
                patomstring = new Str(temp);
            }; break;
        case TOK_NUMBER:
            {
                functor = EXF_ATOM;
                type = EX_NUMBER;
                *(patomnumber = new Number) = temp;
            }; break;
        };
        if (to != from)
            Err(ET_EXPR_SYNTAX);
    }
    else
    {
        if (tok == TOK_FNAME)
        {
            ExFunctor funcNo;
            ExType funcType;
            getFunctionInfo(temp,funcNo,funcType);
            if (funcNo != EXFF_NONE)
            {
                functor = funcNo;
                type = funcType;
            }
            else
            {
                functor = EXF_OTHER_FUNC;
                E( (pName = new QName) -> setLogical(temp,
                    &(NZ(ownerV) -> namespaces), FALSE) );
                type = EX_UNKNOWN;
            };
            int i = from+1,
                j;
            assert(t.items[i] -> tok == TOK_LPAREN);
            i++;
            // original loop test:
            // while (t.items[j = t.findTop(TOK_COMMA,i)] -> tok != TOK_END)
            while (((j = t.findTop(TOK_COMMA,i)) <= to) && (t.items[j] -> tok != TOK_END))
            {
                switch(t.items[j-1] -> tok)
                {   
                case TOK_COMMA:
                case TOK_LPAREN:
                    Err(ET_EXPR_SYNTAX);
                };
                args.append(e = new Expression(ownerV));
                E( e -> parse(t,i,j-1) );
                i = j+1;
            };

            if ((t.items[j = t.findTop(TOK_RPAREN,i)]->tok == TOK_END) || (j > to))
                Err(ET_RPAREN_EXP);
            if(t.items[j-1] -> tok == TOK_COMMA)
                Err(ET_EXPR_SYNTAX);
            if (j > i)  // if any args
            {
                args.append(e = new Expression(ownerV));
                E( e -> parse(t,i,j-1) );
            }
            if (to != j)
                Err(ET_EXPR_SYNTAX);
        } // end "tok == TOK_FNAME"
        else
        {   // it must be a LocPath
            type = EX_NODESET;
            functor = EXF_LOCPATH;
            int howfar = from;
            E( parseLP(t, howfar) );
            if (howfar != to + 1)
                Err(ET_EXPR_SYNTAX);
        }
    }
    return OK;
}

/*================================================================
eFlag parse()
    translates the given token list into an expression (a tree of
    'Expression' objects plus some leaves).
INPUT
    t           a tokenizer whose tokenize() method has been called
    from,to     first and last position in the token list the parsing
                applies to (i.e. a complex expression will parse the
                subexpressions with the same tokenizer but different
                limits)
================================================================*/

eFlag Expression::parse(Tokenizer& t, int from, int to)
// isOp, skipParens
{
    int i;
    ExToken 
        token, 
        mintoken = TOK_NONE;
    int precedence,
        minprec = 999,
        minndx = -1,
        leftmost,
        arity;

    if (from > to)
        Err(ET_EXPR_SYNTAX);

    t.stripParens(from,to);
    // search from right to left (left-associativity)
    for (i = to; i >= from; i--)
    {
        switch(token = t.items[i] -> tok)
        {
        case TOK_RPAREN:
        case TOK_RBRACKET:
            {
                // reverse search:
                E( t.getDelim(i,TRUE) ); // i is decremented at loop end
                if (i == -1)
                    Err(ET_LPARCKET_EXP);
            };
            break;
        default: 
            {
                if (isOp(token, precedence) && (precedence < minprec))
                {
                    minprec = precedence;
                    minndx = leftmost = i;
                    mintoken = token;
//                    if (token == TOK_OR) break;
                }
                else 
                    if (token == mintoken)
                        leftmost = i;
            };
        };
    };

    //minndx now points to the rightmost lowest-precedence operator
    // leftmost points at its leftmost occurence

    if (minndx == -1)
        E( parseBasic(t, from, to) )
    else 
    {
        int tablendx = t.items[minndx] -> tok - TOKGROUP_OPERATORS;
        functor = opTable[tablendx].fu;
        type = opTable[tablendx].ty;
        arity = opTable[tablendx].arity;
        Expression *e = new Expression(ownerV);

        args.append(e);
        switch(arity)
        {
        case 1: 
            {
            if (minndx != from)
                Err(ET_EXPR_SYNTAX)
            else
                E( e -> parse(t,from+1,to) );
            };
            break;
        case 2:
            {
                E( e -> parse(t,from,minndx - 1) );
                args.append(e = new Expression(ownerV));
                E( e -> parse(t,minndx + 1, to) );
            };
            break;
        default:
            {
                E( e -> parse(t,from,leftmost - 1) );
                int another = leftmost, 
                    lastone = leftmost;
                t.getDelim(another);
                while((another <= to) && (t.items[another]->tok != TOK_END))
                {
                    args.append(e = new Expression(ownerV));
                    E( e -> parse(t, lastone + 1, another - 1));
                    lastone = another;
                    t.getDelim(another);
                };
                args.append(e = new Expression(ownerV));
                E( e -> parse(t,lastone + 1, to) );
            };
        };
    }
    return OK;
}


void Expression::setAtom(Context *c)
{
    clearContent();
    functor = EXF_ATOM;
    type = EX_NODESET;
    patomnodeset = c;
}

void Expression::setAtom(const Number& n)
{
    clearContent();
    functor = EXF_ATOM;
    type = EX_NUMBER;
    *(patomnumber = new Number) = (Number&) n;
}

void Expression::setAtom(Bool b)
{
    clearContent();
    functor = EXF_ATOM;
    type = EX_BOOLEAN;
    atombool = b;
}

void Expression::setAtom(const DStr &s)
{
    clearContent();
    functor = EXF_ATOM;
    type = EX_STRING;
    patomstring = new Str(s);
}

/*================================================================
setFragment
    sets the expression to point to a 'result tree fragment' - a newly
    constructed tree - whose address it returns
================================================================*/

Tree *Expression::setFragment()
{
    functor = EXF_FRAGMENT;
    type = EX_NODESET;
    return pTree = new Tree("RTF",FALSE); // not an XSL tree
}

#define funcIsOperator(f) ((EXFO_OR <= f) && (f <= EXFO_Z))
#define funcIsBuiltin(f) ((EXF_FUNCTION <= f) && (f <= EXFF_NONE))

eFlag Expression::eval(Expression &retxpr, Context *c)
{
    // puts(isPattern ? "T" : "F");
    assert(!isPattern && "evaluating pattern!");
    Context *newc;
    switch(functor)
    {
    case EXF_ATOM:
        {
            //cannot use retxpr = *this !!!
            switch(type)
            {
            case EX_STRING:
                retxpr.setAtom(*patomstring);
                break;
            case EX_NUMBER:
                retxpr.setAtom(*patomnumber);
                break;
            case EX_NODESET:
                retxpr.setAtom(patomnodeset -> copy());
                break;
            case EX_BOOLEAN:
                retxpr.setAtom(atombool);
                break;
            default: assert(0);
            }
        }; break;
    case EXF_VAR:
        {
            Expression *ex = proc -> getVarBinding(*pName);
            if (!ex)
                Err(ET_VARIABLE_NOT_FOUND);
            E( ex -> eval(retxpr, c) );
        }; break;
    case EXF_LOCPATH:
    case EXFO_UNION:   
        {
            assert(c && "context is null!");
            E( createContext(newc = c) );
            // assign newc directly without copying
            retxpr.setAtom(newc -> copy()); 
            delete newc; 
        }; break;
    case EXF_OTHER_FUNC: // other function
        {
            Err1(ET_FUNC_NOT_SUPPORTED,pName -> getname());
            // would go like:
            // E( callByName(retxpr, *pName, args, c) );
        }; break;
    case EXF_FILTER:
        {
            assert(c && "context is null!");
            E( createContext(newc = c, c -> position) );
            retxpr.setAtom(newc -> copy());
            delete newc;
        }; break;
    case EXF_STRINGSEQ:
        {
            DStr result;
            Expression temp(ownerV);
            int i,
                argsNumber = args.number();
            for (i = 0; i < argsNumber; i++)
            {
                E( args[i] -> eval(temp, c) );
                result += (temp.tostring());
            };
            retxpr.setAtom(result);
        }; break;
    case EXF_FRAGMENT:
        {
            newc = new Context;
            newc -> set(&(pTree -> root));
            retxpr.setAtom(newc -> copy());
            delete newc;
        }; break;
    default: 
        {
            int i, 
                argsNumber = args.number();
            ExprList atoms(argsNumber);
            Expression *ex;
            for (i = 0; i < argsNumber; i++)
            {
                ex = new Expression(ownerV);
                E( args[i]->eval(*ex, c) );
                atoms.append(ex);
            };
            if (funcIsOperator(functor))
                    E( callOp(retxpr, atoms) )           //an operator
            else 
                if (funcIsBuiltin(functor))
                    E( callFunc(retxpr, atoms, c) )    //a core XPath function
                else
                    Err1(ET_FUNC_NOT_SUPPORTED,pName -> getname());
            atoms.freeall(FALSE);
        };
    };
    return OK;
}

template<class T>
Bool hardCompare(ExFunctor op, T b1, T b2)
{
        Str p,q;
    switch(op)
    {
    case EXFO_EQ: return (Bool) (b1 == b2); break;
    case EXFO_NEQ: return (Bool) !(b1 == b2); break;
    case EXFO_LT: return (Bool) (b1 < b2); break;
    case EXFO_GT: return (Bool) (b2 < b1); break;
    case EXFO_LE: return (Bool) ((b1 < b2) || (b1 == b2)); break;
    case EXFO_GE: return (Bool) ((b2 < b1) || (b1 == b2)); break;
    default: assert(0);
    }
    return FALSE; //just to return something
}

Bool Expression::compareCC(ExFunctor op, const Context &c1, const Context &c2)
{
    DStr s1, s2;
    Context *c1prime = ((Context&) c1).copy(),
        *c2prime = ((Context&) c2).copy();
    Bool resulting = FALSE;
    c1prime->reset();
    while (c1prime->current())
    {
        c2prime->reset();
        while(c2prime->current())
        {
            E( c1prime->current() -> value(s1, c1prime) );
            E( c2prime->current() -> value(s2, c2prime) );
            if (hardCompare(op,s1,s2))
            {
                resulting = TRUE;
                break;
            }
            c2prime->shift();
        };
        c1prime->shift();
    };
    delete c1prime;
    delete c2prime;
    return resulting;
}

Bool Expression::compareCS(ExFunctor op, const Context &c1, const DStr &s2)
{
    DStr s1;
    Bool resulting = FALSE;
    Context *c = ((Context&) c1).copy();

    c -> reset();
    while(c -> current())
    {
        E( c -> current() -> value(s1, c) );
        if (hardCompare(op, s1, s2))
        {
            resulting = TRUE;
            break;
        }
        c -> shift();
    };
    delete c;
    return resulting;
}

Bool Expression::compareCN(ExFunctor op, const Context &c1, const Number& n2)
{
    Number n1;
    DStr s1;
    Context *c = ((Context&) c1).copy();
    Bool resulting = FALSE;

    c -> reset();
    while(c -> current())
    {
        E( c -> current() -> value(s1, c) );
        n1 = s1;
        if (::hardCompare(op, n1, (Number)n2))
        {
            resulting = TRUE;
            break;
        }
        c -> shift();
    };
    delete c;
    return resulting;
}

eFlag Expression::compare(Bool &result, Expression &other, ExFunctor op)
// Both *this and other are assumed to be ATOMS.
{
    assert(functor == EXF_ATOM);
    assert(other.functor == EXF_ATOM);

    ExType histype = other.type;
    //
    if (type == EX_NODESET)
    {
        if (other.type == EX_BOOLEAN)
            result = hardCompare(op, tobool(), other.tobool());
        else
        {
            Context& mynodeset = tonodeset();
            switch(other.type)
            {
            case EX_NODESET:
                result = compareCC(op, mynodeset, other.tonodesetRef());
                break;
            case EX_STRING:
                result = compareCS(op, mynodeset, other.tostring());
                break;
            case EX_NUMBER:
                result = compareCN(op, mynodeset, other.tonumber());
                break;
            default: assert(0);
            };
            delete &mynodeset;
        }
    }
    else 
    {
        if (histype == EX_NODESET)
            E( other.compare(result, *this, op) )
        else
        {   // none of the two are nodesets
            if (type == EX_BOOLEAN || histype == EX_BOOLEAN)
                result = hardCompare(op, tobool(), other.tobool());
            else
            {
                if (type == EX_NUMBER || histype == EX_NUMBER)
                    result = hardCompare(op, tonumber(), other.tonumber());
                else
                {
                    if (type == EX_STRING || histype == EX_STRING)
                        result = hardCompare(op, tostring(), other.tostring());
                    else
                        assert(0);
                }
            }
        }; 
    }
    return OK;
}

eFlag Expression::callOp(Expression& retxpr, ExprList& atoms)
{
    int i,
        atomsNumber = atoms.number();
    switch(functor)
    {
    case EXFO_OR:
    case EXFO_AND:
        {
            assert(atomsNumber > 1);
            Bool result;
            result = atoms[0] -> tobool();
            for (i = 1; i < atomsNumber; i++)
            {
                if (functor == EXFO_OR)
                {
                    if (atoms[i] -> tobool())
                    {
                        result = TRUE;
                        break;
                    }
                }
                else    //EXFO_AND
                {
                    if (!atoms[i] -> tobool())
                    {
                        result = FALSE;
                        break;
                    }
                };
            };
            retxpr.setAtom(result);
        }; break;
    case EXFO_EQ:
    case EXFO_NEQ:
    case EXFO_LT:
    case EXFO_LE:
    case EXFO_GT:
    case EXFO_GE:
        {
            assert(atomsNumber == 2);
            Bool result;
            E( atoms[0]->compare(result,*(atoms[1]),functor) );
            retxpr.setAtom(result);
        }; break;
    case EXFO_PLUS:
    case EXFO_MINUS2:
    case EXFO_MULT:
    case EXFO_DIV:
    case EXFO_MOD:
        {
            assert(atomsNumber > 1);
            double result;
            result = (double) (atoms[0] -> tonumber());
            for (i = 1; i < atomsNumber; i++)
            {
                switch(functor)
                {
                case EXFO_PLUS:
                    result += atoms[i] -> tonumber();
                    break;
                case EXFO_MINUS2:
                    result -= atoms[i] -> tonumber();
                    break;
                case EXFO_MULT:
                    result *= atoms[i] -> tonumber();
                    break;
                case EXFO_DIV:
                    result /= atoms[i] -> tonumber();
                    break;
                case EXFO_MOD:
                    {
                        double d = atoms[i] -> tonumber();
                        result = result - d * floor(result/d);
                    };
                    break;
                };
            };
            retxpr.setAtom(Number(result));
        }; break;
    case EXFO_MINUS1:
        {
            assert(atomsNumber == 1);
            retxpr.setAtom(Number(-(double)(atoms[0] -> tonumber())));
        }; break;
    };
    return OK;
}

/*================================================================
callFunc
    calls the built-in XPath or XSLT function as determined by 
    'this -> functor'. Returns an expression in 'retxpr'.
    'atoms' is a list of atomized arguments, 'c' is the current context
    necessary for certain functions.
================================================================*/

#define checkArgsCount(x) if (atomsNumber != x)\
    Err(ET_BAD_ARGS_N);
#define checkArgsCountMax(x) if (atomsNumber > x)\
    Err(ET_BAD_ARGS_N);
#define checkArgsCountMin(x) if (atomsNumber < x)\
    Err(ET_BAD_ARGS_N);
#define checkArgsCountBetween(x,y) if ((atomsNumber < x) || \
    (atomsNumber > y)) Err(ET_BAD_ARGS_N);
#define checkIsNodeset(x) if (atoms[x] -> type != EX_NODESET)\
    Err(ET_BAD_ARG_TYPE);
#define checkIsString(x) if (atoms[x] -> type != EX_STRING)\
    Err(ET_BAD_ARG_TYPE);
#define checkIsString2(x,y) if ((atoms[x] -> type != EX_STRING) || \
    (atoms[y] -> type != EX_STRING)) Err(ET_BAD_ARG_TYPE);
#define checkIsNumber(x) if (atoms[x] -> type != EX_NUMBER)\
    Err(ET_BAD_ARG_TYPE);

/*................
firstOccurence
Finds the first complete occurence of q in p; returns the 0-based starting
position, or -1 if not found
................*/

int firstOccurence(char *p, char *q)
{
    int i = 0,
        iCurr = -1,
        j = 0;
    do
    {
        i = ++iCurr; j = 0;
        while (p[i] && q[j] && (p[i] == q[j]))
        {
            i++; j++;
        };
    }
    while (p[i] && q[j]);
    if (q[j]) 
        return -1;
    else
        return iCurr;
}

/*................
getBetween
Returns in s the portion of the source string between form and to inclusive.
If to == -1 then copies the whole rest of the string.
................*/

void getBetween(Str& s, char *source, int from, int to)
{
    assert(source);
    int len = strlen(source);
    if (from < 0) from = 0;
    if ((from >= len) || ((to != -1) && (from > to)))
        s = "";
    else
    {
        if (to == -1)
            s = source + from;
        else
            s.nset(source + from, to-from + 1);
    }
}

/*................
getCurrValue
Returns the string value of the current node
................*/

eFlag getCurrValue(Str &s, Context *c)
{
    Vertex *v;
    DStr temp;
    if (!!(v = c -> current()))
        E( v -> value(temp,c) )
    else
        s.empty();
    s = temp;
    return OK;
}


eFlag Expression::callFunc(Expression &retxpr, ExprList &atoms, Context *c)
{
    Vertex *v;
    int atomsNumber = atoms.number();
    switch(functor)
    {
    case EXFF_LAST:
        {
            checkArgsCount(0);
            retxpr.setAtom( Number(c -> number()) );
        }; break;
    case EXFF_POSITION:
        {
            checkArgsCount(0);
            retxpr.setAtom( Number(c -> position + 1) );
        }; break;
    case EXFF_COUNT:
        {
            checkArgsCount(1);
            retxpr.setAtom( 
                Number(atoms[0] -> tonodesetRef().number()) );
        }; break;
    case EXFF_LOCAL_NAME:
    case EXFF_NAMESPACE_URI:
    case EXFF_NAME:
        {
            checkArgsCountMax(1);
            DStr s;
            if (!atomsNumber)
                v = (c -> isFinished()) ? NULL : c -> current();
            else
            {
                checkIsNodeset(0);
                const Context& newc = atoms[0] -> tonodesetRef();
                v = (newc.isVoid()? NULL : newc.current());
            };
            if (v)
            {
                const QName& q = v -> getName();
                switch(functor)
                {
                case EXFF_NAME:
                    q.speak(s,SM_OFFICIAL); break;
                case EXFF_LOCAL_NAME:
                    s = q.getLocal(); break;
                case EXFF_NAMESPACE_URI:
                    s = q.getUri(); break;
                };
            };
            retxpr.setAtom(s);
        };
        break;

    case EXFF_STRING:
        {
            Str s;
            checkArgsCountMax(1);
            if (!atomsNumber)
                E( getCurrValue(s, c) )
            else
                s = atoms[0] -> tostring();
            retxpr.setAtom(s);
        }; break;

    case EXFF_CONCAT:
        {
            checkArgsCountMin(2);
            DStr s;
            for (int k = 0; k < atomsNumber; k++)
            {
                checkIsString(k);
                s += atoms[k] -> tostring();
            };
            retxpr.setAtom(s);
        }; break;

    case EXFF_STARTS_WITH:
        {
            checkArgsCount(2);
            checkIsString2(0,1);
            retxpr.setAtom((Bool) !firstOccurence(
                atoms[0] -> tostringCharPtr(),
                atoms[1] -> tostringCharPtr()));
        }; break;

    case EXFF_CONTAINS:
        {
            checkArgsCount(2);
            checkIsString2(0,1);
            retxpr.setAtom((Bool) (firstOccurence(
                atoms[0] -> tostringCharPtr(),
                atoms[1] -> tostringCharPtr()) != -1));
        }; break;

    case EXFF_SUBSTRING_BEFORE:
    case EXFF_SUBSTRING_AFTER:
        {
            Str s;
            const Str& theSmaller = atoms[1] -> tostringRef();
            char *thestring;
            checkArgsCount(2);
            checkIsString2(0,1);
            int where = firstOccurence(
                thestring = atoms[0] -> tostringCharPtr(),
                theSmaller);
            if (where == -1)
                s.empty();
            else
            {
                if (functor == EXFF_SUBSTRING_BEFORE)
                    getBetween(s, thestring, 0, where-1);
                else
                    getBetween(s, thestring, where + theSmaller.length(), -1);
            };
            retxpr.setAtom(s);
        }; break;

    case EXFF_SUBSTRING:
        {
            checkArgsCountBetween(2,3); 
            checkIsString(0); checkIsNumber(1);
            if (atomsNumber == 3)
                checkIsNumber(2);
            int 
                from = (int) (atoms[1] -> tonumber() - 1),
                to = -1;
            if (atomsNumber > 2)
            {
                to = (int) atoms[2] -> tonumber() - 1;
                if (to == -1)
                    to = -2;
            }
            Str s;
            getBetween(s, atoms[0] -> tostring(), from, to);
            retxpr.setAtom(s);
        }; break;

    case EXFF_STRING_LENGTH:
        {
            checkArgsCountBetween(0,1);
            if (atomsNumber)
            {
                checkIsString(0);
                retxpr.setAtom(Number(atoms[0] -> tostring().length()));
            }
            else
            {
                Str s;
                E( getCurrValue(s, c) );
                retxpr.setAtom(Number(s.length()));
            }
        }; break;

    case EXFF_NORMALIZE_SPACE:
        {
            checkArgsCountBetween(0,1);
            Str s;
            if (atomsNumber)
            {
                checkIsString(0);
                s = atoms[0] -> tostring();
            }
            else
                E( getCurrValue(s, c) );
            char *p = (char*) s;
            DStr stripped;
            skipWhite(p);
            while(*p)
            {
                if (isWhite(*p))
                {
                    skipWhite(p);
                    if (*p)
                        stripped += ' ';
                    p--;
                }
                else
                    stripped += *p;
                p++;
            }
            retxpr.setAtom(stripped);
        }; break;

    case EXFF_TRANSLATE:
        {
            checkArgsCount(3);
            checkIsString2(0,1);
            checkIsString(2);

            DStr resulting;
            char *q,
                *p = atoms[0] -> tostringCharPtr(),
                *src = atoms[1] -> tostringCharPtr(),
                *dest = atoms[2] -> tostringCharPtr();
            int ndx,
                destlen = strlen(dest);
            while(*p)
            {
                q = strchr(src, *p);
                if (q)
                {
                    ndx = (int)(q-src);
                    if (ndx < destlen)
                        resulting += dest[ndx];
                }
                else
                    resulting += *p;
                p++;
            };
            retxpr.setAtom(resulting);
        }; break;

	case EXFF_URL_ENCODE:
		{
			checkArgsCount(1);
			checkIsString(0);

			DStr resulting;
			char *p = atoms[0] -> tostringCharPtr();

			while(*p)
			{
				switch( *p )
				{
					case ' ':
						resulting += "%20";
						break;

					case '"':
						resulting += "%22";
						break;

					case '#':
						resulting += "%23";
						break;
					
					case '%':
						resulting += "%25";
						break;

					case '&':
						resulting += "%26";
						break;

					case '+':
						resulting += "%2B";
						break;

					case '/':
						resulting += "%2F";
						break;

					case ':':
						resulting  += "%3A";
						break;

					case ';':
						resulting += "%3B";
						break;

					case '<':
						resulting += "%3C";
						break;

					case '=':
						resulting += "%3D";
						break;

					case '>':
						resulting += "%3E";
						break;

					case '?':
						resulting += "%3F";
						break;

					case '@':
						resulting += "%40";
						break;

					case '[':
						resulting += "%5B";
						break;

					case '\\':
						resulting += "%5C";
						break;

					case ']':
						resulting += "%5D";
						break;

					case '^':
						resulting += "%5E";
						break;

					case '`':
						resulting += "%60";
						break;

					case '{':
						resulting += "%7B";
						break;

					case '|':
						resulting += "%7C";
						break;
					
					case '}':
						resulting += "%7D";
						break;

					case '~':
						resulting += "%7E";
						break;

					default:
						resulting += *p;
						break;

				}

				p++;
			}

            retxpr.setAtom(resulting);
        }; break;

    case EXFF_BOOLEAN:
        {
            checkArgsCount(1);
            retxpr.setAtom(atoms[0] -> tobool());
        }; break;

    case EXFF_NOT:
        {
            checkArgsCount(1);
            retxpr.setAtom((Bool)!(atoms[0] -> tobool()));
        }; break;

    case EXFF_TRUE:
        {
            checkArgsCount(0);
            retxpr.setAtom(TRUE);
        }; break;

    case EXFF_FALSE:
        {
            checkArgsCount(0);
            retxpr.setAtom(FALSE);
        }; break;

    case EXFF_LANG:
        {
            checkArgsCount(1);
            checkIsString(0);
            Warn(W_UNSUPP_LANG);
            retxpr.setAtom(FALSE);
        }; break;

    case EXFF_NUMBER:
        {
            checkArgsCountMax(1);
            Number n;
            if (!atomsNumber)
            {
                Str s;
                E( getCurrValue(s, c) );
                n = s;
            }
            else
                n = atoms[0] -> tonumber();
            retxpr.setAtom(n);
        }; break;

    case EXFF_SUM:
        {
            DStr s;
            Number n, sum = 0;
            checkArgsCount(1);
            checkIsNodeset(0);
            Context *newc = &(atoms[0] -> tonodeset());
            newc -> reset();
            while (!newc -> isFinished())
            {
                E( newc -> current() -> value(s, newc) );
                n = s;
                if (n.isNaN())
                {
                    sum.setNaN(); 
                    break;
                };
                sum = sum + n;
                newc -> shift();
            };
            delete newc;
            retxpr.setAtom(sum);
        }; break;

    case EXFF_FLOOR:
    case EXFF_CEILING:
    case EXFF_ROUND:
        {
            checkArgsCount(1);
            checkIsNumber(0);
            Number n = atoms[0] -> tonumber();
            switch(functor)
            {
            case EXFF_FLOOR:
                n = floor((double)n); break;
            case EXFF_CEILING:
                n = ceil((double)n); break;
            case EXFF_ROUND:
                n = floor((double)n + .5); break;
            };
            retxpr.setAtom(n);
        }; break;

    case EXFF_DOCUMENT:
        {
            checkArgsCount(1);
            DStr location;
            Tree *newtree;
            Context *newc = new Context;
            if (atoms[0] -> type == EX_NODESET)
            {
                const Context& ctxt = atoms[0] -> tonodesetRef();
                int ctxtNumber = ctxt.number();
                for (int k = 0; k < ctxtNumber; k++)
                {
                    E( ctxt[k] -> value(location, c) );
                    E( proc -> readTreeFromURI(newtree, location, 
                        // the following used to be NZ(ownerV) -> ownerT -> name
                        proc -> baseForVertex(NZ(ownerV)), FALSE) );
                    // check for duplicities and correct URI sorting!
                    newc -> append(&(newtree -> root));
                };
            }
            else
            {
                location = atoms[0] -> tostring();
                E( proc -> readTreeFromURI(newtree, location, 
                    proc -> baseForVertex(NZ(ownerV)), FALSE) );
                newc -> append(&(newtree -> root));
            }
            retxpr.setAtom(newc);
        }; break;

    case EXFF_GENERATE_ID:
        {
            DStr s;
            switch(atomsNumber)
            {
            case 0:
                v = (c -> isFinished() ? NULL : c -> current());
                break;
            case 1:
                {
                    checkIsNodeset(0);
                    const Context& newc = atoms[0] -> tonodesetRef();
                    v = (newc.isVoid()? NULL : newc.current());
                }; break;
            default:
                Err(ET_BAD_ARGS_N);
            };
            if (v)
	    {
	        s = "i__";
                s += v -> stamp;
	    }
            retxpr.setAtom(s);
        }; break;

    case EXFF_SYSTEM_PROPERTY:
        {
            checkArgsCount(1);
            checkIsString(0);
            QName q;
            q.setLogical(atoms[0] -> tostring(), &(NZ(ownerV) -> namespaces), FALSE);
            if (q.getUri().operator==(theXSLTNamespace))
            {
                if (q.getLocal() == (char*) "version")
                    retxpr.setAtom(Number(1.0));
                else if (q.getLocal() == (char*) "vendor")
                    retxpr.setAtom(Str("Ginger Alliance"));
                else if (q.getLocal() == (char*) "vendor-url")
                    retxpr.setAtom(Str("www.gingerall.com"));
                else
                    retxpr.setAtom(Str(""));
            }
            else
                retxpr.setAtom(Str(""));
        }; break;
    default:
        Err1(ET_FUNC_NOT_SUPPORTED, getFuncName(functor));
    }
    return OK;
}

eFlag Expression::createLPContext(Context *&c, int baseNdx)
{
    assert(functor == EXF_LOCPATH);
    Context *myctxt;
    if (c -> isVoid()) 
    {
        c = new Context;
        return OK;
    };
    E( args[0] -> createContext(myctxt = c, baseNdx) );
    E( createLPContextSum(1, c = myctxt) );
    delete myctxt;
    return OK;
}


eFlag Expression::createLPContextSum(int level, Context *&c)
{
    assert(functor == EXF_LOCPATH);
    if (c -> isVoid()) 
    {
        c = new Context;
        return OK;
    };

    if (level >= args.number())
    {
        c = c -> copy();
        return OK;
    }
    
    // contexts for locsteps up to 'level-1' (incl.) have been created
    Context 
        *newc = new Context, *newc2,
        *myctxt,
        *returnedc;
    int cNumber = c -> number();
    for (int j = 0; j < cNumber; j++)
    {
        E( args[level] -> createContext(myctxt = c, j) );
        E( createLPContextSum(level + 1, returnedc = myctxt) );
        delete myctxt;
        newc2 = newc -> swallow(returnedc);
        delete newc;
        newc = newc2;
        delete returnedc;
    }
    c = newc;
    return OK;
}


/*................................................................
createContext()
    creates a context for this expression, based on its functor.
................................................................*/


eFlag Expression::createContext(Context *& c, int baseNdx /* = -1 */)
{
    Context *newc;
    int i, j, 
        argsNumber = args.number();
    if (baseNdx == -1)
        baseNdx = c -> position;
    switch(functor)
    {
    case EXF_VAR:
        {
            Expression *deref = proc -> getVarBinding(*pName);
            if (!deref)
                Err(ET_VARIABLE_NOT_FOUND);
            E( deref -> createContext(c, baseNdx) );
        };
        break;
    case EXF_ATOM:
        {
            if (type != EX_NODESET)
                Err(ET_CONTEXT_FOR_BAD_EXPR);
            c = patomnodeset -> copy();
        }; break;
    case EXFO_UNION:
        {
            assert(baseNdx != -1);  // meaningful only for a locpath
            Context 
                *csummand,
                *newc2;
            assert(argsNumber);
            E( args[0] -> createContext(newc = c, baseNdx) );
            for (i = 1; i < argsNumber; i++)
            {
                E( args[i] -> createContext(csummand = c, baseNdx) );
                newc2 = newc -> swallow(csummand);
                delete newc;
                newc = newc2;
                delete csummand;
            }
            // clean up c!
            (c = newc) -> reset();
        }
        break;
    case EXF_LOCPATH:
        {
            E( createLPContext(c, baseNdx) );
        }
        break;
    case EXF_FILTER:
        {
            assert(baseNdx != -1);  // meaningful only for a locpath
            E( args[0] -> createContext(newc = c, baseNdx) );
            Context *filteredc;
            for (i = 1; i < argsNumber - (int) hasPath; i++)
            {
                filteredc = new Context;
                newc -> reset();
                Bool istrue;
                int newcNumber = newc -> number();
                for (j = 0; j < newcNumber; j++)
                {
                    E(args[i] -> trueFor(newc, istrue));
                    if (istrue)
                        filteredc -> append((*newc)[j]);
                    newc -> shift();
                };
                delete newc;
                newc = filteredc;
                if (!newc -> number()) break;
            };
            if (hasPath)
            {
                // removed: E( args[i] -> createLPContextSum(0, filteredc = newc) );
                E( args[argsNumber-1] -> createLPContextSum(0, filteredc = newc) );
                delete newc;
                newc = filteredc;
            }
            c = newc;
        }
        break;
    case EXF_LOCSTEP:
        {
            assert(step);
            assert(baseNdx != -1);  // meaningful only for a locpath
            E( step -> createContextNoPreds(newc = c, baseNdx) );
            Context *filteredc;
            int stepPredsNumber = step -> preds.number();
            for (i = 0; i < stepPredsNumber; i++)
            {
                filteredc = new Context;
                newc -> reset();
                Bool istrue;
                int newcNumber = newc -> number();
                for (j = 0; j < newcNumber; j++)
                {
                    E( step -> preds[i] -> trueFor(newc,istrue) );
                    if (istrue)
                        filteredc -> append((*newc)[j]);
                    newc -> shift();
                };
                delete newc;
                newc = filteredc;
                if (!newc -> number()) break;
            };
            c = newc;
        };
        break;
    default:
        if (funcIsBuiltin(functor))
        {
            Expression resolved(ownerV);
            E( eval(resolved, c) );
            E( resolved.createContext(c, baseNdx) );
        }
        else
        Err(ET_CONTEXT_FOR_BAD_EXPR);
    };
    return OK;
}

Bool Expression::matchesLP(Vertex *v, int lastIndex)
{
    assert(functor == EXF_LOCPATH);
    // Vertex *v = c -> current();
    int i;
    LocStep *p;
    Vertex *w = v;
    // for (i = args.number()-1; i >= 0; i--)
    for (i = lastIndex; i >= 0; i--)
    {
        if (!w) return FALSE;
        p = args[i] -> step;
        switch(p -> ax)
        {
        case AXIS_ROOT:
            if (i)
                assert(0); //root not first
            if (!p -> matches(w)) return FALSE;
            break;
        case AXIS_CHILD:
        case AXIS_ATTRIBUTE:
            {
                if (!p -> matches(w)) return FALSE;
                w = w -> parent;
            };
            break;
        case AXIS_DESC_OR_SELF:
            {
                if (!p -> matches(w)) return FALSE;
                Vertex *previous = w;
                while (previous)
                {
                    if (matchesLP(previous, i-1))
                        return TRUE;
                    else
                        previous = previous -> parent;
                };
                return FALSE;
            }
            break;
        default:
            assert(0);      // bad axis in pattern
        }
    }
    return TRUE;
}



