%{

class CValueParser;

#if 0

%}

%union
{
    WCHAR * pwszChar;
    DBCOMMANDOP dbop;
    CDbRestriction * pRest;
    CStorageVariant * pStorageVar;
    CValueParser  *pPropValueParser;
    int iInt;
    int iEmpty;
}

%left  _OR
%left  _AND _NEAR _NEARDIST
%left  _NOT
%left  '(' ')'


/***
 *** Tokens (used also by flex via parser.h)
 ***/

/***
 *** reserved_words
 ***/

%token _CONTAINS
%token _AND
%token _OR
%token _NOT
%token _NEAR
%token _LT
%token _GT
%token _LTE
%token _GTE
%token _EQ
%token _NE
%token _ALLOF
%token _SOMEOF
%token _OPEN
%token _CLOSE
%token _VECTOR_END
%token _VE
%token _VE_END
%token _PROPEND
%token _NEAR_END

%token _LTSOME
%token _GTSOME
%token _LTESOME
%token _GTESOME
%token _EQSOME
%token _NESOME
%token _ALLOFSOME
%token _SOMEOFSOME

%token _LTALL
%token _GTALL
%token _LTEALL
%token _GTEALL
%token _EQALL
%token _NEALL
%token _ALLOFALL
%token _SOMEOFALL
%token _COERCE

%token _SHGENPREFIX
%token _SHGENINFLECT
%token _GENPREFIX
%token _GENINFLECT
%token _GENNORMAL

/***
 *** Terminal tokens
 ***/
%token <pwszChar>   _PHRASE
%token <pwszChar>   _PROPNAME
%token <pwszChar>   _NEARDIST
%token <pwszChar>   _NEARUNIT
%token <pwszChar>   _WEIGHT
%token <pwszChar>   _REGEX
%token <pwszChar>   _FREETEXT
%token <pwszChar>   _VECTORELEMENT
%token <pwszChar>   _VEMETHOD
%token <pwszChar>   _PHRASEORREGEX

/***
 *** Nonterminal tokens
 ***/

%type <pStorageVar>    Value
%type <pStorageVar> VectorValue
%type <pPropValueParser> SingletVector
%type <pPropValueParser> EmptyVector
%type <pPropValueParser> MultiVector
%type <dbop>           Op
%type <dbop>           Matches
%type <iEmpty>         Property  // placeholder.  value is empty (info saved to state)
%type <iEmpty>         Contains  // placeholder.  value is empty
%type <iEmpty>         Open      // placeholder.  value is empty
%type <iEmpty>         Close     // placeholder.  value is empty
%type <iInt>           Gen
%type <iEmpty>         GenEnd    // placeholder.  value is empty (info saved to state)
%type <iEmpty>         PropEnd   // placeholder.  value is empty (info saved to state)
%type <iInt>           ShortGen


%type <pRest>          Term
%type <pRest>          PropTerm
%type <pRest>          NestTerm
%type <pRest>          CoerceTerm
%type <pRest>          NearTerm
%type <pRest>          AndTerm
%type <pRest>          VectorTerm
%type <pRest>          VectorSpec
%type <pRest>          query

