/*****************************************************************************/
/*                                                                           */
/*                                 scanner.c                                 */
/*                                                                           */
/*                      Source file line info structure                      */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 1998-2010, Ullrich von Bassewitz                                      */
/*                Roemerstrasse 52                                           */
/*                D-70794 Filderstadt                                        */
/* EMail:         uz@cc65.org                                                */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>

/* common */
#include "chartype.h"
#include "fp.h"
#include "tgttrans.h"

/* cc65 */
#include "datatype.h"
#include "error.h"
#include "function.h"
#include "global.h"
#include "hexval.h"
#include "ident.h"
#include "input.h"
#include "litpool.h"
#include "preproc.h"
#include "scanner.h"
#include "standard.h"
#include "symtab.h"



/*****************************************************************************/
/*                                   data                                    */
/*****************************************************************************/



Token CurTok;           /* The current token */
Token NextTok;          /* The next token */



/* Token types */
enum {
    TT_C89      = 0x01 << STD_C89,      /* Token valid in C89 */
    TT_C99      = 0x01 << STD_C99,      /* Token valid in C99 */
    TT_CC65     = 0x01 << STD_CC65      /* Token valid in cc65 */
};

/* Token table */
static const struct Keyword {
    char*           Key;        /* Keyword name */
    unsigned char   Tok;        /* The token */
    unsigned char   Std;        /* Token supported in which standards? */
} Keywords [] = {
    { "_Pragma",        TOK_PRAGMA,     TT_C89 | TT_C99 | TT_CC65  },   /* !! */
    { "__AX__",         TOK_AX,         TT_C89 | TT_C99 | TT_CC65  },
    { "__A__",          TOK_A,          TT_C89 | TT_C99 | TT_CC65  },
    { "__EAX__",        TOK_EAX,        TT_C89 | TT_C99 | TT_CC65  },
    { "__X__",          TOK_X,          TT_C89 | TT_C99 | TT_CC65  },
    { "__Y__",          TOK_Y,          TT_C89 | TT_C99 | TT_CC65  },
    { "__asm__",        TOK_ASM,        TT_C89 | TT_C99 | TT_CC65  },
    { "__attribute__",  TOK_ATTRIBUTE,  TT_C89 | TT_C99 | TT_CC65  },
    { "__cdecl__",      TOK_CDECL,      TT_C89 | TT_C99 | TT_CC65  },
    { "__far__",        TOK_FAR,        TT_C89 | TT_C99 | TT_CC65  },
    { "__fastcall__",   TOK_FASTCALL,   TT_C89 | TT_C99 | TT_CC65  },
    { "__inline__",     TOK_INLINE,     TT_C89 | TT_C99 | TT_CC65  },
    { "__near__",       TOK_NEAR,       TT_C89 | TT_C99 | TT_CC65  },
    { "asm",            TOK_ASM,                          TT_CC65  },
    { "auto",           TOK_AUTO,       TT_C89 | TT_C99 | TT_CC65  },
    { "break",          TOK_BREAK,      TT_C89 | TT_C99 | TT_CC65  },
    { "case",           TOK_CASE,       TT_C89 | TT_C99 | TT_CC65  },
    { "cdecl",          TOK_CDECL,                        TT_CC65  },
    { "char",           TOK_CHAR,       TT_C89 | TT_C99 | TT_CC65  },
    { "const",          TOK_CONST,      TT_C89 | TT_C99 | TT_CC65  },
    { "continue",       TOK_CONTINUE,   TT_C89 | TT_C99 | TT_CC65  },
    { "default",        TOK_DEFAULT,    TT_C89 | TT_C99 | TT_CC65  },
    { "do",             TOK_DO,         TT_C89 | TT_C99 | TT_CC65  },
    { "double",         TOK_DOUBLE,     TT_C89 | TT_C99 | TT_CC65  },
    { "else",           TOK_ELSE,       TT_C89 | TT_C99 | TT_CC65  },
    { "enum",           TOK_ENUM,       TT_C89 | TT_C99 | TT_CC65  },
    { "extern",         TOK_EXTERN,     TT_C89 | TT_C99 | TT_CC65  },
    { "far",            TOK_FAR,                          TT_CC65  },
    { "fastcall",       TOK_FASTCALL,                     TT_CC65  },
    { "float",          TOK_FLOAT,      TT_C89 | TT_C99 | TT_CC65  },
    { "for",            TOK_FOR,        TT_C89 | TT_C99 | TT_CC65  },
    { "goto",           TOK_GOTO,       TT_C89 | TT_C99 | TT_CC65  },
    { "if",             TOK_IF,         TT_C89 | TT_C99 | TT_CC65  },
    { "inline",         TOK_INLINE,              TT_C99 | TT_CC65  },
    { "int",            TOK_INT,        TT_C89 | TT_C99 | TT_CC65  },
    { "long",           TOK_LONG,       TT_C89 | TT_C99 | TT_CC65  },
    { "near",           TOK_NEAR,                         TT_CC65  },
    { "register",       TOK_REGISTER,   TT_C89 | TT_C99 | TT_CC65  },
    { "restrict",       TOK_RESTRICT,            TT_C99 | TT_CC65  },
    { "return",         TOK_RETURN,     TT_C89 | TT_C99 | TT_CC65  },
    { "short",          TOK_SHORT,      TT_C89 | TT_C99 | TT_CC65  },
    { "signed",         TOK_SIGNED,     TT_C89 | TT_C99 | TT_CC65  },
    { "sizeof",         TOK_SIZEOF,     TT_C89 | TT_C99 | TT_CC65  },
    { "static",         TOK_STATIC,     TT_C89 | TT_C99 | TT_CC65  },
    { "struct",         TOK_STRUCT,     TT_C89 | TT_C99 | TT_CC65  },
    { "switch",         TOK_SWITCH,     TT_C89 | TT_C99 | TT_CC65  },
    { "typedef",        TOK_TYPEDEF,    TT_C89 | TT_C99 | TT_CC65  },
    { "union",          TOK_UNION,      TT_C89 | TT_C99 | TT_CC65  },
    { "unsigned",       TOK_UNSIGNED,   TT_C89 | TT_C99 | TT_CC65  },
    { "void",           TOK_VOID,       TT_C89 | TT_C99 | TT_CC65  },
    { "volatile",       TOK_VOLATILE,   TT_C89 | TT_C99 | TT_CC65  },
    { "while",          TOK_WHILE,      TT_C89 | TT_C99 | TT_CC65  },
};
#define KEY_COUNT       (sizeof (Keywords) / sizeof (Keywords [0]))



