/*============================================================================
    ORCA Interpreter
    Copyright (C) 2005-2006  Karl Robillard

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
===========================================================================*/


#include "os.h"
#include "ovalue.h"
#include "orca_atoms.h"
#include "internal.h"


#define PARSE_EX
#define orChar          orInt
#define UChar           char
#define OR_ITER_BLOCK(ita,itb,blk,scell) \
    ita = blk->values + scell->series.it; \
    itb = blk->values + blk->used;



extern int orMatchString( const OString* strA, int iA,
                          const OString* strB, int iB );
extern int orFindString( const OString* strA, int iA,
                         const OString* strB, int iB );


typedef struct
{
    uint8_t  type;
    uint8_t  flags;
    uint8_t  datatype;
    uint8_t  _pad;
    OIndex  n;      /* Series number */
    OIndex  it;     /* Element Iterator */
    OIndex  end;    /* Slice end */
}
UCellSlice;


typedef struct
{
    //UThread*   thread;
    OValue*    rules;
    UCellSlice input;
    OString*   str;
    int        error;
    int        matchCase;
}
StringParser;


typedef struct
{
    //UThread*   thread;
    OValue*     rules;
    UCellSlice  input;
    OString*   blk;
    int        error;
}
BlockParser;


/*
  Goto next rule; search for '|'.
*/
static const OValue* nextRule( const OValue* it, const OValue* end )
{
    while( it != end )
    {
        if( orIs(it, OT_WORD) && (orAtom(it) == OR_ATOM_BAR) )
        {
            return it + 1;
        }
        ++it;
    }
    return 0;
}


static int _repeatChar( OString* input, OIndex pos, int limit, int c )
{
    UChar* start;
    UChar* it;
    UChar* end;

    it  = input->charArray;
    end = it + input->used;
    it += pos;
    if( end > (it + limit) )
        end = it + limit;

    start = it;
    while( it != end )
    {
        if( *it != c )
            break;
        ++it;
    }
    return it - start;
}


static int _repeatBitset( OString* input, OIndex pos, int limit,
                          const OValue* patc )
{
    UChar* start;
    UChar* it;
    UChar* end;
    int c;
    OBinary* bin = orSTRING(patc);
    const uint8_t* bits = bin->byteArray;
    int maxC = bin->used * 8;

    it  = input->charArray;
    end = it + input->used;
    it += pos;
    if( end > (it + limit) )
        end = it + limit;

    start = it;
    while( it != end )
    {
        c = *it;
        if( c >= maxC )
            break;
        if( ! orBitIsSet( bits, c ) )
            break;
        ++it;
    }
    return it - start;
}