%{

#endif

#define YYDEBUG CIDBG

#include <malloc.h>
#include "yybase.hxx"
#include "parser.h" // defines yystype
#include "parsepl.h"
#include "flexcpp.h"

#if CIDBG == 1
#define AssertReq(x)    Assert(x != NULL)
#else
#define AssertReq(x)
#endif

const GUID guidSystem = PSGUID_STORAGE;
static CDbColId psContents( guidSystem, PID_STG_CONTENTS );

//+---------------------------------------------------------------------------
//
//  Function:  TreeFromText, public
//
//  Synopsis:  Create a CDbRestriction from a restriction string
//
//  Arguments: [wcsRestriction] -- restriction
//             [ColumnMapper]   -- property list
//             [lcid]           -- locale id of the query
//
//  History:   01-Oct-97 emilyb    created
//             26-Aug-98 KLam      No longer need to lower case
//
//----------------------------------------------------------------------------


CDbContentRestriction * TreeFromText(
    WCHAR const *   wcsRestriction,
    IColumnMapper & ColumnMapper,
    LCID            lcid )
{
    unsigned cwc = 1 + wcslen( wcsRestriction );
    XGrowable<WCHAR> xRestriction( cwc );
    WCHAR * pwc = xRestriction.Get();
    RtlCopyMemory( pwc, wcsRestriction, cwc * sizeof WCHAR );

    cwc--;

    // The parser can't deal with trailing space so strip it off

    while ( cwc > 0 && L' ' == pwc[cwc-1] )
        cwc--;
    pwc[cwc] = 0;

    TripLexer Lexer;
    XPtr<YYPARSER> xParser( new TripParser( ColumnMapper, lcid, Lexer ) );

    xParser->yyprimebuffer( pwc );

    #if 0 // YYDEBUG == 1
        // Set this to 1 if you want command line output. to 0 otherwise.
        xParser->SetDebug();
    #endif

    // Actually parse the text producing a tree

    SCODE hr = xParser->Parse();

    if (FAILED(hr))
        THROW( CException( hr ) );

    // return the DBCOMMANDTREE
    return  (CDbContentRestriction *)( xParser->GetParseTree() );

} //TextFromTree

void StripQuotes(WCHAR *wcsPhrase)
{
    ULONG cChars = wcslen(wcsPhrase);
    LPWSTR pLast = wcsPhrase + cChars - 1;
    if (L'"' == *wcsPhrase && L'"' == *pLast)
    {
        *pLast = L'\0';
        MoveMemory(wcsPhrase, wcsPhrase+1, sizeof(WCHAR) * (cChars-1) );
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CValueParser::CValueParser, public
//
//  Synopsis:  Allocs CStorageVariant of correct type
//
//  History:   01-Oct-97 emilyb    created
//             02-Sep-98 KLam      Added locale
//
//----------------------------------------------------------------------------

CValueParser::CValueParser(
    BOOL fVectorElement,
    DBTYPE PropType,
    LCID locale ) :
        _pStgVariant( 0 ),
        _fVector(fVectorElement),
        _PropType (PropType),
        _cElements ( 0 ),
        _locale ( locale )
{

    if ( _fVector )
    {
        // this is a vector
        if ( DBTYPE_VECTOR != ( _PropType & DBTYPE_VECTOR ) )
            THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );

        VARENUM ve = (VARENUM ) _PropType;
        if ( _PropType == ( DBTYPE_VECTOR | DBTYPE_WSTR ) )
            ve = (VARENUM) (VT_VECTOR | VT_LPWSTR);
        else if ( _PropType == ( DBTYPE_VECTOR | DBTYPE_STR ) )
            ve = (VARENUM) (VT_VECTOR | VT_LPSTR);

        _pStgVariant.Set( new CStorageVariant( ve, _cElements ) );
    }
    else
    {
        _pStgVariant.Set( new CStorageVariant() );
    }
    if ( _pStgVariant.IsNull() )
        THROW( CException( E_OUTOFMEMORY ) );
}

//+---------------------------------------------------------------------------
//
//  Member:    CValueParser::AddValue, public
//
//  Synopsis:  Adds value to CStorageVariant
//
//  Arguments: [pwszValue] -- value
//
//  History:   01-Oct-97 emilyb    code moved here from CPropertyValueParser
//
//----------------------------------------------------------------------------

void CValueParser::AddValue(WCHAR const * pwszValue)
{
    if ( _pStgVariant.IsNull() )
        THROW( CException( E_OUTOFMEMORY ) );

    switch ( _PropType & ~DBTYPE_VECTOR  )
    {

    case DBTYPE_WSTR :
    case DBTYPE_WSTR | DBTYPE_BYREF :
        {
            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetLPWSTR( pwszValue, _cElements );
            else
                _pStgVariant->SetLPWSTR( pwszValue );
            break;
        }
    case DBTYPE_BSTR :
        {
            BSTR bstr = SysAllocString( pwszValue );

            if ( 0 == bstr )
                THROW( CException( E_OUTOFMEMORY ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetBSTR( bstr, _cElements );
            else
                _pStgVariant->SetBSTR( bstr );

            SysFreeString( bstr );
            break;
        }
    case DBTYPE_STR :
    case DBTYPE_STR | DBTYPE_BYREF :
        {
            // make sure there's enough room to translate

            unsigned cbBuffer = 1 + 3 * wcslen( pwszValue );
            XArray<char> xBuf( cbBuffer );

            int cc = WideCharToMultiByte( CP_ACP,
                                          0,
                                          pwszValue,
                                          -1,
                                          xBuf.Get(),
                                          cbBuffer,
                                          NULL,
                                          NULL );

            if ( 0 == cc )
            {
                #if CIDBG
                ULONG ul = GetLastError();
                #endif
                THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
            }

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetLPSTR( xBuf.Get(), _cElements );
            else
                _pStgVariant->SetLPSTR( xBuf.Get() );
            break;
        }

    case DBTYPE_I1 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            LONG l = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( ( l > SCHAR_MAX ) ||
                 ( l < SCHAR_MIN ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetI1( (CHAR) l, _cElements );
            else
                _pStgVariant->SetI1( (CHAR) l );

            break;
        }
    case DBTYPE_UI1 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            ULONG ul = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( ul > UCHAR_MAX )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetUI1( (BYTE) ul, _cElements );
            else
                _pStgVariant->SetUI1( (BYTE) ul );

            break;
        }
    case DBTYPE_I2 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            LONG l = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( ( l > SHRT_MAX ) ||
                 ( l < SHRT_MIN ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetI2( (short) l, _cElements );
            else
                _pStgVariant->SetI2( (short) l );

            break;
        }
    case DBTYPE_UI2 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            ULONG ul = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( ul > USHRT_MAX )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetUI2( (USHORT) ul, _cElements );
            else
                _pStgVariant->SetUI2( (USHORT) ul );
            break;
        }
    case DBTYPE_I4 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            LONG l = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetI4( l, _cElements );
            else
                _pStgVariant->SetI4( l );
            break;
        }
    case DBTYPE_UI4 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            ULONG ul = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetUI4( ul, _cElements );
            else
                _pStgVariant->SetUI4( ul );
            break;
        }
    case DBTYPE_ERROR :
        {
            // SCODE/HRESULT are typedefed as long (signed)

            CQueryScanner scan( pwszValue, FALSE, _locale );
            SCODE sc = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( sc, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetERROR( sc, _cElements );
            else
                _pStgVariant->SetERROR( sc );
            break;
        }
    case DBTYPE_I8 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            _int64 ll = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( ll, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            LARGE_INTEGER LargeInt;
            LargeInt.QuadPart = ll;

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetI8(  LargeInt , _cElements );
            else
                _pStgVariant->SetI8(  LargeInt  );

            break;
        }
    case DBTYPE_UI8 :
        {
            CQueryScanner scan( pwszValue, FALSE, _locale );
            unsigned _int64 ull = 0;
            BOOL fAtEndOfString;
            if ( ! ( scan.GetNumber( ull, fAtEndOfString ) &&
                     fAtEndOfString ) )
                THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );

            ULARGE_INTEGER LargeInt;
            LargeInt.QuadPart = ull;

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetUI8(  LargeInt , _cElements );
            else
                _pStgVariant->SetUI8(  LargeInt  );

            break;
        }
    case DBTYPE_BOOL :
        {
            if ( pwszValue[0] == 'T' ||
                 pwszValue[0] == 't' )
                if ( _PropType & DBTYPE_VECTOR )
                    _pStgVariant->SetBOOL( VARIANT_TRUE, _cElements );
                else
                    _pStgVariant->SetBOOL( VARIANT_TRUE );
            else
                if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetBOOL( VARIANT_FALSE, _cElements );
            else
                _pStgVariant->SetBOOL( VARIANT_FALSE );

            break;
        }
    case DBTYPE_R4 :
        {
            WCHAR *pwcEnd = 0;

            float Float = (float)( wcstod( pwszValue, &pwcEnd ) );

            if ( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
                THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetR4( Float, _cElements );
            else
                _pStgVariant->SetR4( Float );

            break;
        }
    case DBTYPE_R8 :
        {
            WCHAR *pwcEnd = 0;
            double Double = ( double )( wcstod( pwszValue, &pwcEnd ) );

            if ( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
                THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetR8( Double, _cElements );
            else
                _pStgVariant->SetR8( Double );

            break;
        }
    case DBTYPE_DECIMAL :
        {
            WCHAR *pwcEnd = 0;
            double Double = ( double )( wcstod( pwszValue, &pwcEnd ) );
     
            if( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
                THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );
     
            // Vectors are not supported by OLE for VT_DECIMAL (yet)
     
            Win4Assert( 0 == ( _PropType & DBTYPE_VECTOR ) );
     
            PROPVARIANT * pPropVar = (PROPVARIANT *) _pStgVariant.GetPointer();
            VarDecFromR8( Double, &(pPropVar->decVal) );
            pPropVar->vt = VT_DECIMAL;
            break;
        }
    case DBTYPE_DATE :
        {
            FILETIME ftValue;
            ParseDateTime( pwszValue, ftValue );

            SYSTEMTIME stValue;
            BOOL fOK = FileTimeToSystemTime( &ftValue, &stValue );

            if ( !fOK )
                THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );

            DATE dosDate;
            fOK = SystemTimeToVariantTime( &stValue, &dosDate );

            if ( !fOK )
                THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetDATE( dosDate, _cElements );
            else
                _pStgVariant->SetDATE( dosDate );

            break;
        }
    case VT_FILETIME :
        {
            FILETIME ftValue;
            ParseDateTime( pwszValue, ftValue );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetFILETIME( ftValue, _cElements );
            else
                _pStgVariant->SetFILETIME( ftValue );

            break;
        }
    case DBTYPE_CY :
        {
            double dbl;

            if ( swscanf( pwszValue,
                          L"%lf",
                          &dbl ) < 1 )
                THROW( CParserException( QPARSE_E_EXPECTING_CURRENCY ) );

            CY cyCurrency;
            VarCyFromR8( dbl, &cyCurrency );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetCY( cyCurrency,  _cElements );
            else
                _pStgVariant->SetCY( cyCurrency );

            break;
        }
    case DBTYPE_GUID :
    case DBTYPE_GUID | DBTYPE_BYREF:
        {
            CLSID clsid;

            if ( swscanf( pwszValue,
                          L"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                          &clsid.Data1,
                          &clsid.Data2,
                          &clsid.Data3,
                          &clsid.Data4[0], &clsid.Data4[1],
                          &clsid.Data4[2], &clsid.Data4[3],
                          &clsid.Data4[4], &clsid.Data4[5],
                          &clsid.Data4[6], &clsid.Data4[7] ) < 11 )
                THROW( CParserException( QPARSE_E_EXPECTING_GUID ) );

            if ( _PropType & DBTYPE_VECTOR )
                _pStgVariant->SetCLSID( clsid, _cElements );
            else
                _pStgVariant->SetCLSID( &clsid );
            break;
        }
    default:
        {
            THROW( CParserException( QPARSE_E_UNSUPPORTED_PROPERTY_TYPE ) );
        }
    } // switch

    // make sure memory allocations succeeded

    if ( !_pStgVariant->IsValid() )
        THROW( CException( E_OUTOFMEMORY ) );

    if ( _fVector )
    {
        _cElements++;
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CValueParser::ParseDateTime, private
//
//  Synopsis:   Attempts to parse a date expression.
//
//  Arguments:  phrase -- pointer to the phrase to parse
//              ft     -- reference to the FILETIME structure to fill in
//                        with the result
//
//  History:    31-May-96   dlee       Created
//              23-Jan-97   KyleP      Better Year 2000 support
//              02-Sep-98   KLam       Use user settings for Y2K support
//
//----------------------------------------------------------------------------

void CValueParser::ParseDateTime(
    WCHAR const *    phrase,
    FILETIME & ft )
{
    if( !CheckForRelativeDate( phrase, ft ) )
    {
        SYSTEMTIME stValue = { 0, 0, 0, 0, 0, 0, 0, 0 };

        int cItems = swscanf( phrase,
                              L"%4hd/%2hd/%2hd %2hd:%2hd:%2hd:%3hd",
                              &stValue.wYear,
                              &stValue.wMonth,
                              &stValue.wDay,
                              &stValue.wHour,
                              &stValue.wMinute,
                              &stValue.wSecond,
                              &stValue.wMilliseconds );

        if ( 1 == cItems )
            cItems = swscanf( phrase,
                              L"%4hd-%2hd-%2hd %2hd:%2hd:%2hd:%3hd",
                              &stValue.wYear,
                              &stValue.wMonth,
                              &stValue.wDay,
                              &stValue.wHour,
                              &stValue.wMinute,
                              &stValue.wSecond,
                              &stValue.wMilliseconds );

        if( cItems != 3 && cItems != 6 && cItems != 7)
            THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );

        //
        // Make a sensible split for Year 2000 using the user's system settings
        //

        if ( stValue.wYear < 100 )
        {
            DWORD dwYearHigh = 0;
            if ( 0 == GetCalendarInfo ( _locale,
                                        CAL_GREGORIAN,
                                        CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
                                        0,
                                        0,
                                        &dwYearHigh ) )
            {
                THROW ( CException () );
            }

            if ( ( dwYearHigh < 99 ) || ( dwYearHigh > 9999 ) )
                dwYearHigh = 2029;

            WORD wMaxDecade = (WORD) dwYearHigh % 100;
            WORD wMaxCentury = (WORD) dwYearHigh - wMaxDecade;
            if ( stValue.wYear <= wMaxDecade )
                stValue.wYear += wMaxCentury;
            else
                stValue.wYear += ( wMaxCentury - 100 );
        }

        if( !SystemTimeToFileTime( &stValue, &ft ) )
            THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
    }
} //ParseDateTime

//+---------------------------------------------------------------------------
//
//  Member:     CValueParser::CheckForRelativeDate, private
//
//  Synopsis:   Attempts to parse a relative date expression.  If successful,
//              it fills in the FILETIME structure with the calculated
//              absolute date.
//
//  Notes:      Returns TRUE if the phrase is recognized as a relative
//              date (i.e., it begins with a '-').  Otherwise, returns FALSE.
//              The format of a relative date is
//              "-"{INTEGER("h"|"n"|"s"|"y"|"q"|"m"|"d"|"w")}*
//              Case is not significant.
//
//  Arguments:  phrase -- pointer to the phrase to parse
//              ft -- reference to the FILETIME structure to fill in
//                      with the result
//
//  History:    26-May-94   t-jeffc     Created
//              02-Mar-95   t-colinb    Moved from CQueryParser to
//                                      be more accessible
//              22-Jan-97   KyleP       Fix local/UTC discrepancy
//
//----------------------------------------------------------------------------

BOOL CValueParser::CheckForRelativeDate(
    WCHAR const *    phrase,
    FILETIME & ft )
{
    if( *phrase++ == L'-' )
    {
        SYSTEMTIME st;
        LARGE_INTEGER liLocal;
        LONGLONG llTicksInADay = ((LONGLONG)10000000) * ((LONGLONG)3600)
                                 * ((LONGLONG) 24);
        LONGLONG llTicksInAHour = ((LONGLONG) 10000000) * ((LONGLONG)3600);
        int iMonthDays[12]  = {1,-1,1,0,1,0,1,1,0,1,0,1};
        int iLoopValue, iPrevMonth, iPrevQuarter, iQuarterOffset;
        WORD wYear, wDayOfMonth, wStartDate;

        //
        //Obtain local time and convert it to file time
        //Copy the filetime to largeint data struct
        //

        GetSystemTime(&st);
        if(!SystemTimeToFileTime(&st, &ft))
            THROW( CParserException( QPARSE_E_INVALID_LITERAL ));
        liLocal.LowPart = ft.dwLowDateTime;
        liLocal.HighPart = ft.dwHighDateTime;
        LONGLONG llRelDate = (LONGLONG)0;
        for( ;; )
        {
            // eat white space
            while( iswspace( *phrase ) )
                phrase++;

            if( *phrase == 0 ) break;

            // parse the number
            WCHAR * pwcEnd;
            LONG lValue = wcstol( phrase, &pwcEnd, 10 );

            if( lValue < 0 )
                THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );

            // eat white space
            phrase = pwcEnd;
            while( iswspace( *phrase ) )
                phrase++;

            // grab the unit char & subtract the appropriate amount
            WCHAR wcUnit = *phrase++;
            switch( wcUnit )
            {
            case L'y':
            case L'Y':
                lValue *= 4;
                // Fall through and handle year like 4 quarters

            case L'q':
            case L'Q':
                lValue *= 3;
                // Fall through and handle quarters like 3 months

            case L'm':
            case L'M':
                 // Getting the System time to determine the day and month.

                if(!FileTimeToSystemTime(&ft, &st))
                {
                    THROW(CParserException(QPARSE_E_INVALID_LITERAL));
                }
                wStartDate = st.wDay;
                wDayOfMonth = st.wDay;
                iLoopValue = lValue;
                while(iLoopValue)
                {
                    // Subtracting to the end of previous month
                    llRelDate = llTicksInADay * ((LONGLONG)(wDayOfMonth));
                    liLocal.QuadPart -= llRelDate;
                    ft.dwLowDateTime = liLocal.LowPart;
                    ft.dwHighDateTime = liLocal.HighPart;
                    SYSTEMTIME stTemp;
                    if(!FileTimeToSystemTime(&ft, &stTemp))
                    {
                         THROW(CParserException(QPARSE_E_INVALID_LITERAL));
                    }
                    //
                    // if the end of previous month is greated then start date then we subtract to back up to the
                    // start date.  This will take care of 28/29 Feb(backing from 30/31 by 1 month).
                    //
                    if(stTemp.wDay > wStartDate)
                    {
                        llRelDate = llTicksInADay * ((LONGLONG)(stTemp.wDay - wStartDate));
                        liLocal.QuadPart -= llRelDate;
                        ft.dwLowDateTime = liLocal.LowPart;
                        ft.dwHighDateTime = liLocal.HighPart;
                        // Getting the date into stTemp for further iteration
                        if(!FileTimeToSystemTime(&ft, &stTemp))
                        {
                           THROW( CParserException( QPARSE_E_INVALID_LITERAL ));
                        }
                    }
                    wDayOfMonth = stTemp.wDay;
                    iLoopValue--;
                } //End While

                break;

            case L'w':
            case L'W':
                lValue *= 7;

            case L'd':
            case L'D':
                llRelDate = llTicksInADay * ((LONGLONG)lValue);
                liLocal.QuadPart -= llRelDate;
                ft.dwLowDateTime = liLocal.LowPart;
                ft.dwHighDateTime = liLocal.HighPart;
                break;

            case L'h':
            case L'H':
                llRelDate = llTicksInAHour * ((LONGLONG)lValue);
                liLocal.QuadPart -= llRelDate;
                ft.dwLowDateTime = liLocal.LowPart;
                ft.dwHighDateTime = liLocal.HighPart;
                break;

            case L'n':
            case L'N':
                llRelDate = ((LONGLONG)10000000) * ((LONGLONG)60) * ((LONGLONG)lValue) ;
                liLocal.QuadPart -= llRelDate;
                ft.dwLowDateTime = liLocal.LowPart;
                ft.dwHighDateTime = liLocal.HighPart;
                break;

            case L's':
            case L'S':
                llRelDate = ((LONGLONG)10000000) * ((LONGLONG)lValue);
                liLocal.QuadPart -= llRelDate;
                ft.dwLowDateTime = liLocal.LowPart;
                ft.dwHighDateTime = liLocal.HighPart;
                break;

            default:
                THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
            }

        } // for( ;; )

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}


%}



%start query
%%

/***
 ***      Tripolish YACC grammar
 ***
 ***      Note - left recursion (i.e. A: A,B) used throughout because yacc
 ***      handles it more efficiently.
 ***
 ***/
query:          AndTerm
                   {
                   $$ = $1;
                   }
            |   query _OR AndTerm
                {
                    AssertReq($1);
                    AssertReq($3);

                    XDbRestriction prstQuery($1);
                    XDbRestriction prstTerm($3);
                    _setRst.Remove( $1 );
                    _setRst.Remove( $3 );


                    if (DBOP_or == $1->GetCommandType())
                    {
                        // add right term & release its smart pointer
                        ((CDbBooleanNodeRestriction *)($1))->AppendChild( prstTerm.GetPointer() );
                        prstTerm.Acquire();
                        $$ = prstQuery.Acquire();
                    }
                    else
                    {
                        // create smart Or node
                        XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_or ) );
                        if( 0 == prstNew.GetPointer() )
                            THROW( CException( E_OUTOFMEMORY ) );

                        prstNew->SetWeight(MAX_QUERY_RANK);

                        // add left term & release its smart pointer
                        prstNew->AppendChild( prstQuery.GetPointer() );
                        prstQuery.Acquire();

                        // add right term & release its smart pointer
                        prstNew->AppendChild( prstTerm.GetPointer() );
                        prstTerm.Acquire();

                        $$ = prstNew.Acquire();
                    }
                    _setRst.Add( $$ );
                }
            |   _NOT query
                {

                        AssertReq($2);

                        XDbRestriction prstQuery($2);
                        _setRst.Remove( $2 );

                        // Create not node

                        XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_not ) );
                        if( 0 == prstNew.GetPointer() )
                            THROW( CException( E_OUTOFMEMORY ) );


                        prstNew->SetWeight(MAX_QUERY_RANK);

                        // add right term and release its smart pointer
                        prstNew->AppendChild( prstQuery.GetPointer() );
                        prstQuery.Acquire();

                        $$ = prstNew.Acquire();
                        _setRst.Add( $$ );
                }
           ;

