//  Copyright (c) 2001-2009 Hartmut Kaiser
//  Copyright (c) 2009 Carl Barron
//
//  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)

#ifndef MATLIB_H_05102009
#define MATLIB_H_05102009
#include <boost/spirit/include/lex.hpp>
#include <vector>
#include <string>

struct set_lexer_state
{
    std::string state;
    set_lexer_state(const std::string &a):state(a){}
    template <class Iterator,class Context>
    void operator () (Iterator const&, Iterator const&
      , BOOST_SCOPED_ENUM(boost::spirit::lex::pass_flags)&, std::size_t
      , Context &ctx) const
    {
        ctx.set_state_name(state.c_str());
    }
};

#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable: 4512) // assignment operator could not be generated.
#endif
struct store_double
{
    std::vector<double> &out;
    store_double(std::vector<double> &a):out(a){}
    template <class Iterator,class LexerContext>
    void operator () (Iterator const& start, Iterator const& end
      , BOOST_SCOPED_ENUM(boost::spirit::lex::pass_flags)&, std::size_t
      , LexerContext &) const
    {
        std::string work(start, end);
        out.push_back(std::atof(work.c_str()));
    }
};

struct add_row
{
    std::vector<std::vector<double> > &matrix;
    std::vector<double> &row;

    add_row(std::vector<std::vector<double> > &a,std::vector<double> &b)
        :matrix(a),row(b) {}
    template <class Iterator,class Context>
    void operator () (Iterator const&, Iterator const&
      , BOOST_SCOPED_ENUM(boost::spirit::lex::pass_flags)&, std::size_t
      , Context &ctx) const
    {
        matrix.push_back(std::vector<double>());
        matrix.back().swap(row);
        ctx.set_state_name("A");
    }
};

template <class Lexer>
struct matlib_tokens : boost::spirit::lex::lexer<Lexer>
{
    matlib_tokens(std::vector<std::vector<double> > &a)
      : matrix(a)
    {
        typedef boost::spirit::lex::token_def<> token_def_;

        this->self.add_pattern("REAL1", "[0-9]+(\\.[0-9]*)?");
        this->self.add_pattern("REAL2", "\\.[0-9]+");

        number = "[-+]?({REAL1}|{REAL2})([eE][-+]?[0-9]+)?";

        this->self
            =   token_def_('[') [set_lexer_state("A")]
            ;

        this->self("A")
            =   token_def_('[') [set_lexer_state("B")]
            |   ','
            |   token_def_(']') [set_lexer_state("INITIAL")]
            ;

        this->self("B")
            =   number [store_double(row)]
            |   ','
            |   token_def_(']') [add_row(matrix,row)]
            ;
    }

    boost::spirit::lex::token_def<> number;
    std::vector<std::vector<double> > &matrix;
    std::vector<double> row;
};
#ifdef _MSC_VER
#  pragma warning(pop)
#endif

#endif

