Listing 2: The source file that implements the scanner class


//
// scanner.cpp
//
// Copyright (C) 1996 by Dan Saks.
// May be copied for private, non-commercial use,
// provided this copyright notice remains intact.
// All other rights reserved.
//
#include <ctype.h>
#include <iostream>
#include <string.h>
#include "scanner.h"
#define DIM(a) (sizeof(a)/sizeof(a[0]))
scanner::pair const scanner::keyword[] =
    {
        { "bool", token::TYPE_KEYWORD },
        { "char", token::TYPE_KEYWORD },
        { "const", token::CONST },
        { "double", token::TYPE_KEYWORD },
        { "float", token::TYPE_KEYWORD },
        { "int", token::TYPE_KEYWORD },
        { "void", token::TYPE_KEYWORD },
        { "volatile", token::VOLATILE },
        { "wchar_t", token::TYPE_KEYWORD }
    };
token scanner::get()
    {
    if (next_token.kind() != token::NO_VALUE)
        {
        current_token = next_token;
        next_token = token();
        }
    else
        {
        previous_token = current_token;
        current_token = scan();
        }
    return current_token;
    }
token scanner::unget()
    {
    next_token = current_token;
    return current_token = previous_token;
    }
token scanner::scan()
    {
    int c;
    token::category kind;
    string text;
    while (isspace(c = in_stream.get()))
        ;
    //
    // scan an integer-literal
    //
    if (isdigit(c))
        {
        kind = token::INT_LITERAL;
        do
            {
            text += char(c);
            c = in_stream.get();
            }
        while (isdigit(c));
        in_stream.putback(c);
        }
    //
    // scan an identifier, a name, or a type-keyword
    //
    else if (isalpha(c) || c == '_')
        {
        kind = token::IDENTIFIER;
        do
            {
            text += char(c);
            c = in_stream.get();
            }
        while (isalnum(c) || c == '_');
        in_stream.putback(c);
        if (isupper(text[0]))
            kind = token::NAME;
        else
            for (int i = 0; i < DIM(keyword); ++i)
                if (text == keyword[i].text)
                    {
                    kind = keyword[i].kind;
                    break;
                    }
        }
    //
    // scan a scope-resolution operator
    //
    else if (c == ':')
        {
        if ((c = in_stream.get()) != ':')
            {
            in_stream.putback(c);
            kind = token::NO_SUCH;
            text = ":";
            }
        else
            {
            kind = token::SCOPE;
            text = "::";
            }
        }
    //
    // scan a single-character operator
    //
    else if (strchr("*&[]();,", c) != 0)
        {
        kind = token::category(c);
        text = char(c);
        }
    //
    // scan end-of-file
    //
    else if (c == EOF)
        kind = token::NO_MORE;
    //
    // there's no such token
    //
    else
        {
        kind = token::NO_SUCH;
        text = char(c);
        }
    return token(kind, text);
    }
const char *image(token::category tc)
    {
    switch (tc)
        {
        case token::AMPERSAND:
            return "&";
        case token::COMMA:
            return ",";
        case token::LEFT_BRACKET:
            return "[";
        case token::LEFT_PAREN:
            return "(";
        case token::RIGHT_BRACKET:
            return "]";
        case token::RIGHT_PAREN:
            return ")";
        case token::SEMICOLON:
            return ";";
        case token::STAR:
            return "*";
        case token::NO_MORE:
            return "end of input";
        case token::SCOPE:
            return "::";
        case token::INT_LITERAL:
            return "int literal";
        case token::IDENTIFIER:
            return "identifier";
        case token::NAME:
            return "name";
        case token::TYPE_KEYWORD:
            return "type keyword";
        case token::CONST:
            return "const";
        case token::VOLATILE:
            return "volatile";
        }
    return "no such token";
    }