AndTerm:            NearTerm
                    { $$ = $1;}

                |   _NOT PropTerm
                    {
                        AssertReq($2);

                        XDbRestriction prstTerm($2);
                        _setRst.Remove( $2 );

                        // Create not node

                        XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_not ) );
                        if( 0 == prstNew.GetPointer() )
                            THROW( CException( E_OUTOFMEMORY ) );

                        prstNew->SetWeight(MAX_QUERY_RANK);

                        // add right term and release its smart pointer
                        prstNew->AppendChild( prstTerm.GetPointer() );
                        prstTerm.Acquire();

                        $$ = prstNew.Acquire();
                        _setRst.Add( $$ );
                    }
                |   AndTerm _AND NearTerm
                    {
                        AssertReq($1);
                        AssertReq($3);

                        XDbRestriction prstQuery($1);
                        XDbRestriction prstTerm($3);
                        _setRst.Remove( $1 );
                        _setRst.Remove( $3 );

                        if (DBOP_and == $1->GetCommandType())
                        {
                            // add right term & release its smart pointer
                            ((CDbBooleanNodeRestriction *)($1))->AppendChild( prstTerm.GetPointer() );
                            prstTerm.Acquire();
                            $$ = prstQuery.Acquire();
                        }
                        else
                        {
                            // create smart And node
                            XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_and ) );
                            if( prstNew.GetPointer() == 0 )
                                THROW( CException( E_OUTOFMEMORY ) );

                            prstNew->SetWeight(MAX_QUERY_RANK);

                            // add left term & release its smart pointer
                            prstNew->AppendChild( prstQuery.GetPointer() );
                            prstQuery.Acquire();

                            // add right term & release its smart pointer
                            prstNew->AppendChild( prstTerm.GetPointer() );
                            prstTerm.Acquire();

                            $$ = prstNew.Acquire();
                        }
                        _setRst.Add( $$ );
                    }
                |   AndTerm _AND _NOT NearTerm
                    {
                        AssertReq($1);
                        AssertReq($4);

                        XDbRestriction prstQuery($1);
                        XDbRestriction prstTerm($4);
                        _setRst.Remove( $1 );
                        _setRst.Remove( $4 );

                        // create smart Not node
                        XDbNotRestriction prstNot( new CDbNotRestriction );
                        if( prstNot.GetPointer() == 0 )
                            THROW( CException( E_OUTOFMEMORY ) );

                        prstNot->SetWeight(MAX_QUERY_RANK);

                        // set child of Not node & release smart factor pointer
                        prstNot->SetChild( prstTerm.GetPointer() );
                        prstTerm.Acquire();

                        if (DBOP_and == $1->GetCommandType())
                        {
                            // add right term & release its smart pointer
                            ((CDbBooleanNodeRestriction *)($1))->AppendChild( prstNot.GetPointer() );
                            prstNot.Acquire();

                            $$ = prstQuery.Acquire();
                        }
                        else
                        {
                            // create smart And node
                            XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_and ) );
                            if( prstNew.GetPointer() == 0 )
                                THROW( CException( E_OUTOFMEMORY ) );

                            prstNew->SetWeight(MAX_QUERY_RANK);

                            // add left term & release its smart pointer
                            prstNew->AppendChild( prstQuery.GetPointer() );
                            prstQuery.Acquire();

                            // add right term & release its smart pointer
                            prstNew->AppendChild( prstNot.GetPointer() );
                            prstNot.Acquire();

                            $$ = prstNew.Acquire();
                        }
                        _setRst.Add( $$ );
                    }
            ;

