/*

    vregular.cc

    VUCAKO regular expressions

    5.10.2001

    Copyright (C) 2001 by Josef Pelikan, MFF UK Prague
        http://cgg.ms.mff.cuni.cz/~pepca/

    VUCAKO Bench is Open Source software (see http://www.opensource.org/
    for description what Open Source is); you may distributed it and/or
    modify it under the terms of GNU General Public License as published
    by the Free Software Foundation; either version 2, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    A copy of the GNU General Public License is available as
    `/usr/doc/copyright/GPL' in the Debian GNU/Linux distribution or on
    the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You can
    also obtain it by writing to the Free Software Foundation, Inc., 59
    Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "pch.h"

//#define TRACE
    // debug-prints

//-----------------------------------------------------------
//  VRegularItem (abstract class):

class VRegularItem {

protected:

    VRegularExpression *re;

    VRegularItem ( VRegularExpression *_re )
        { re = _re, next = NULL; minMatch = maxMatch = -1; cached = false; }

    bool cached;
        // this item will be cached?

    byte *mCache;
        // match cache pointer (static index correction is included)

public:

    VRegularItem *next;
        // linked list of VRegularItems (NULL .. end of actual scope)

    int minMatch;
        // minimum number of matching characters (to the end of list)
        // -1 if not determined yet

    int maxMatch;
        // maximum number of matching characters (to the end of list)
        // -1 if not determined yet

    virtual bool match ( int pos, int len ) =NULL;
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache ) =NULL;
        // fill "minMatch" and "maxMatch" values

    virtual void prepareCache ();
        // prepares cache pointers (mCache)

    virtual void relocateCache ( int delta );
        // relocates cache pointers

    virtual ~VRegularItem ();

    };

void VRegularItem::prepareCache ()
    // prepares cache pointers (mCache)
{
        // presumes valid values of minMatch and maxMatch
    if ( cached && minMatch <= re->searchLen ) {
        int maxReduced = re->searchLen;
        if ( maxReduced > maxMatch ) maxReduced = maxMatch;
            // valid bounds: <minMatch,maxReduced>
        int correction = (maxReduced * (re->tt1 - maxReduced)) >> 1;
            // local cache pointer (including static correction):
        mCache = re->cache + re->cacheUsed + ((correction + 3) >> 2);
            // index after the last cache item used in this VRegularItem:
        correction = ((minMatch * (minMatch - re->tt1)) >> 1) + re->searchLen - minMatch + 1;
            // end of used area:
        re->cacheUsed = mCache + ((correction + 3) >> 2) - re->cache;
        }
        // the rest of the scope:
    if ( next ) next->prepareCache();
}

void VRegularItem::relocateCache ( int delta )
    // relocates cache pointers
{
    if ( cached ) mCache += delta;
        // the rest of the scope:
    if ( next ) next->relocateCache(delta);
}

VRegularItem::~VRegularItem ()
{
    if ( next ) delete next;
}

//-----------------------------------------------------------
//  Macros for fast caching:

#ifdef VREG_STAT

#  define CACHE_TEST \
    byte *cachePtr;\
    int cacheShift;\
    if ( cached ) {\
        re->tries++;\
        cacheShift = ((len * (len - re->tt1)) >> 1) + pos;\
        cachePtr = mCache + (cacheShift >> 2);\
        cacheShift = (cacheShift & 3) << 1;\
        byte cacheItem = *cachePtr >> cacheShift;\
        if ( cacheItem & 2 ) {\
            re->hits++;\
            if ( cacheItem & 1 ) re->positive++;\
            return( (cacheItem & 1) > 0 );\
            }\
        }

#  define CACHE_SET(result) \
    if ( cached ) {\
        re->inserts++;\
        *cachePtr |= (((result) ? 3 : 2) << cacheShift);\
        }

#else

#  define CACHE_TEST \
    byte *cachePtr;\
    int cacheShift;\
    if ( cached ) {\
        cacheShift = ((len * (len - re->tt1)) >> 1) + pos;\
        cachePtr = mCache + (cacheShift >> 2);\
        cacheShift = (cacheShift & 3) << 1;\
        byte cacheItem = *cachePtr >> cacheShift;\
        if ( cacheItem & 2 )\
            return( (cacheItem & 1) > 0 );\
        }

#  define CACHE_SET(result) \
    if ( cached )\
        *cachePtr |= (((result) ? 3 : 2) << cacheShift);

#endif

//-----------------------------------------------------------
//  VRegString (simple string):

class VRegString : public VRegularItem {

protected:

    char *string;

    int length;

public:

    VRegString ( VRegularExpression *_re, const char *source, int len );
        // "source" can be escaped!

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, fills wild-cards

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    virtual ~VRegString ();

    };

VRegString::VRegString ( VRegularExpression *_re, const char *source, int len ) : VRegularItem(_re)
{
    string = new char[len+1];
    length = 0;
    do {
        switch ( *source ) {
            case '|':
            case '&':
            case '(':
            case ')':
            case '*':
            case '?':
            case '+':
            case '{':
            case '.':
            case '[':
            case '^':
                string[length] = (char)0;
                return;
            case '\\':
                source++;
                len--;
            default:
                string[length++] = *source++;
            }
        } while ( --len > 0 );
    string[length] = (char)0;
}

bool VRegString::match ( int pos, int len )
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("s");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    CACHE_TEST;                             // cache test
    bool result = true;
    if ( length )
        if ( re->ignoreCase() ) {           // ignoring letter case
            if ( STRNICMP(string,re->string+pos,length) ) result = false;
            }
        else {                              // exact string match
            if ( strncmp(string,re->string+pos,length) ) result = false;
            }
    if ( result && len != length )
        result = next->match(pos+length,len-length);
    CACHE_SET(result);                      // cache write
    return result;
}

void VRegString::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    cached = cache;
    minMatch = maxMatch = length;
    if ( next ) {
        next->fillBounds(false);
        minMatch += next->minMatch;
        maxMatch += next->maxMatch;
        }
}

VRegString::~VRegString ()
{
    if ( string ) delete []string;
}

//-----------------------------------------------------------
//  VRegOr:

#define MaxAlternative 32

class VRegOr : public VRegularItem {

protected:

    VRegularItem *arr[MaxAlternative];

    int used;

    int minMyMatch, maxMyMatch;             // match bounds for the alternative itself

public:

    VRegOr ( VRegularExpression *_re, VRegularItem *fac );

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    virtual void prepareCache ();
        // prepares cache pointers (mCache)

    virtual void relocateCache ( int delta );
        // relocates cache pointers

    void append ( VRegularItem *fac );

    virtual ~VRegOr ();

    };

VRegOr::VRegOr ( VRegularExpression *_re, VRegularItem *fac ) : VRegularItem(_re)
{
    arr[0] = fac;
    used = 1;
}

bool VRegOr::match ( int pos, int len )
    // returns "true" if any match can be found, [fills wild-cards]
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("O");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    CACHE_TEST;
    int i;
    int minTry = len - (next ? next->maxMatch : 0);
    if ( minTry < minMyMatch ) minTry = minMyMatch;
    int maxTry = len - (next ? next->minMatch : 0);
    if ( maxTry > maxMyMatch ) maxTry = maxMyMatch;
    while ( maxTry >= minTry ) {            // maxTry for us, len-maxTry for the rest
        for ( i = 0; i < used; i++ )
            if ( arr[i]->match(pos,maxTry) ) break;
        if ( i < used &&
             (len == maxTry && (!next || !next->minMatch) ||
              next && next->match(pos+maxTry,len-maxTry)) ) {
            CACHE_SET(true);
            return true;
            }
        maxTry--;
        }
    CACHE_SET(false);
    return false;
}

void VRegOr::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    cached = cache;
    if ( used ) {
        arr[0]->fillBounds(false);
        minMatch = arr[0]->minMatch;
        maxMatch = arr[0]->maxMatch;
        int i;
        for ( i = 1; i < used; i++ ) {
            arr[i]->fillBounds(false);
            if ( arr[i]->minMatch < minMatch ) minMatch = arr[i]->minMatch;
            if ( arr[i]->maxMatch > maxMatch ) maxMatch = arr[i]->maxMatch;
            }
        }
    else
        minMatch = maxMatch = 0;
    minMyMatch = minMatch;
    maxMyMatch = maxMatch;
    if ( next ) {
        next->fillBounds(minMatch<maxMatch);
        minMatch += next->minMatch;
        maxMatch += next->maxMatch;
        }
}

void VRegOr::prepareCache ()
    // prepares cache pointers (mCache)
{
    int i;
    for ( i = 0; i < used; i++ )
        arr[i]->prepareCache();
    VRegularItem::prepareCache();
}

void VRegOr::relocateCache ( int delta )
    // relocates cache pointers
{
    int i;
    for ( i = 0; i < used; i++ )
        arr[i]->relocateCache(delta);
    VRegularItem::relocateCache(delta);
}

void VRegOr::append ( VRegularItem *fac )
{
    if ( used == MaxAlternative )
        delete fac;
    else
        arr[used++] = fac;
}

VRegOr::~VRegOr ()
{
    while ( used-- )
        delete arr[used];
}

//-----------------------------------------------------------
//  VRegAnd:

class VRegAnd : public VRegOr {

public:

    VRegAnd ( VRegularExpression *_re, VRegularItem *fac );

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    };

VRegAnd::VRegAnd ( VRegularExpression *_re, VRegularItem *fac ) : VRegOr(_re,fac)
{
}

bool VRegAnd::match ( int pos, int len )
    // returns "true" if any match can be found, fills wild-cards
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("A");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    CACHE_TEST;
    int i;
    int minTry = len - (next ? next->maxMatch : 0);
    if ( minTry < minMyMatch ) minTry = minMyMatch;
    int maxTry = len - (next ? next->minMatch : 0);
    if ( maxTry > maxMyMatch ) maxTry = maxMyMatch;
    while ( maxTry >= minTry ) {            // maxTry for us, len-maxTry for the rest
        for ( i = 0; i < used; i++ )
            if ( !arr[i]->match(pos,maxTry) ) break;
        if ( i == used &&
             (len == maxTry && (!next || !next->minMatch) ||
              next && next->match(pos+maxTry,len-maxTry)) ) {
            CACHE_SET(true);
            return true;
            }
        maxTry--;
        }
    CACHE_SET(false);
    return false;
}

void VRegAnd::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    cached = cache;
    if ( used ) {
        arr[0]->fillBounds(false);
        minMatch = arr[0]->minMatch;
        maxMatch = arr[0]->maxMatch;
        int i;
        for ( i = 1; i < used; i++ ) {
            arr[i]->fillBounds(false);
            if ( arr[i]->minMatch > minMatch ) minMatch = arr[i]->minMatch;
            if ( arr[i]->maxMatch < maxMatch ) maxMatch = arr[i]->maxMatch;
            }
        }
    else
        minMatch = maxMatch = 0;
    minMyMatch = minMatch;
    maxMyMatch = maxMatch;
    if ( next && minMatch <= maxMatch ) {
        next->fillBounds(minMatch<maxMatch);
        minMatch += next->minMatch;
        maxMatch += next->maxMatch;
        }
}

//-----------------------------------------------------------
//  VRegNot:

class VRegNot : public VRegularItem {

protected:

    VRegularItem *item;

    int minAfter, maxAfter;                 // match bounds for the alternative itself

public:

    VRegNot ( VRegularExpression *_re, VRegularItem *it );

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    virtual void prepareCache ();
        // prepares cache pointers (mCache)

    virtual void relocateCache ( int delta );
        // relocates cache pointers

    virtual ~VRegNot ();

    };

VRegNot::VRegNot ( VRegularExpression *_re, VRegularItem *it ) : VRegularItem(_re)
{
    item = it;
}

bool VRegNot::match ( int pos, int len )
    // returns "true" if any match can be found, [fills wild-cards]
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("N");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    CACHE_TEST;
    int minTry = len - maxAfter;
    if ( minTry < 0 ) minTry = 0;
    int maxTry = len - minAfter;
    while ( maxTry >= minTry ) {            // maxTry for us, len-maxTry for the rest
        if ( !item->match(pos,maxTry) &&
             (len == maxTry && !minAfter ||
              next && next->match(pos+maxTry,len-maxTry)) ) {
            CACHE_SET(true);
            return true;
            }
        maxTry--;
        }
    CACHE_SET(false);
    return false;
}

void VRegNot::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    cached = cache;
    if ( item ) item->fillBounds(false);
    if ( next ) {
        next->fillBounds(true);
        minAfter = next->minMatch;
        maxAfter = next->maxMatch;
        }
    else
        minAfter = maxAfter = 0;
    minMatch = minAfter;
    maxMatch = VRegInfinity;
}

void VRegNot::prepareCache ()
    // prepares cache pointers (mCache)
{
    if ( item ) item->prepareCache();
    VRegularItem::prepareCache();
}

void VRegNot::relocateCache ( int delta )
    // relocates cache pointers
{
    if ( item ) item->relocateCache(delta);
    VRegularItem::relocateCache(delta);
}

VRegNot::~VRegNot ()
{
    if ( item ) delete item;
}

//-----------------------------------------------------------
//  VRegIteration:

class VRegIteration : public VRegularItem {

protected:

    VRegularItem *item;

    int minIter, maxIter;                   // minimum & maximum number of iterations

    int itemMin, itemMax;                   // length bounds of the iterated item

    int minMyMatch, maxMyMatch;             // match bounds for the iteration itself

    bool matchIter ( int iter, int pos, int len );
        // tries exactly "iter" iterations to match the given sub-string

public:

    VRegIteration ( VRegularExpression *_re, VRegularItem *it, const char *&ptr );

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    virtual void prepareCache ();
        // prepares cache pointers (mCache)

    virtual void relocateCache ( int delta );
        // relocates cache pointers

    virtual ~VRegIteration ();

    };

VRegIteration::VRegIteration ( VRegularExpression *_re, VRegularItem *it, const char *&ptr )
    : VRegularItem(_re)
{
    item = it;
    switch ( *ptr++ ) {
        case '*':                           // 0 - infinity
            minIter = 0;
            maxIter = VRegInfinity;
            break;
        case '?':                           // 0 - 1
            minIter = 0;
            maxIter = 1;
            break;
        case '+':                           // 1 - infinity
            minIter = 1;
            maxIter = VRegInfinity;
            break;
        case '{':                           // min - max "{min,max}"
            int skip;
            if ( sscanf(ptr,"%d,%d%n",&minIter,&maxIter,&skip) != 2 ||
                 *(ptr += skip) != '}' ) {
                    // error in {min,max} iteration description
                printf("Error in {min,max} iteration description\n");
                exit(11);
                }
            ptr++;
            if ( minIter < 0 ) minIter = 0;
            if ( maxIter < minIter ) maxIter = minIter;
            if ( !maxIter ) maxIter++;
        }
}

bool VRegIteration::matchIter ( int iter, int pos, int len )
    // tries exactly "iter" iterations to match the given sub-string
{
#ifdef VREG_STAT
    re->iterCalls++;
#endif
    if ( !iter )                            // empty iteration
        return( len == 0 );
    if ( iter == 1 )                        // simple match
        return item->match(pos,len);
    if ( len < iter * itemMin || len > iter * itemMax ) return false;
    int myMin = len - (iter - 1) * itemMax; // string-len bounds for the first iteration
    if ( myMin < itemMin ) myMin = itemMin;
    int myMax = len - (iter - 1) * itemMin;
    if ( myMax > itemMax ) myMax = itemMax;
    while ( myMax >= myMin ) {              // take exactly "myMax" from the beginning
        if ( item->match(pos,myMax) &&
             matchIter(iter-1,pos+myMax,len-myMax) )
            return true;
        myMax--;
        }
    return false;
}

bool VRegIteration::match ( int pos, int len )
    // returns "true" if any match can be found, [fills wild-cards]
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("I");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    CACHE_TEST;
    int minTry = len - (next ? next->maxMatch : 0);
    if ( minTry < minMyMatch ) minTry = minMyMatch;
    int maxTry = len - (next ? next->minMatch : 0);
    if ( maxTry > maxMyMatch ) maxTry = maxMyMatch;
    while ( maxTry >= minTry ) {            // maxTry for us, len-maxTry for the rest
        int actMin = (maxTry + itemMax - 1) / itemMax;
        if ( actMin < minIter ) actMin = minIter;
        int actMax = itemMin ? (maxTry / itemMin) : VRegInfinity;
        if ( actMax > maxIter ) actMax = maxIter;
        while ( actMin <= actMax ) {        // "actMin" iterations
            if ( matchIter(actMin,pos,maxTry) ) break;
            actMin++;
            }
        if ( actMin <= actMax &&
             (len == maxTry && (!next || !next->minMatch) ||
              next && next->match(pos+maxTry,len-maxTry)) ) {
            CACHE_SET(true);
            return true;
            }
        maxTry--;
        }
    CACHE_SET(false);
    return false;
}

void VRegIteration::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    cached = cache;
    item->fillBounds(true);
    minMatch = minMyMatch = (itemMin = item->minMatch) * minIter;
    maxMatch = maxMyMatch = (itemMax = item->maxMatch) * maxIter;
    if ( next ) {
        next->fillBounds(minMatch<maxMatch);
        minMatch += next->minMatch;
        maxMatch += next->maxMatch;
        }
}

void VRegIteration::prepareCache ()
    // prepares cache pointers (mCache)
{
    if ( item ) item->prepareCache();
    VRegularItem::prepareCache();
}

void VRegIteration::relocateCache ( int delta )
    // relocates cache pointers
{
    if ( item ) item->relocateCache(delta);
    VRegularItem::relocateCache(delta);
}

VRegIteration::~VRegIteration ()
{
    if ( item ) delete item;
}

//-----------------------------------------------------------
//  VRegWildcard:

class VRegWildcard : public VRegularItem {

protected:

    unsigned *mask;                         // character mask (or NULL for ".")

public:

    VRegWildcard ( VRegularExpression *_re, const char *&ptr );

    virtual bool match ( int pos, int len );
        // returns "true" if any match can be found, [fills wild-cards]

    virtual void fillBounds ( bool cache );
        // fill "minMatch" and "maxMatch" values

    void negate ();
        // negates the wildcard

    virtual ~VRegWildcard ();

    };

/*
  <wildcard> ::= "." |
                 "[" [ "^" ] <list> "]"
  <list> ::= [ "]" ] [ "-" ] <inner>
  <inner> ::= <innerchar> <inner> |
              <innerchar> "-" <innerchar> <inner> |
              ""
  <innerchar> ::= -- regular ASCII character excluding "]" --
*/
VRegWildcard::VRegWildcard ( VRegularExpression *_re, const char *&ptr ) : VRegularItem(_re)
{
    if ( *ptr++ == '.' ) {                  // "." - universal wildcard
        mask = NULL;
        return;
        }
    mask = new unsigned[256/32];
    memset(mask,0,256/8);
    bool neg = false;
    if ( *ptr == '^' ) {                    // global negation
        neg = true;
        ptr++;
        }
    if ( *ptr == ']' ) {                    // explicit right brace
        mask[(unsigned)']' >> 5] |= (1 << ((unsigned)']' & 0x1F));
        ptr++;
        }
    if ( *ptr == '-' ) {                    // explicit "minus"
        mask[(unsigned)'-' >> 5] |= (1 << ((unsigned)'-' & 0x1F));
        ptr++;
        }
    while ( *ptr && *ptr != ']' ) {         // one character or range
        unsigned first = 0xFF & (unsigned)*ptr++;
        if ( *ptr == '-' && ptr[1] && ptr[1] != ']' ) { // range
            unsigned second = 0xFF & (unsigned)*++ptr;
            ptr++;
            while ( first <= second ) {
                mask[first >> 5] |= (1 << (first & 0x1F));
                first++;
                }
            }
        else                                // regular character
            mask[first >> 5] |= (1 << (first & 0x1F));
        }
    if ( *ptr++ != ']' ) {
            // error: wild-card was not closed properly
        printf("Error:  wild-card was not closed properly\n");
        exit(11);
        }
    if ( neg ) negate();
}