/* Stuff for determining the type of an integer constant */
#define IT_INT          0x01
#define IT_UINT         0x02
#define IT_LONG         0x04
#define IT_ULONG        0x08



/*****************************************************************************/
/*                                   code                                    */
/*****************************************************************************/



static int CmpKey (const void* Key, const void* Elem)
/* Compare function for bsearch */
{
    return strcmp ((const char*) Key, ((const struct Keyword*) Elem)->Key);
}



static token_t FindKey (const char* Key)
/* Find a keyword and return the token. Return IDENT if the token is not a
** keyword.
*/
{
    struct Keyword* K;
    K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey);
    if (K && (K->Std & (0x01 << IS_Get (&Standard))) != 0) {
        return K->Tok;
    } else {
        return TOK_IDENT;
    }
}



static int SkipWhite (void)
/* Skip white space in the input stream, reading and preprocessing new lines
** if necessary. Return 0 if end of file is reached, return 1 otherwise.
*/
{
    while (1) {
        while (CurC == '\0') {
            if (NextLine () == 0) {
                return 0;
            }
            Preprocess ();
        }
        if (IsSpace (CurC)) {
            NextChar ();
        } else {
            return 1;
        }
    }
}



int TokIsFuncSpec (const Token* T)
/* Return true if the token is a function specifier */
{
    return (T->Tok == TOK_INLINE)   ||
           (T->Tok == TOK_FASTCALL) || (T->Tok == TOK_CDECL) ||
           (T->Tok == TOK_NEAR)     || (T->Tok == TOK_FAR);
}