NearTerm:       CoerceTerm
                { $$ = $1; }
            |   NearTerm _NEAR CoerceTerm
                {
                    // uses defaults

                    AssertReq($1);
                    AssertReq($3);

                    XDbRestriction prstLeft($1);
                    XDbRestriction prstRight($3);
                    _setRst.Remove( $1 );
                    _setRst.Remove( $3 );

                    if (DBOP_content_proximity == $1->GetCommandType() &&
                        50 == ((CDbProximityNodeRestriction *)$1)->GetProximityDistance() &&
                        PROXIMITY_UNIT_WORD == ((CDbProximityNodeRestriction *)$1)->GetProximityUnit())
                    {
                        // add right term & release its smart pointer
                        ((CDbProximityNodeRestriction *)$1)->AppendChild( prstRight.GetPointer() );
                        prstRight.Acquire();

                        $$ = prstLeft.Acquire();
                    }
                    else
                    {
                        // create smart Prox node
                        XDbProximityNodeRestriction prstNew(new CDbProximityNodeRestriction()); // uses defaults

                        if ( prstNew.IsNull() || !prstNew->IsValid() )
                            THROW( CException( E_OUTOFMEMORY ) );

                        // add left phrase & release its smart pointer
                        prstNew->AppendChild( prstLeft.GetPointer() );
                        prstLeft.Acquire();

                        // add right term & release its smart pointer
                        prstNew->AppendChild( prstRight.GetPointer() );
                        prstRight.Acquire();

                        $$ = prstNew.Acquire();
                    }
                    _setRst.Add( $$ );
                }
            |   NearTerm _NEARDIST _NEARUNIT _NEAR_END CoerceTerm
                {
                    AssertReq($1);
                    AssertReq($2);
                    AssertReq($3);
                    AssertReq($5);

                    XDbRestriction prstLeft($1);
                    XDbRestriction prstRight($5);
                    _setRst.Remove( $1 );
                    _setRst.Remove( $5 );

                    WCHAR * pwcEnd;
                    ULONG ulValue = wcstol( $2, &pwcEnd, 10 );
                    ULONG ulUnit;

                    if (L'w' == *$3)
                        ulUnit = PROXIMITY_UNIT_WORD;
                    else if (L's' == *$3)
                        ulUnit = PROXIMITY_UNIT_SENTENCE;
                    else if (L'p' == *$3)
                        ulUnit = PROXIMITY_UNIT_PARAGRAPH;
                    else if (L'c' == *$3)
                        ulUnit = PROXIMITY_UNIT_CHAPTER;

                    if (DBOP_content_proximity == $1->GetCommandType() &&
                        ulValue == ((CDbProximityNodeRestriction *)$1)->GetProximityDistance() &&
                        ulUnit == ((CDbProximityNodeRestriction *)$1)->GetProximityUnit())
                    {
                        // add right term & release its smart pointer
                        ((CDbProximityNodeRestriction *)$1)->AppendChild( prstRight.GetPointer() );
                        prstRight.Acquire();

                        $$ = prstLeft.Acquire();
                    }
                    else
                    {
                        // create smart Prox node
                        XDbProximityNodeRestriction prstNew(new CDbProximityNodeRestriction(ulUnit, ulValue));

                        if( prstNew.IsNull() || !prstNew->IsValid() )
                            THROW( CException( E_OUTOFMEMORY ) );

                        // add left phrase & release its smart pointer
                        prstNew->AppendChild( prstLeft.GetPointer() );
                        prstLeft.Acquire();

                        // add right term & release its smart pointer
                        prstNew->AppendChild( prstRight.GetPointer() );
                        prstRight.Acquire();

                        $$ = prstNew.Acquire();
                    }
                    _setRst.Add( $$ );
                }
           ;