bool VRegWildcard::match ( int pos, int len )
    // returns "true" if any match can be found, [fills wild-cards]
{
#ifdef VREG_STAT
    re->calls++;
#endif
#ifdef TRACE
    printf("w");
#endif
        // trivial bound test:
    if ( len < minMatch || len > maxMatch ) return false;
    if ( mask )
        if ( re->ignoreCase() ) {           // ignoring letter case
            unsigned ch = 0xFF & (unsigned)toupper(re->string[pos]);
            if ( !(mask[ch >> 5] & (1 << (ch & 0x1F))) ) {
                unsigned cl = 0xFF & (unsigned)tolower(re->string[pos]);
                if ( cl == ch ||
                     !(mask[cl >> 5] & (1 << (cl & 0x1F))) ) return false;
                }
            }
        else {                              // exact character match
            unsigned ch = 0xFF & (unsigned)re->string[pos];
            if ( !(mask[ch >> 5] & (1 << (ch & 0x1F))) ) return false;
            }
    if ( len == 1 ) return true;
    return next->match(pos+1,len-1);
}

void VRegWildcard::fillBounds ( bool cache )
    // fill "minMatch" and "maxMatch" values
{
    if ( minMatch >= 0 ) return;
    minMatch = maxMatch = 1;
    if ( next ) {
        next->fillBounds(cache);
        minMatch += next->minMatch;
        maxMatch += next->maxMatch;
        }
}

