/*=============================================================================
    Boost.Wave: A Standard compliant C++ preprocessor library
    
    Sample: Print out the preprocessed tokens returned by the Wave iterator

    This sample shows, how it is possible to use a custom lexer type and a 
    custom token type with the Wave library. 
    
    http://www.boost.org/

    Copyright (c) 2001-2007 Hartmut Kaiser. Distributed under the Boost 
    Software License, Version 1.0. (See accompanying file 
    LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

#include "cpp_tokens.hpp"                  // global configuration

///////////////////////////////////////////////////////////////////////////////
//  Include Wave itself
#include <boost/wave.hpp>

///////////////////////////////////////////////////////////////////////////////
//  The following files contain the custom lexer type to use
#include "slex_token.hpp"
#include "slex_iterator.hpp"

///////////////////////////////////////////////////////////////////////////////
//  include lexer specifics, import lexer names
#if !defined(BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION)
#include "slex/cpp_slex_lexer.hpp"
#endif // !defined(BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION)

///////////////////////////////////////////////////////////////////////////////
//  import required names
using namespace boost::spirit;

using std::string;
using std::getline;
using std::ifstream;
using std::cout;
using std::cerr;
using std::endl;
using std::ostream;

///////////////////////////////////////////////////////////////////////////////
//  main program
int
main(int argc, char *argv[])
{
    if (2 != argc) {
        cout << "Usage: cpp_tokens input_file" << endl;
        return 1;
    }

// read the file to analyse into a std::string     
    ifstream infile(argv[1]);
    string teststr;
    if (infile.is_open()) {
        infile.unsetf(std::ios::skipws);
        string line;
        for (getline(infile, line); infile.good(); getline(infile, line)) {
            teststr += line;
            teststr += '\n';
        }
    }
    else {
        teststr = argv[1];
    }

//  The following typedef does the trick. It defines the context type to use, 
//  which depends on the lexer type (provided by the second template 
//  parameter). Our lexer type 'slex_iterator<>' depends on a custom token type
//  'slex_token<>'. Our custom token type differs from the original one provided 
//  by the Wave library only by defining an additional operator<<(), which is 
//  used to dump the token information carried by a given token (see loop 
//  below).
    typedef boost::wave::cpplexer::slex_token<> token_type;
    typedef boost::wave::cpplexer::slex::slex_iterator<token_type> lexer_type;
    typedef boost::wave::context<std::string::iterator, lexer_type> 
        context_type;

// The C++ preprocessor iterator shouldn't be constructed directly. It is to be
// generated through a boost::wave::context<> object. This object is 
// additionally to be used to initialize and define different parameters of 
// the actual preprocessing.
// The preprocessing of the input stream is done on the fly behind the scenes
// during iteration over the context_type::iterator_type stream.
    context_type ctx (teststr.begin(), teststr.end(), argv[1]);

    ctx.set_language(boost::wave::enable_long_long(ctx.get_language()));
    ctx.set_language(boost::wave::enable_preserve_comments(ctx.get_language()));
    ctx.set_language(boost::wave::enable_prefer_pp_numbers(ctx.get_language()));

    context_type::iterator_type first = ctx.begin();
    context_type::iterator_type last = ctx.end();
    context_type::token_type current_token;

    try {
    //  Traverse over the tokens generated from the input and dump the token
    //  contents.
        while (first != last) {
        // retrieve next token
            current_token = *first;
        
        // output token info
            cout << "matched " << current_token << endl;
            ++first;
        }
    }
    catch (boost::wave::cpp_exception const& e) {
    // some preprocessing error
        cerr 
            << e.file_name() << "(" << e.line_no() << "): "
            << e.description() << endl;
        return 2;
    }
    catch (std::exception const& e) {
    // use last recognized token to retrieve the error position
        cerr 
            << current_token.get_position().get_file() 
            << "(" << current_token.get_position().get_line() << "): "
            << "unexpected exception: " << e.what()
            << endl;
        return 3;
    }
    catch (...) {
    // use last recognized token to retrieve the error position
        cerr 
            << current_token.get_position().get_file() 
            << "(" << current_token.get_position().get_line() << "): "
            << "unexpected exception." << endl;
        return 4;
    }
    return 0;
}