void SymName (char* S)
/* Read a symbol from the input stream. The first character must have been
** checked before calling this function. The buffer is expected to be at
** least of size MAX_IDENTLEN+1.
*/
{
    unsigned Len = 0;
    do {
        if (Len < MAX_IDENTLEN) {
            ++Len;
            *S++ = CurC;
        }
        NextChar ();
    } while (IsIdent (CurC) || IsDigit (CurC));
    *S = '\0';
}



int IsSym (char* S)
/* If a symbol follows, read it and return 1, otherwise return 0 */
{
    if (IsIdent (CurC)) {
        SymName (S);
        return 1;
    } else {
        return 0;
    }
}



static void UnknownChar (char C)
/* Error message for unknown character */
{
    Error ("Invalid input character with code %02X", C & 0xFF);
    NextChar ();                        /* Skip */
}



static void SetTok (int tok)
/* Set NextTok.Tok and bump line ptr */
{
    NextTok.Tok = tok;
    NextChar ();
}



static int ParseChar (void)
/* Parse a character. Converts escape chars into character codes. */
{
    int C;
    int HadError;
    int Count;

    /* Check for escape chars */
    if (CurC == '\\') {
        NextChar ();
        switch (CurC) {
            case '?':
                C = '\?';
                break;
            case 'a':
                C = '\a';
                break;
            case 'b':
                C = '\b';
                break;
            case 'f':
                C = '\f';
                break;
            case 'r':
                C = '\r';
                break;
            case 'n':
                C = '\n';
                break;
            case 't':
                C = '\t';
                break;
            case 'v':
                C = '\v';
                break;
            case '\"':
                C = '\"';
                break;
            case '\'':
                C = '\'';
                break;
            case '\\':
                C = '\\';
                break;
            case 'x':
            case 'X':
                /* Hex character constant */
                if (!IsXDigit (NextC)) {
                    Error ("\\x used with no following hex digits");
                    C = ' ';
                } else {
                    HadError = 0;
                    C = 0;
                    while (IsXDigit (NextC)) {
                        if ((C << 4) >= 256) {
                            if (!HadError) {
                                Error ("Hex character constant out of range");
                                HadError = 1;
                            }
                        } else {
                            C = (C << 4) | HexVal (NextC);
                        }
                        NextChar ();
                    }
                }
                break;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
                /* Octal constant */
                Count = 1;
                C = HexVal (CurC);
                while (IsODigit (NextC) && Count++ < 3) {
                    C = (C << 3) | HexVal (NextC);
                    NextChar ();
                }
                if (C >= 256)
                    Error ("Octal character constant out of range");
                break;
            default:
                C = CurC;
                Error ("Illegal escaped character: 0x%02X", CurC);
                break;
        }
    } else {
        C = CurC;
    }

    /* Skip the character read */
    NextChar ();

    /* Do correct sign extension */
    return SignExtendChar (C);
}



static void CharConst (void)
/* Parse a character constant. */
{
    int C;

    /* Skip the quote */
    NextChar ();

    /* Get character */
    C = ParseChar ();

    /* Check for closing quote */
    if (CurC != '\'') {
        Error ("'\'' expected");
    } else {
        /* Skip the quote */
        NextChar ();
    }

    /* Setup values and attributes */
    NextTok.Tok  = TOK_CCONST;

    /* Translate into target charset */
    NextTok.IVal = SignExtendChar (TgtTranslateChar (C));

    /* Character constants have type int */
    NextTok.Type = type_int;
}



static void StringConst (void)
/* Parse a quoted string */
{
    /* String buffer */
    StrBuf S = AUTO_STRBUF_INITIALIZER;

    /* Assume next token is a string constant */
    NextTok.Tok  = TOK_SCONST;

    /* Concatenate strings. If at least one of the concenated strings is a wide
    ** character literal, the whole string is a wide char literal, otherwise
    ** it's a normal string literal.
    */
    while (1) {

        /* Check if this is a normal or a wide char string */
        if (CurC == 'L' && NextC == '\"') {
            /* Wide character literal */
            NextTok.Tok = TOK_WCSCONST;
            NextChar ();
            NextChar ();
        } else if (CurC == '\"') {
            /* Skip the quote char */
            NextChar ();
        } else {
            /* No string */
            break;
        }

        /* Read until end of string */
        while (CurC != '\"') {
            if (CurC == '\0') {
                Error ("Unexpected newline");
                break;
            }
            SB_AppendChar (&S, ParseChar ());
        }

        /* Skip closing quote char if there was one */
        NextChar ();

        /* Skip white space, read new input */
        SkipWhite ();

    }

    /* Terminate the string */
    SB_AppendChar (&S, '\0');

    /* Add the whole string to the literal pool */
    NextTok.SVal = AddLiteralStr (&S);

    /* Free the buffer */
    SB_Done (&S);
}