void VRegWildcard::negate ()
    // negates the wildcard
{
    if ( mask ) {
        int i;
        for ( i = 0; i < 256/32; )
            mask[i++] ^= 0xFFFFFFFF;
        }
    else {
        mask = new unsigned[256/32];
        memset(mask,0,256/8);
        }
}

VRegWildcard::~VRegWildcard ()
{
    if ( mask ) delete []mask;
}

//-----------------------------------------------------------
//  VRegularExpression:

VRegularExpression::VRegularExpression ( const char *expr, bool ic )
{
    iCase = ic;
    ex = expr;
    it = regexp();
    ex = expr;
#ifdef VREG_STAT
    calls = iterCalls = inserts = tries = hits = positive = 0;
#endif
    searchLen = 0;
    cache = NULL;
    cacheAlloc = 0;
}

bool VRegularExpression::match ( const char *str )
    // true/false string matching
{
    if ( (it == NULL) != (str == NULL) ) return false;
    if ( !str ) return true;
    it->fillBounds(false);                  // set minMatch, maxMatch
        // [re-]initialize the match cache:
    int newLen = strlen(str);
    if ( newLen != searchLen ) {            // cache pointers need to be recomputed
        cacheUsed = 0;
        searchLen = newLen;
        tt1 = searchLen + searchLen + 1;
        it->prepareCache();
        if ( cacheAlloc < cacheUsed ) {     // cache array has to be extended
            if ( cache ) delete []cache;
            byte *newCache = (cacheAlloc = cacheUsed) ? new byte[cacheUsed] : NULL;
            int delta = newCache - cache;
            cache = newCache;
            if ( delta ) it->relocateCache(delta);
            }
        }
    if ( cacheUsed ) memset(cache,0,cacheUsed);
    string = str;
    return it->match(0,searchLen);
}