CoerceTerm:
                Gen NestTerm GenEnd
                {
                    $$ = $2;
                }
            |   Gen _COERCE Gen NestTerm GenEnd
                {
                    AssertReq($4);
                    XDbRestriction prstQuery($4);

                    $4->SetWeight(MAX_QUERY_RANK);
                    $$ = prstQuery.Acquire();
                }
            |   Gen _WEIGHT Gen NestTerm GenEnd
                {
                    AssertReq($2);
                    AssertReq($4);
                    XDbRestriction prstQuery($4);

                    WCHAR * pwcEnd;
                    double dWeight = wcstod( $2, &pwcEnd );
                    if ( dWeight > 1.0 )
                       THROW( CParserException( QPARSE_E_WEIGHT_OUT_OF_RANGE ) );
                    LONG lWeight = (LONG)(dWeight * MAX_QUERY_RANK);
                    $4->SetWeight(lWeight);
                    $$ = prstQuery.Acquire();
                }
            ;

Gen:            /* empty */
                {
                    $$ = 0;
                }
            |   _GENPREFIX
                {
                    SetCurrentGenerate(GENERATE_METHOD_PREFIX);
                    $$ = GENERATE_METHOD_PREFIX;
                }
            |   _GENINFLECT
                {
                    SetCurrentGenerate(GENERATE_METHOD_INFLECT);
                    $$ = GENERATE_METHOD_INFLECT;
                }
            ;