/*
   Returns zero if matching rule not found.
*/
static const OValue* _parseStr( StringParser* pe, const OValue* rit,
                               const OValue* rend, OIndex* spos )
{
    const OBlock* cblk;
    const OValue* tval;
    int32_t repMin;
    int32_t repMax;
    OString* istr;
    OIndex pos = *spos;

    istr = pe->str;

match:

    while( rit != rend )
    {
        switch( orType(rit) )
        {
            case OT_WORD:
                switch( orAtom(rit) )
                {
                case OR_ATOM_OPT:
                    ++rit;
                    repMin = 0;
                    repMax = 1;
                    goto repeat;

                case OR_ATOM_ANY:
                    ++rit;
                    repMin = 0;
                    repMax = 0x7fffffff;
                    goto repeat;

                case OR_ATOM_SOME:
                    ++rit;
                    repMin = 1;
                    repMax = 0x7fffffff;
                    goto repeat;

                case OR_ATOM_BREAK:
                    return rit;

                case OR_ATOM_BAR:
                    goto complete;

                case OR_ATOM_TO:
                case OR_ATOM_THRU:
                {
                    OAtom ratom = orAtom(rit);

                    ++rit;
                    if( rit == rend )
                        return 0;

                    if( orIs(rit, OT_WORD) )
                    {
                        orWordVal( rit, cblk, tval );
                    }
                    else
                    {
                        tval = rit;
                    }

                    switch( orType(tval) )
                    {
                        case OT_CHAR:
                        {
                            int c = orChar(tval);
                            UChar* cp  = istr->charArray + pos;
                            UChar* end = istr->charArray + pe->input.end;
                            while( cp != end )
                            {
                                if( *cp == c )
                                    break;
                                ++cp;
                            }
                            if( cp == end )
                                goto failed;
                            pos = cp - istr->charArray;
                            if( ratom == OR_ATOM_THRU )
                                ++pos;
                        }
                            break;

                        case OT_STRING:
                        {
                            OString* pat = orSTRING(tval);
                            pos = orFindString( istr, pos,
                                                pat, tval->series.it );
                            if( pos < 0 )
                                goto failed;
                            if( ratom == OR_ATOM_THRU )
                                pos += pat->used - tval->series.it;
                        }
                            break;

                        case OT_BITSET:
                        {
                            OBinary* bin = orSTRING(tval);
                            const uint8_t* bits = bin->byteArray;
                            int maxC = bin->used * 8;
                            UChar* cp  = istr->charArray + pos;
                            UChar* end = istr->charArray + pe->input.end;
                            int c;
                            while( cp != end )
                            {
                                c = *cp;
                                if( c < maxC )
                                {
                                    if( orBitIsSet( bits, c ) )
                                        break;
                                }
                                ++cp;
                            }
                            if( cp == end )
                                goto failed;
                            pos = cp - istr->charArray;
                            if( ratom == OR_ATOM_THRU )
                                ++pos;
                        }
                            break;

                        case OT_BLOCK:
                            // TODO
                            orError( PARSE_EX
                                     "to/thru block! not implemented" );
                            pe->error = 1;
                            return 0;
                    }
                    ++rit;
                }
                    break;

                case OR_ATOM_SKIP:
                    // TODO - int! skip
                    //if( pos >= istr->used )
                    //    return 0;
                    ++rit;
                    ++pos;
                    break;

                //case OR_ATOM_COPY:

                default:
                    orWordVal( rit, cblk, tval );

                    if( orIs(tval, OT_CHAR) )
                        goto match_char;
                    else if( orIs(tval, OT_STRING) )
                        goto match_string;
                    else if( orIs(tval, OT_BLOCK) )
                        goto match_block;
                    else if( orIs(tval, OT_BITSET) )
                        goto match_bitset;
                    else
                    {
                        orError( PARSE_EX
                                 "parse expected char!/block!/bitset!" );
                        pe->error = 1;
                        return 0;
                    }
                    break;
                }
                break;

            case OT_SETWORD:
            {
                OValue* cell;
                orWordVal( rit, cblk, cell );
                ++rit;

                orSetTF( cell, OT_STRING );
                orSetSeries( cell, pe->input.n, pos );
            }
                break;
#if 0
            case OT_GETWORD:
            {
                OValue* cell;
                orWordVal( rit, cblk, cell );
                ++rit;

                if( orIs(cell, OT_SLICE) && (cell->series.n == pe->input.n) )
                    cell->slice.end = pos;
            }
                break;
#endif
            case OT_INTEGER:
                repMin = orInt(rit);

                ++rit;
                if( rit == rend )
                    return 0;

                if( orIs(rit, OT_INTEGER) )
                {
                    repMax = orInt(rit);
                    ++rit;
                }
                else
                {
                    repMax = repMin;
                }
                goto repeat;

            case OT_CHAR:
                tval = rit;
match_char:
                if( istr->charArray[ pos ] == orChar(tval) )
                {
                    ++rit;
                    ++pos;
                }
                else
                    goto failed;
                break;

            case OT_BLOCK:
                tval = rit;
match_block:
                cblk = orBLOCK( tval );
                tval = _parseStr( pe, cblk->values + tval->series.it,
                                      cblk->values + cblk->used, &pos );
                istr = pe->str;
                if( ! tval )
                    goto failed_eval;
                ++rit;
                break;

            case OT_PAREN:
                orEvalBlock( orBlockPtr(rit->series.n), rit->series.it );
                if( orErrorThrown )
                {
                    pe->error = 1;
                    return 0;
                }

                /* Re-aquire pointer & check if input modified. */
                istr = pe->str = orStringPtr( pe->input.n );
                if( istr->used < pe->input.end )
                    pe->input.end = istr->used;

                ++rit;
                break;

            case OT_STRING:
                tval = rit;
match_string:
            {
                OString* pat = orSTRING( tval );

                /*
                if( pe->matchCase )
                    pos = orMatchStringCase(istr, pos, pat, tval->series.it);
                else
                */
                    pos = orMatchString( istr, pos, pat, tval->series.it );

                if( pos )
                    ++rit;
                else
                    goto failed;
            }
                break;

            case OT_BITSET:
                tval = rit;
match_bitset:
            {
                OString* bin = orSTRING( tval );
                int c;

                c = istr->charArray[ pos ];
                if( orBitIsSet( bin->byteArray, c ) )
                {
                    ++rit;
                    ++pos;
                }
                else
                    goto failed;
            }
                break;

            default:
                orError( PARSE_EX "invalid parse value" );
                             //orDatatypeName( orType(rit) ) );
                pe->error = 1;
                return 0;
        }
    }

complete:

    *spos = pos;
    return rit;

repeat:

    /* Repeat rit for repMin to repMax times. */

    if( rit == rend )
    {
        orError( PARSE_EX "Enexpected end of parse rule" );
        pe->error = 1;
        return 0;
    }
    else
    {
        int count;

        if( orIs(rit, OT_WORD) )
        {
            orWordVal( rit, cblk, tval );
        }
        else
        {
            tval = rit;
        }

        switch( orType(tval) )
        {
            case OT_CHAR:
                count = _repeatChar( istr, pos, repMax, orChar(tval) );
                pos += count;
                break;

            case OT_STRING:
            {
                OString* pat;
                int p2;

                count = 0;
                pat = orSTRING(tval);

                while( count < repMax )
                {
                    p2 = orMatchString( istr, pos, pat, tval->series.it );
                    if( ! p2 )
                        break;
                    pos = p2;
                    ++count;
                }
            }
                break;

            case OT_BITSET:
                count = _repeatBitset( istr, pos, repMax, tval );
                pos += count;
                break;

            case OT_BLOCK:
            {
                OValue* ci;
                OValue* ce;

                count = 0;
                cblk = orBLOCK( tval );
                OR_ITER_BLOCK( ci, ce, cblk, tval );

                while( count < repMax )
                {
                    if( pos == pe->input.end )
                        break;
                    if( ! _parseStr( pe, ci, ce, &pos ) )
                        break;
                    ++count;
                }
                if( pe->error )
                    return 0;
                istr = pe->str;
            }
                break;

            default:
                orError( PARSE_EX "Invalid parse rule" );
                pe->error = 1;
                return 0;
        }

        if( count < repMin )
            goto failed;
        ++rit;
    }
    goto match;

failed_eval:

    if( pe->error )
        return 0;

failed:

    rit = nextRule( rit, rend );
    if( rit )
    {
        pos = *spos;
        goto match;
    }
    return 0;
}