static void NumericConst (void)
/* Parse a numeric constant */
{
    unsigned Base;              /* Temporary number base */
    unsigned Prefix;            /* Base according to prefix */
    StrBuf   S = STATIC_STRBUF_INITIALIZER;
    int      IsFloat;
    char     C;
    unsigned DigitVal;
    unsigned long IVal;         /* Value */

    /* Check for a leading hex, octal or binary prefix and determine the
    ** possible integer types.
    */
    if (CurC == '0') {
        /* Gobble 0 and examine next char */
        NextChar ();
        if (toupper (CurC) == 'X') {
            Base = Prefix = 16;
            NextChar ();        /* gobble "x" */
        } else if (toupper (CurC) == 'B' && IS_Get (&Standard) >= STD_CC65) {
            Base = Prefix = 2;
            NextChar ();        /* gobble 'b' */
        } else {
            Base = 10;          /* Assume 10 for now - see below */
            Prefix = 8;         /* Actual prefix says octal */
        }
    } else {
        Base  = Prefix = 10;
    }

    /* Because floating point numbers don't have octal prefixes (a number
    ** with a leading zero is decimal), we first have to read the number
    ** before converting it, so we can determine if it's a float or an
    ** integer.
    */
    while (IsXDigit (CurC) && HexVal (CurC) < Base) {
        SB_AppendChar (&S, CurC);
        NextChar ();
    }
    SB_Terminate (&S);

    /* The following character tells us if we have an integer or floating
    ** point constant. Note: Hexadecimal floating point constants aren't
    ** supported in C89.
    */
    IsFloat = (CurC == '.' ||
               (Base == 10 && toupper (CurC) == 'E') ||
               (Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99));

    /* If we don't have a floating point type, an octal prefix results in an
    ** octal base.
    */
    if (!IsFloat && Prefix == 8) {
        Base = 8;
    }

    /* Since we do now know the correct base, convert the remembered input
    ** into a number.
    */
    SB_Reset (&S);
    IVal = 0;
    while ((C = SB_Get (&S)) != '\0') {
        DigitVal = HexVal (C);
        if (DigitVal >= Base) {
            Error ("Numeric constant contains digits beyond the radix");
        }
        IVal = (IVal * Base) + DigitVal;
    }

    /* We don't need the string buffer any longer */
    SB_Done (&S);

    /* Distinguish between integer and floating point constants */
    if (!IsFloat) {

        unsigned Types;
        int      HaveSuffix;

        /* Check for a suffix and determine the possible types */
        HaveSuffix = 1;
        if (toupper (CurC) == 'U') {
            /* Unsigned type */
            NextChar ();
            if (toupper (CurC) != 'L') {
                Types = IT_UINT | IT_ULONG;
            } else {
                NextChar ();
                Types = IT_ULONG;
            }
        } else if (toupper (CurC) == 'L') {
            /* Long type */
            NextChar ();
            if (toupper (CurC) != 'U') {
                Types = IT_LONG | IT_ULONG;
            } else {
                NextChar ();
                Types = IT_ULONG;
            }
        } else {
            HaveSuffix = 0;
            if (Prefix == 10) {
                /* Decimal constants are of any type but uint */
                Types = IT_INT | IT_LONG | IT_ULONG;
            } else {
                /* Octal or hex constants are of any type */
                Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG;
            }
        }

        /* Check the range to determine the type */
        if (IVal > 0x7FFF) {
            /* Out of range for int */
            Types &= ~IT_INT;
            /* If the value is in the range 0x8000..0xFFFF, unsigned int is not
            ** allowed, and we don't have a type specifying suffix, emit a
            ** warning, because the constant is of type long.
            */
            if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) {
                Warning ("Constant is long");
            }
        }
        if (IVal > 0xFFFF) {
            /* Out of range for unsigned int */
            Types &= ~IT_UINT;
        }
        if (IVal > 0x7FFFFFFF) {
            /* Out of range for long int */
            Types &= ~IT_LONG;
        }

        /* Now set the type string to the smallest type in types */
        if (Types & IT_INT) {
            NextTok.Type = type_int;
        } else if (Types & IT_UINT) {
            NextTok.Type = type_uint;
        } else if (Types & IT_LONG) {
            NextTok.Type = type_long;
        } else {
            NextTok.Type = type_ulong;
        }

        /* Set the value and the token */
        NextTok.IVal = IVal;
        NextTok.Tok  = TOK_ICONST;

    } else {

        /* Float constant */
        Double FVal = FP_D_FromInt (IVal);      /* Convert to double */

        /* Check for a fractional part and read it */
        if (CurC == '.') {

            Double Scale;

            /* Skip the dot */
            NextChar ();

            /* Read fractional digits */
            Scale  = FP_D_Make (1.0);
            while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) {
                /* Get the value of this digit */
                Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale);
                /* Add it to the float value */
                FVal = FP_D_Add (FVal, FracVal);
                /* Scale base */
                Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal));
                /* Skip the digit */
                NextChar ();
            }
        }

        /* Check for an exponent and read it */
        if ((Base == 16 && toupper (CurC) == 'F') ||
            (Base == 10 && toupper (CurC) == 'E')) {

            unsigned Digits;
            unsigned Exp;

            /* Skip the exponent notifier */
            NextChar ();

            /* Read an optional sign */
            if (CurC == '-') {
                NextChar ();
            } else if (CurC == '+') {
                NextChar ();
            }

            /* Read exponent digits. Since we support only 32 bit floats
            ** with a maximum exponent of +-/127, we read the exponent
            ** part as integer with up to 3 digits and drop the remainder.
            ** This avoids an overflow of Exp. The exponent is always
            ** decimal, even for hex float consts.
            */
            Digits = 0;
            Exp    = 0;
            while (IsDigit (CurC)) {
                if (++Digits <= 3) {
                    Exp = Exp * 10 + HexVal (CurC);
                }
                NextChar ();
            }

            /* Check for errors: We must have exponent digits, and not more
            ** than three.
            */
            if (Digits == 0) {
                Error ("Floating constant exponent has no digits");
            } else if (Digits > 3) {
                Warning ("Floating constant exponent is too large");
            }

            /* Scale the exponent and adjust the value accordingly */
            if (Exp) {
                FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp)));
            }
        }

        /* Check for a suffix and determine the type of the constant */
        if (toupper (CurC) == 'F') {
            NextChar ();
            NextTok.Type = type_float;
        } else {
            NextTok.Type = type_double;
        }

        /* Set the value and the token */
        NextTok.FVal = FVal;
        NextTok.Tok  = TOK_FCONST;

    }
}