GenEnd:         /* empty */
                {
                    $$ = GENERATE_METHOD_EXACT;
                }
            |   _GENNORMAL
                {
                    // don't set the generate method to 0 here. Doing so will
                    // reset the method before it gets used.
                    $$ = GENERATE_METHOD_EXACT;
                }

NestTerm:       VectorTerm
                {
                    $$ = $1;
                }
            |   Term
                {
                    $$ = $1;
                }
            |   Open query Close
                {
                    $$ = $2;
                }
            ;

VectorTerm:     VectorSpec _VECTOR_END
                {
                    $$ = $1;
                }

VectorSpec:     _VEMETHOD _VE query
                {
                    AssertReq($1);
                    AssertReq($3);

                    XDbRestriction prstQuery($3);
                    _setRst.Remove( $3 );

                    ULONG rankMethod = VECTOR_RANK_JACCARD; // default if nothing else matches

                    if ( 0 == _wcsicmp( L"jaccard", $1) )
                    {
                        rankMethod = VECTOR_RANK_JACCARD;
                    }
                    else if ( 0 == _wcsicmp( L"dice", $1) )
                    {
                        rankMethod = VECTOR_RANK_DICE;
                    }
                    else if ( 0 == _wcsicmp( L"inner", $1) )
                    {
                        rankMethod = VECTOR_RANK_INNER;
                    }
                    else if ( 0 == _wcsicmp( L"max", $1) )
                    {
                        rankMethod = VECTOR_RANK_MAX;
                    }
                    else if ( 0 == _wcsicmp( L"min", $1) )
                    {
                        rankMethod = VECTOR_RANK_MIN;
                    }
                    else
                    {
                        THROW( CException( QPARSE_E_INVALID_RANKMETHOD ) );
                    }

                    // create smart Vector node
                    XDbVectorRestriction prstNew( new CDbVectorRestriction( rankMethod ) );

                    if ( prstNew.IsNull() || !prstNew->IsValid() )
                    {
                        THROW( CException( E_OUTOFMEMORY ) );
                    }

                    // add expression & release its smart pointer
                    prstNew->AppendChild( prstQuery.GetPointer() );
                    prstQuery.Acquire();

                    // Let the next vector element start off with a clean slate...
                    // We don't want the current element's property and genmethod
                    // to rub off on it.
                    InitState();


                    $$ = prstNew.Acquire();
                    _setRst.Add( $$ );
                }
            |   VectorSpec _VE query
                {
                    AssertReq($1);
                    AssertReq($3);

                    XDbRestriction prstLeft($1);
                    XDbRestriction prstRight($3);
                    _setRst.Remove( $1 );
                    _setRst.Remove( $3 );

                    // add right term & release its smart pointer
                    ((CDbVectorRestriction *)($1))->AppendChild( prstRight.GetPointer() );
                    prstRight.Acquire();

                    // Let the next vector element start off with a clean slate...
                    // We don't want the current element's property and genmethod
                    // to rub off on it.
                    InitState();

                    $$ = prstLeft.Acquire();
                    _setRst.Add( $$ );
                }
            ;