#ifdef VREG_STAT

void VRegularExpression::sync ()
{
}

#endif

void VRegularExpression::wildCard ( int i, int &begin, int &end )
    // returns wild-card range from the last string match
{
    begin = end = 0;
}

VRegularExpression::~VRegularExpression ()
{
    if ( it ) delete it;
    if ( cache ) delete []cache;
}

//-----------------------------------------------------------
//  regular expression parsing:

/*
  <regexp> ::= <factor> [ "|" <factor> ]* |
               ""
*/
VRegularItem *VRegularExpression::regexp ()
{
    VRegularItem *fac = factor();
    if ( !fac ) return NULL;
    if ( *ex != '|' ) return fac;
    VRegOr *alt = new VRegOr(this,fac);
    do {
        ex++;
        fac = factor();
        if ( !fac ) return alt;
        alt->append(fac);
        } while ( *ex == '|' );
    return alt;
}

/*
  <factor> ::= <term> [ "&" <term> ]*
*/
VRegularItem *VRegularExpression::factor ()
{
    VRegularItem *t = term();
    if ( !t ) return NULL;
    if ( *ex != '&' ) return t;
    VRegAnd *con = new VRegAnd(this,t);
    do {
        t = term();
        if ( !t ) return con;
        con->append(t);
        } while ( *ex == '&' );
    return con;
}