void NextToken (void)
/* Get next token from input stream */
{
    ident token;

    /* We have to skip white space here before shifting tokens, since the
    ** tokens and the current line info is invalid at startup and will get
    ** initialized by reading the first time from the file. Remember if
    ** we were at end of input and handle that later.
    */
    int GotEOF = (SkipWhite() == 0);

    /* Current token is the lookahead token */
    if (CurTok.LI) {
        ReleaseLineInfo (CurTok.LI);
    }
    CurTok = NextTok;

    /* When reading the first time from the file, the line info in NextTok,
    ** which was copied to CurTok is invalid. Since the information from
    ** the token is used for error messages, we must make it valid.
    */
    if (CurTok.LI == 0) {
        CurTok.LI = UseLineInfo (GetCurLineInfo ());
    }

    /* Remember the starting position of the next token */
    NextTok.LI = UseLineInfo (GetCurLineInfo ());

    /* Now handle end of input. */
    if (GotEOF) {
        /* End of file reached */
        NextTok.Tok = TOK_CEOF;
        return;
    }

    /* Determine the next token from the lookahead */
    if (IsDigit (CurC) || (CurC == '.' && IsDigit (NextC))) {
        /* A number */
        NumericConst ();
        return;
    }

    /* Check for wide character literals */
    if (CurC == 'L' && NextC == '\"') {
        StringConst ();
        return;
    }

    /* Check for keywords and identifiers */
    if (IsSym (token)) {

        /* Check for a keyword */
        if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
            /* Reserved word found */
            return;
        }
        /* No reserved word, check for special symbols */
        if (token[0] == '_' && token[1] == '_') {
            /* Special symbols */
            if (strcmp (token+2, "FILE__") == 0) {
                NextTok.SVal = AddLiteral (GetCurrentFile());
                NextTok.Tok  = TOK_SCONST;
                return;
            } else if (strcmp (token+2, "LINE__") == 0) {
                NextTok.Tok  = TOK_ICONST;
                NextTok.IVal = GetCurrentLine();
                NextTok.Type = type_int;
                return;
            } else if (strcmp (token+2, "func__") == 0) {
                /* __func__ is only defined in functions */
                if (CurrentFunc) {
                    NextTok.SVal = AddLiteral (F_GetFuncName (CurrentFunc));
                    NextTok.Tok  = TOK_SCONST;
                    return;
                }
            }
        }

        /* No reserved word but identifier */
        strcpy (NextTok.Ident, token);
        NextTok.Tok = TOK_IDENT;
        return;
    }

    /* Monstrous switch statement ahead... */
    switch (CurC) {

        case '!':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_NE);
            } else {
                NextTok.Tok = TOK_BOOL_NOT;
            }
            break;

        case '\"':
            StringConst ();
            break;

        case '%':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_MOD_ASSIGN);
            } else {
                NextTok.Tok = TOK_MOD;
            }
            break;

        case '&':
            NextChar ();
            switch (CurC) {
                case '&':
                    SetTok (TOK_BOOL_AND);
                    break;
                case '=':
                    SetTok (TOK_AND_ASSIGN);
                    break;
                default:
                    NextTok.Tok = TOK_AND;
            }
            break;

        case '\'':
            CharConst ();
            break;

        case '(':
            SetTok (TOK_LPAREN);
            break;

        case ')':
            SetTok (TOK_RPAREN);
            break;

        case '*':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_MUL_ASSIGN);
            } else {
                NextTok.Tok = TOK_STAR;
            }
            break;

        case '+':
            NextChar ();
            switch (CurC) {
                case '+':
                    SetTok (TOK_INC);
                    break;
                case '=':
                    SetTok (TOK_PLUS_ASSIGN);
                    break;
                default:
                    NextTok.Tok = TOK_PLUS;
            }
            break;

        case ',':
            SetTok (TOK_COMMA);
            break;

        case '-':
            NextChar ();
            switch (CurC) {
                case '-':
                    SetTok (TOK_DEC);
                    break;
                case '=':
                    SetTok (TOK_MINUS_ASSIGN);
                    break;
                case '>':
                    SetTok (TOK_PTR_REF);
                    break;
                default:
                    NextTok.Tok = TOK_MINUS;
            }
            break;

        case '.':
            NextChar ();
            if (CurC == '.') {
                NextChar ();
                if (CurC == '.') {
                    SetTok (TOK_ELLIPSIS);
                } else {
                    UnknownChar (CurC);
                }
            } else {
                NextTok.Tok = TOK_DOT;
            }
            break;

        case '/':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_DIV_ASSIGN);
            } else {
                NextTok.Tok = TOK_DIV;
            }
            break;

        case ':':
            SetTok (TOK_COLON);
            break;

        case ';':
            SetTok (TOK_SEMI);
            break;

        case '<':
            NextChar ();
            switch (CurC) {
                case '=':
                    SetTok (TOK_LE);
                    break;
                case '<':
                    NextChar ();
                    if (CurC == '=') {
                        SetTok (TOK_SHL_ASSIGN);
                    } else {
                        NextTok.Tok = TOK_SHL;
                    }
                    break;
                default:
                    NextTok.Tok = TOK_LT;
            }
            break;

        case '=':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_EQ);
            } else {
                NextTok.Tok = TOK_ASSIGN;
            }
            break;

        case '>':
            NextChar ();
            switch (CurC) {
                case '=':
                    SetTok (TOK_GE);
                    break;
                case '>':
                    NextChar ();
                    if (CurC == '=') {
                        SetTok (TOK_SHR_ASSIGN);
                    } else {
                        NextTok.Tok = TOK_SHR;
                    }
                    break;
                default:
                    NextTok.Tok = TOK_GT;
            }
            break;

        case '?':
            SetTok (TOK_QUEST);
            break;

        case '[':
            SetTok (TOK_LBRACK);
            break;

        case ']':
            SetTok (TOK_RBRACK);
            break;

        case '^':
            NextChar ();
            if (CurC == '=') {
                SetTok (TOK_XOR_ASSIGN);
            } else {
                NextTok.Tok = TOK_XOR;
            }
            break;

        case '{':
            SetTok (TOK_LCURLY);
            break;

        case '|':
            NextChar ();
            switch (CurC) {
                case '|':
                    SetTok (TOK_BOOL_OR);
                    break;
                case '=':
                    SetTok (TOK_OR_ASSIGN);
                    break;
                default:
                    NextTok.Tok = TOK_OR;
            }
            break;

        case '}':
            SetTok (TOK_RCURLY);
            break;

        case '~':
            SetTok (TOK_COMP);
            break;

        default:
            UnknownChar (CurC);

    }

}