Open:           _OPEN
                {
                    SaveState();
                    $$ = 0;
                }
Close:          _CLOSE
                {
                    RestoreState();
                    $$ = 0;
                }

Term:           PropTerm
                {
                    $$ = $1;
                }

            |   Property Contains _PHRASE ShortGen PropEnd
                {
                    $$ = BuildPhrase($3, $4);
                    _setRst.Add( $$ );
                }
            |   Property Contains _PHRASE PropEnd
                {
                    $$ = BuildPhrase($3, 0);
                    _setRst.Add( $$ );
                }
            |   Property Contains Gen _PHRASE GenEnd PropEnd
                {
                    $$ = BuildPhrase($4, $3);
                    _setRst.Add( $$ );
                }
            |   Property Contains query
                {
                    $$ = $3;
                }
            |   Property Contains _FREETEXT ShortGen PropEnd
                {
                    AssertReq($3);
                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    // We used the property. Now pop it off if need be
                    if (fDeferredPop)
                        PopProperty();

                    // Clear generation method so it won't rub off on the following phrase
                    SetCurrentGenerate(GENERATE_METHOD_EXACT);

                    // generation method not used - if it's there, ignore it
                    // (already stripped from longhand version, but not from
                    // shorthand version
                    LPWSTR pLast = $3 + wcslen($3) -1;
                    if ( L'*' == *pLast) // prefix
                        *pLast-- = L'\0';
                    if ( L'*' == *pLast) // inflect
                        *pLast-- = L'\0';

                    XDbNatLangRestriction pRest ( new CDbNatLangRestriction ($3, *pps, _lcid));
                    if ( pRest.IsNull() || !pRest->IsValid() )
                        THROW( CException( E_OUTOFMEMORY ) );

                    $$ = pRest.Acquire();
                    _setRst.Add( $$ );
                }
            ;