#define NoIteration(c)   ((c) != '*' && (c) != '?' && (c) != '+' && (c) != '{')

/*
  <term> ::= <term> <term> |
             <atom> "*" |
             <atom> "?" |
             <atom> "+" |
             <atom> "{" [ <number> ] "," [ <number> ] "}" |
             [ "^" ] <atom>
  <atom> ::= "(" <regexp> ")" |
             <wildcard> | <escape> | <char>
*/
VRegularItem *VRegularExpression::term ()
{
    VRegularItem *result = NULL;            // result as linked-list
    VRegularItem *last = NULL;              // last item of linked-list
    VRegularItem *iter = NULL;              // atom to be iterated
        // collect regular characters into a VRegString:
    const char *bEx = ex;
    const char *lEx = ex - 1;
    const char *eEx = ex;
    bool wasNot = false;
    do                                      // skip over one regular character
        switch ( *eEx ) {

                // backslash: regular (escaped) character or end of input string
            case '\\':
                lEx = eEx++;
                if ( *eEx ) {
                    eEx++;
                    if ( wasNot ) {         // negation
                        iter = new VRegString(this,bEx,eEx-bEx);
                        (last ? last->next : result) = iter = new VRegNot(this,iter);
                        last = iter;
                        if ( NoIteration(*eEx) ) iter = NULL;
                        lEx = (bEx = eEx) - 1;
                        wasNot = false;
                        }
                    break;
                    }

                // end of regular string:
            case '|':
            case '&':
            case '(':
            case ')':
            case '^':
            case '\0':
                    // eEc points after the regular string
                if ( eEx > bEx ) {          // deferred string
                    iter = new VRegString(this,bEx,eEx-bEx);
                    (last ? last->next : result) = iter;
                    last = iter;
                    iter = NULL;
                    }
                if ( *eEx == '^' ) {        // following atom will be negated
                    wasNot = !wasNot;
                    lEx = eEx++;
                    if ( *(bEx = eEx) != '(' ) break;
                    }
                if ( *eEx != '(' ) {        // end of this term
                    ex = eEx;
                    return result;
                    }
                    // start of a sub-expression "( <regexp> )":
                ex = eEx + 1;
                iter = regexp();
                if ( *ex != ')' ) {
                        // error: ')' expected
                    printf("Error: ')' expected\n");
                    exit(11);
                    }
                lEx = ex++;
                bEx = eEx = ex;
                if ( wasNot ) {
                    iter = new VRegNot(this,iter);
                    wasNot = false;
                    }
                if ( NoIteration(*ex) ) {   // simple sub-expression
                    (last ? last->next : result) = iter;
                    last = iter;
                    iter = NULL;
                    break;
                    }

                // previous item has to be iterated:
            case '*':                       // 0 - infinity
            case '?':                       // 0 - 1
            case '+':                       // 1 - infinity
            case '{':                       // min - max "{min,max}"
                if ( !iter ) {              // iterate the preceding character
                    if ( lEx < bEx ) {
                            // error: I have nothing to iterate
                        printf("Error: nothing to iterate\n");
                        exit(11);
                        }
                    if ( lEx > bEx ) {      // deferred string
                        iter = new VRegString(this,bEx,lEx-bEx);
                        (last ? last->next : result) = iter;
                        last = iter;
                        }
                        // iterate the [lEx,eEx] character:
                    iter = new VRegString(this,lEx,eEx-lEx);
                    }
                if ( !iter ) {
                        // error: error in iteration
                    printf("Error: error in iteration\n");
                    exit(11);
                    }
                    // do the iteration job:
                if ( wasNot ) {
                    iter = new VRegNot(this,iter);
                    wasNot = false;
                    }
                iter = new VRegIteration(this,iter,eEx);
                (last ? last->next : result) = iter;
                last = iter;
                iter = NULL;
                lEx = (bEx = eEx) - 1;
                break;

                // wildcard starting symbol:
            case '.':
            case '[':
                if ( eEx > bEx ) {          // deferred string
                    iter = new VRegString(this,bEx,eEx-bEx);
                    (last ? last->next : result) = iter;
                    last = iter;
                    iter = NULL;
                    }
                iter = new VRegWildcard(this,eEx);
                lEx = (bEx = eEx) - 1;
                if ( wasNot ) {
                    ((VRegWildcard*)iter)->negate();
                    wasNot = false;
                    }
                if ( NoIteration(*eEx) ) {  // simple sub-expression
                    (last ? last->next : result) = iter;
                    last = iter;
                    iter = NULL;
                    }
                break;

                // regular characters:
            default:
                lEx = eEx++;
                if ( wasNot ) {             // negation
                    iter = new VRegString(this,bEx,eEx-bEx);
                    (last ? last->next : result) = iter = new VRegNot(this,iter);
                    last = iter;
                    if ( NoIteration(*eEx) ) iter = NULL;
                    lEx = (bEx = eEx) - 1;
                    wasNot = false;
                    }
            }
    while ( true );
}