#if 0
static const char* _parseStrFailedMessage( StringParser* pe,
                                           const UChar* it, const UChar* end )
{
    UChar* dest;
    OString* str = orStringPtr( pe->thread->callTempBinN );
    str->used = 0;
    ur_strCat( str, "parse failed at \"", 17 );

    orArrayReserve( str, sizeof(UChar), str->used + (end - it) + 2 );
    dest = str->charArray + str->used;
    while( it != end )
    {
        if( *it == '\n' )
            break;
        *dest++ = *it++;
        //++str->used;
    }
    *dest++ = '"';
    *dest   = '\0';

    return str->charArray;
}
#endif


static void _parseStringS( OValue* a1, OValue* rules, int all, int useCase )
{
    OBinary* custom;
    uint8_t* delim;
    const char* cp;
    const char* end;
    const char* sstart;
    OBlock* rblk;
    OIndex rblkN;
    OIndex rblkHold;
    OIndex customHold;
    OString* str = orSTRING(a1);

    (void) useCase;

    rblk  = orMakeBlock( 0 );
    rblkN = orBlockN( rblk );
    rblkHold = orHold( OT_BLOCK, rblkN );

    if( orIs(rules, OT_STRING) )
    {
        custom = orMakeCharset( orSTRING(rules), rules->series.it );
        customHold = orHold( OT_BINARY, orStringN(custom) );
        delim = custom->byteArray;
        if( ! all )
        {
            cp = "\t\n\r ,;";
            while( *cp )
            {
                orSetBit( delim, *cp );
                ++cp;
            }
        }
    }
    else
    {
        custom = 0;
        delim = (uint8_t*) memAlloc(32);
        memCpy( delim, charset_white, 32 );
        orSetBit( delim, ',' );
        orSetBit( delim, ';' );
    }


    assert( a1->series.it <= str->used );

    cp  = str->charArray + a1->series.it;
    end = str->charArray + str->used;
    sstart = cp;

    while( cp != end )
    {
        int c = *cp;
        if( orBitIsSet(delim, c) )
        {
            if( cp > sstart )
            {
                orAppendString( rblk, orMakeCString(sstart, cp-sstart) );
            }
            sstart = cp + 1;
        }
        ++cp;
    }

    if( cp > sstart )
    {
        orAppendString( rblk, orMakeCString(sstart, cp-sstart) );
    }

    if( custom )
    {
        orRelease( customHold );
        orArrayFree( custom );  // Garbage collection will free this later.
    }
    else
    {
        memFree( delim );
    }

    orRelease( rblkHold );
    orResultBLOCK( rblkN );
}