void SkipTokens (const token_t* TokenList, unsigned TokenCount)
/* Skip tokens until we reach TOK_CEOF or a token in the given token list.
** This routine is used for error recovery.
*/
{
    while (CurTok.Tok != TOK_CEOF) {

        /* Check if the current token is in the token list */
        unsigned I;
        for (I = 0; I < TokenCount; ++I) {
            if (CurTok.Tok == TokenList[I]) {
                /* Found a token in the list */
                return;
            }
        }

        /* Not in the list: Skip it */
        NextToken ();

    }
}



int Consume (token_t Token, const char* ErrorMsg)
/* Eat token if it is the next in the input stream, otherwise print an error
** message. Returns true if the token was found and false otherwise.
*/
{
    if (CurTok.Tok == Token) {
        NextToken ();
        return 1;
    } else {
        Error ("%s", ErrorMsg);
        return 0;
    }
}



int ConsumeColon (void)
/* Check for a colon and skip it. */
{
    return Consume (TOK_COLON, "':' expected");
}



int ConsumeSemi (void)
/* Check for a semicolon and skip it. */
{
    /* Try do be smart about typos... */
    if (CurTok.Tok == TOK_SEMI) {
        NextToken ();
        return 1;
    } else {
        Error ("';' expected");
        if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
            NextToken ();
        }
        return 0;
    }
}



int ConsumeComma (void)
/* Check for a comma and skip it. */
{
    /* Try do be smart about typos... */
    if (CurTok.Tok == TOK_COMMA) {
        NextToken ();
        return 1;
    } else {
        Error ("',' expected");
        if (CurTok.Tok == TOK_SEMI) {
            NextToken ();
        }
        return 0;
    }
}



int ConsumeLParen (void)
/* Check for a left parenthesis and skip it */
{
    return Consume (TOK_LPAREN, "'(' expected");
}



int ConsumeRParen (void)
/* Check for a right parenthesis and skip it */
{
    return Consume (TOK_RPAREN, "')' expected");
}



int ConsumeLBrack (void)
/* Check for a left bracket and skip it */
{
    return Consume (TOK_LBRACK, "'[' expected");
}



int ConsumeRBrack (void)
/* Check for a right bracket and skip it */
{
    return Consume (TOK_RBRACK, "']' expected");
}



int ConsumeLCurly (void)
/* Check for a left curly brace and skip it */
{
    return Consume (TOK_LCURLY, "'{' expected");
}



int ConsumeRCurly (void)
/* Check for a right curly brace and skip it */
{
    return Consume (TOK_RCURLY, "'}' expected");
}