PropTerm:       Property Matches _REGEX PropEnd
                {
                    AssertReq($3);

                    CDbColId *pps;
                    DBTYPE dbType;

                    GetCurrentProperty(&pps, &dbType);

                    // We used the property. Now pop it off if need be
                    if (fDeferredPop)
                        PopProperty();

                    if ( ( ( DBTYPE_WSTR|DBTYPE_BYREF ) != dbType ) &&
                        ( ( DBTYPE_STR|DBTYPE_BYREF ) != dbType ) &&
                        ( VT_BSTR != dbType ) &&
                        ( VT_LPWSTR != dbType ) &&
                        ( VT_LPSTR != dbType ) )
                        THROW( CParserException( QPARSE_E_EXPECTING_REGEX_PROPERTY ) );

                    if( $3 == 0 )
                        THROW( CParserException( QPARSE_E_EXPECTING_REGEX ) );

                    // create smart Property node
                    XDbPropertyRestriction prstProp( new CDbPropertyRestriction );
                    if( prstProp.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    prstProp->SetRelation(DBOP_like);      // LIKE relation

                    if ( ( ! prstProp->SetProperty( *pps ) ) ||
                        ( ! prstProp->SetValue( $3 ) ) ||
                        ( ! prstProp->IsValid() ) )
                        THROW( CException( E_OUTOFMEMORY ) );

                    // release & return smart Property node
                    $$ = prstProp.Acquire();
                    _setRst.Add( $$ );
                }

            |   Property Op Value PropEnd
                {
                    AssertReq($2);
                    AssertReq($3);
                    XPtr<CStorageVariant> pStorageVar( $3 );
                    _setStgVar.Remove( $3 );

                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    // We used the property. Now pop it off if need be
                    if (fDeferredPop)
                        PopProperty();

                    // create smart Property node
                    XDbPropertyRestriction prstProp( new CDbPropertyRestriction );
                    if( prstProp.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    if (! prstProp->SetProperty( *pps ) )
                        THROW( CException( E_OUTOFMEMORY ) );

                    // don't allow @contents <relop> X -- it's too expensive and we'll
                    // never find any hits anyway (until we implement this feature)

                    if ( *pps == psContents )
                        THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );

                    prstProp->SetRelation( $2 );

                    if ( 0 != pStorageVar.GetPointer() )
                    {
                        // This should always be the case  - else PropValueParser would have thrown

                        if ( ! ( ( prstProp->SetValue( pStorageVar.GetReference() ) ) &&
                            ( prstProp->IsValid() ) ) )
                            THROW( CException( E_OUTOFMEMORY ) );
                    }
                    $$ = prstProp.Acquire();
                    _setRst.Add( $$ );
                }
            ;

Property:       /* empty */
                {
                    $$ = 0;
                }
            |   _PROPNAME
                {
                    PushProperty($1);
                    $$ = 0;
                }
            ;

PropEnd:        /* empty */
                {
                    fDeferredPop = FALSE;
                    $$ = 0;
                }
            |   _PROPEND
                {
                    // When PropEnd is matched, the action of using the property
                    // hasn't yet taken place. So popping the property right now
                    // will cause the property to be unavailable when the action
                    // is performed. Instead, pop it off after it has been used.
                    fDeferredPop = TRUE;
                    $$ = 0;
                }
           ;

Op:             _EQ
                { $$ = DBOP_equal;}
            |   _NE
                { $$ = DBOP_not_equal;}
            |   _GT
                { $$ = DBOP_greater;}
            |   _GTE
                { $$ = DBOP_greater_equal;}
            |   _LT
                { $$ = DBOP_less;}
            |   _LTE
                { $$ = DBOP_less_equal;}
            |   _ALLOF
                { $$ = DBOP_allbits;}
            |   _SOMEOF
                { $$ = DBOP_anybits;}
            |   _EQSOME
                { $$ = DBOP_equal_any;}
            |   _NESOME
                { $$ = DBOP_not_equal_any;}
            |   _GTSOME
                { $$ = DBOP_greater_any;}
            |   _GTESOME
                { $$ = DBOP_greater_equal_any;}
            |   _LTSOME
                { $$ = DBOP_less_any;}
            |   _LTESOME
                { $$ = DBOP_less_equal_any;}
            |   _ALLOFSOME
                { $$ = DBOP_allbits_any;}
            |   _SOMEOFSOME
                { $$ = DBOP_anybits_any;}
            |   _EQALL
                { $$ = DBOP_equal_all;}
            |   _NEALL
                { $$ = DBOP_not_equal_all;}
            |   _GTALL
                { $$ = DBOP_greater_all;}
            |   _GTEALL
                { $$ = DBOP_greater_equal_all;}
            |   _LTALL
                { $$ = DBOP_less_all;}
            |   _LTEALL
                { $$ = DBOP_less_equal_all;}
            |   _ALLOFALL
                { $$ = DBOP_allbits_all;}
            |   _SOMEOFALL
                { $$ = DBOP_anybits_all;}
            ;

Matches:     /* empty */
                { $$ = 0; }
            | _EQ
                { $$ = DBOP_equal; }


Value:          _PHRASE
                {
                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    CValueParser PropValueParser( FALSE, dbType, _lcid );
                    StripQuotes($1);
                    PropValueParser.AddValue( $1 );

                    $$ = PropValueParser.AcquireStgVariant();
                    _setStgVar.Add( $$ );
                 }
            |   _FREETEXT
                {
                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    CValueParser PropValueParser( FALSE, dbType, _lcid );
                    PropValueParser.AddValue( $1 );

                    $$ = PropValueParser.AcquireStgVariant();
                    _setStgVar.Add( $$ );
                }
           |    VectorValue
                {
                    $$ = $1;
                }

           ;

VectorValue:    EmptyVector
                {
                    XPtr <CValueParser> pPropValueParser ( $1 );
                    _setValueParser.Remove( $1 );
                    $$ = $1->AcquireStgVariant();
                    _setStgVar.Add( $$ );
                }
            |   SingletVector
                {
                    XPtr <CValueParser> pPropValueParser ( $1 );
                    _setValueParser.Remove( $1 );
                    $$ = $1->AcquireStgVariant();
                    _setStgVar.Add( $$ );
                }
            |   MultiVector _VE_END
                {
                    XPtr <CValueParser> pPropValueParser ( $1 );
                    _setValueParser.Remove( $1 );
                    $$ = $1->AcquireStgVariant();
                    _setStgVar.Add( $$ );
                }

EmptyVector:   _OPEN _CLOSE
               {
                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    XPtr <CValueParser> pPropValueParser ( new CValueParser( TRUE, dbType, _lcid ) );
                    if( pPropValueParser.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    $$ = pPropValueParser.Acquire();
                    _setValueParser.Add( $$ );
               }

SingletVector:  _OPEN _PHRASE _CLOSE
                {
                    AssertReq($2);

                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    XPtr <CValueParser> pPropValueParser ( new CValueParser( TRUE, dbType, _lcid ) );
                    if( pPropValueParser.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    StripQuotes($2);
                    pPropValueParser->AddValue( $2 );

                    $$ = pPropValueParser.Acquire();
                    _setValueParser.Add( $$ );
                }
            |   _OPEN _FREETEXT _CLOSE
                {
                    AssertReq($2);

                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    XPtr <CValueParser> pPropValueParser ( new CValueParser( TRUE, dbType, _lcid ) );
                    if( pPropValueParser.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    pPropValueParser->AddValue( $2 );

                    $$ = pPropValueParser.Acquire();
                    _setValueParser.Add( $$ );
                }



MultiVector:    _VECTORELEMENT _VECTORELEMENT
                {
                    AssertReq($1);
                    AssertReq($2);

                    CDbColId *pps;
                    DBTYPE dbType;
                    GetCurrentProperty(&pps, &dbType);

                    XPtr <CValueParser> pPropValueParser ( new CValueParser( TRUE, dbType, _lcid ) );
                    if( pPropValueParser.GetPointer() == 0 )
                        THROW( CException( E_OUTOFMEMORY ) );

                    pPropValueParser->AddValue( $1 );
                    pPropValueParser->AddValue( $2 );

                    $$ = pPropValueParser.Acquire();
                    _setValueParser.Add( $$ );
                }


            |   MultiVector _VECTORELEMENT
                {
                    AssertReq($1);
                    AssertReq($2);

                    $1->AddValue($2);

                    $$ = $1;
                }
            ;

Contains:      /* empty */
               {
                   $$ = 0;
               }
            |  _CONTAINS
               {
                   $$ = 0;
               }
            ;

ShortGen:      /* empty */
               {
                   $$ = 0;
               }
            |  _SHGENPREFIX
               {
                   $$ = 1;
               }
            |  _SHGENINFLECT
               {
                   $$ = 2;
               }
            ;