/*==========================================================================*/


#if 0
/**
  Returns index in blk where fval is found or -1 if fval is not found.
*/
static int _findBlock( const OBlock* blk, OIndex pos, const OValue* fval )
{
    const OValue* it  = blk->values + pos;
    const OValue* end = blk->values + blk->used;

    // TODO: If fval is block then all values must match.

    assert( pos <= blk->used );

    while( it != end )
    {
        if( ur_equal(it, fval) )
            return it - blk->values;
        ++it;
    }
    return -1;
}
#endif


/*
   Returns zero if matching rule not found.
*/
static const OValue* _parseBlock( BlockParser* pe, const OValue* rit,
                                 const OValue* rend, int* spos )
{
    const OBlock* cblk;
    const OValue* tval;
    int32_t repMin;
    int32_t repMax;
    OAtom atom;
    OBlock* iblk;
    OIndex pos = *spos;

    iblk = pe->blk;

match:

    while( rit != rend )
    {
        switch( orType(rit) )
        {
            case OT_WORD:
                atom = orAtom(rit);

                if( atom < OT_COUNT )
                {
                    // Datatype
                    if( pos >= pe->input.end )
                        goto failed;
                    tval = iblk->values + pos;
                    if( orType(tval) != atom )
                    {
                        if( atom == OT_NUMBER )
                        {
                            if( orIs(tval,OT_INTEGER) || orIs(tval,OT_DECIMAL) )
                                goto type_matched;
                        }
                        goto failed;
                    }
type_matched:
                    ++rit;
                    ++pos;
                }
                else switch( atom )
                {
                    case OR_ATOM_OPT:
                        ++rit;
                        repMin = 0;
                        repMax = 1;
                        goto repeat;

                    case OR_ATOM_ANY:
                        ++rit;
                        repMin = 0;
                        repMax = 0x7fffffff;
                        goto repeat;

                    case OR_ATOM_SOME:
                        ++rit;
                        repMin = 1;
                        repMax = 0x7fffffff;
                        goto repeat;

                    case OR_ATOM_BREAK:
                        return rit;

                    case OR_ATOM_BAR:
                        goto complete;

                    case OR_ATOM_TO:
                    case OR_ATOM_THRU:
                    {
                        const OValue* ci;
                        const OValue* ce;
                        OAtom ratom = orAtom(rit);

                        ++rit;
                        if( rit == rend )
                            return 0;

                        ci = iblk->values + pos;
                        ce = iblk->values + pe->input.end;

                        if( orIs(rit, OT_WORD) )
                        {
                            if( orAtom(rit) < OT_COUNT )
                            {
                                atom = orAtom(rit);
                                while( ci != ce )
                                {
                                    if( orType(ci) == atom )
                                        break;
                                    ++ci;
                                }
                                if( ci == ce )
                                    goto failed;
                                pos = ci - iblk->values;
                                if( ratom == OR_ATOM_THRU )
                                    ++pos;
                                ++rit;
                                break;
                            }
                            else
                            {
                                orWordVal( rit, cblk, tval );
                            }
                        }
                        else
                        {
                            tval = rit;
                        }


                        if( orIs(tval, OT_BLOCK) )
                        {
                            // TODO: If block then all values must match.
                            orError( PARSE_EX
                                     "to/thru block! not implemented" );
                            pe->error = 1;
                            return 0;
                        }
                        else
                        {
                            while( ci != ce )
                            {
                                if( orEqual(ci, tval) )
                                    break;
                                ++ci;
                            }
                            if( ci == ce )
                                goto failed;
                            pos = ci - iblk->values;
                            if( ratom == OR_ATOM_THRU )
                                ++pos;
                        }
                        ++rit;
                    }
                        break;

                    case OR_ATOM_SKIP:
                        // TODO - int! skip
                        //if( pos >= ser->used )
                        //    return 0;
                        ++rit;
                        ++pos;
                        break;

                    //case OR_ATOM_COPY:

                    default:
                    {
                        orWordVal( rit, cblk, tval );

                        if( orIs(tval, OT_BLOCK) )
                        {
                            goto match_block;
                        }
                        else
                        {
                            orError( PARSE_EX "parse expected block" );
                            pe->error = 1;
                            return 0;
                        }
                    }
                        break;
                }
                break;

            case OT_SETWORD:
            {
                OValue* cell;
                orWordVal( rit, cblk, cell );
                ++rit;

                orSetTF( cell, OT_BLOCK );
                orSetSeries( cell, pe->input.n, pos );
            }
                break;
#if 0
            case OT_GETWORD:
            {
                OValue* cell;
                orWordVal( rit, cblk, cell );
                ++rit;

                if( orIs(cell, OT_SLICE) && (cell->series.n == pe->input.n) )
                    cell->slice.end = pos;
            }
                break;
#endif
            case OT_LITWORD:
                if( pos >= pe->input.end )
                    goto failed;
                tval = iblk->values + pos;
                if( orIs(tval, OT_WORD) && (orAtom(tval) == orAtom(rit)) )
                {
                    ++rit;
                    ++pos;
                }
                else
                    goto failed;
                break;

            case OT_INTEGER:
                repMin = orInt(rit);

                ++rit;
                if( rit == rend )
                    return 0;

                if( orIs(rit, OT_INTEGER) )
                {
                    repMax = orInt(rit);
                    ++rit;
                }
                else
                {
                    repMax = repMin;
                }
                goto repeat;
#if 0
            case OT_DATATYPE:
            {
                OValue* val;

                blk = orBlockPtr(blkN);
                val = blk->values + pos;
                if( orType(val) == ur_datatype(rit) )
                {
                    ++rit;
                    ++pos;
                }
                else
                    goto failed;
            }
                break;
#endif
            case OT_BLOCK:
                tval = rit;
match_block:
                cblk = orBLOCK( tval );
                tval = _parseBlock( pe, cblk->values + tval->series.it,
                                        cblk->values + cblk->used, &pos );
                iblk = pe->blk;
                if( ! tval )
                    goto failed_eval;
                ++rit;
                break;

            case OT_PAREN:
                orEvalBlock( orBlockPtr(rit->series.n), rit->series.it );
                if( orErrorThrown )
                {
                    pe->error = 1;
                    return 0;
                }

                /* Re-aquire pointer & check if input modified. */
                iblk = pe->blk = orBlockPtr( pe->input.n );
                if( iblk->used < pe->input.end )
                    pe->input.end = iblk->used;

                ++rit;
                break;

            default:
                orError( PARSE_EX "invalid parse value" );
                pe->error = 1;
                return 0;
        }
    }

complete:

    *spos = pos;
    return rit;

repeat:

    /* Repeat rit for repMin to repMax times. */

    if( rit == rend )
    {
        orError( PARSE_EX "Enexpected end of parse rule" );
        pe->error = 1;
        return 0;
    }
    else
    {
        int count;

        if( orIs(rit, OT_WORD) )
        {
            orWordVal( rit, cblk, tval );
        }
        else
        {
            tval = rit;
        }

        switch( orType(tval) )
        {
            case OT_DATATYPE:
                // If repMax would pass end of input limit repMax.
                count = pe->input.end - pos;
                if( count < repMax )
                    repMax = count;
                count = 0;

                atom = orDatatype(tval);
                while( count < repMax )
                {
                    tval = iblk->values + pos;
                    if( orType(tval) != atom )     // Test mask?
                        break;
                    ++pos;
                    ++count;
                }
                break;

            case OT_LITWORD:
                // If repMax would pass end of input limit repMax.
                count = pe->input.end - pos;
                if( count < repMax )
                    repMax = count;
                count = 0;

                atom = orAtom(tval);
                while( count < repMax )
                {
                    tval = iblk->values + pos;
                    if( ! orIs(tval, OT_WORD) )
                        break;
                    if( orAtom(tval) != atom )
                        break;
                    ++pos;
                    ++count;
                }
                break;

            case OT_BLOCK:
            {
                const OValue* ci;
                const OValue* ce;

                count = 0;
                cblk = orBLOCK( tval );
                OR_ITER_BLOCK( ci, ce, cblk, tval );

                while( count < repMax )
                {
                    if( pos == pe->input.end )
                        break;
                    if( ! _parseBlock( pe, ci, ce, &pos ) )
                        break;
                    ++count;
                }
                if( pe->error )
                    return 0;
                iblk = pe->blk;
            }
                break;

            default:
                orError( PARSE_EX "Invalid parse rule" );
                pe->error = 1;
                return 0;
        }

        if( count < repMin )
            goto failed;
        ++rit;
    }
    goto match;

failed_eval:

    if( pe->error )
        return 0;

failed:

    rit = nextRule( rit, rend );
    if( rit )
    {
        pos = *spos;
        goto match;
    }
    return 0;
}


/*==========================================================================*/


#define REF_PARSE_ALL   a1+2
#define REF_PARSE_CASE  a1+3

/*
  parse: native [input rules /all /case]
*/
OR_NATIVE_PUB( orParseNative )
{
    OValue* ser   = a1;
    OValue* rules = a1 + 1;

    if( orIs(rules, OT_BLOCK) )
    {
        if( orIs(ser, OT_STRING) )
        {
            StringParser pe;
            OString* str;
            OBlock* blk;
            OValue* rit;
            OValue* rend;

            str = orSTRING(ser);

            pe.rules     = rules;
            pe.input.n   = ser->series.n;
            pe.input.it  = ser->series.it;
            pe.input.end = str->used;
            pe.str       = str;
            pe.error     = 0;
            pe.matchCase = 0;

            blk = orBLOCK(rules);
            OR_ITER_BLOCK( rit, rend, blk, rules );

            rules = (OValue*) _parseStr( &pe, rit, rend, &pe.input.it );
            if( pe.error )
                return;

#if 0
            orSetTF( ser, OT_STRING );
            orSetSeries( ser, pe.input.n, pe.input.it );
#else
            orSetTF( ser, OT_LOGIC );
            orLogic(ser) = (pe.input.it == pe.input.end) ? 1 : 0;
#endif
            return;
        }
        else if( orIs(ser, OT_BLOCK) )
        {
            BlockParser pe;
            OBlock* blk;
            OValue* rit;
            OValue* rend;

            blk = orBLOCK(ser);

            pe.rules     = rules;
            pe.input.n   = ser->series.n;
            pe.input.it  = ser->series.it;
            pe.input.end = blk->used;
            pe.blk       = blk;
            pe.error     = 0;

            blk = orBLOCK(rules);
            OR_ITER_BLOCK( rit, rend, blk, rules );

            rules = (OValue*) _parseBlock( &pe, rit, rend, &pe.input.it );
            if( pe.error )
                return;

#if 0
            orSetTF( ser, OT_BLOCK );
            orSetSeries( ser, pe.input.n, pe.input.it );
#else
            orSetTF( ser, OT_LOGIC );
            orLogic(ser) = (pe.input.it == pe.input.end) ? 1 : 0;
#endif
            return;
        }
    }
    else if( orIs(ser, OT_STRING) )
    {
        _parseStringS( ser, rules,
                       orRefineSet(REF_PARSE_ALL)  ? 1 : 0,
                       orRefineSet(REF_PARSE_CASE) ? 1 : 0 );
        return;
    }

    orError( "parse expected string! argument" );
}


/*EOF*/
