#include <PastisParser.hpp>
#include <iostream>

#include <rang.hpp>

#define TAO_PEGTL_NAMESPACE language
#include <pegtl.hpp>
#include <pegtl/analyze.hpp>

namespace language
{
using namespace language;

// clang-format off

struct comment
    : sor< if_must<
             string< '/', '/' >,
             until< eolf >
             >,
           if_must<
             string< '/', '*' >,
             until< string< '*', '/' > >
             >
           > {};

struct ignored
    : star< sor< space, comment>  >{};

// struct ignored : star< space >{};

struct integer
    : seq< opt< one< '+', '-' > >, plus< digit > >{};
struct INTEGER : seq< integer, ignored >{};

struct real
    : seq< opt< one< '+',
                     '-' >
                >,
           sor< seq<
                  plus< digit >,
                  one < '.' >,
                  star< digit >
                  >,
                seq<
                  one < '.' >,
                  plus< digit >
                  >
                >,
           opt<
             seq<
               one< 'E',
                    'e' >,
               opt< one< '+',
                         '-' >
                    >,
               plus<digit>
               >
             >
           >{};

struct REAL : seq< real, ignored >{};

struct expression : sor< REAL, INTEGER > {};

struct semicol : one< ';' >{};
struct SEMICOL : seq< semicol , ignored > {};

struct instruction
    : sor<seq< expression , SEMICOL>,
          SEMICOL>
{};

struct grammar
    : must<ignored, star<instruction>,eof>{};
// clang-format on

template< typename Rule >
struct my_action : nothing< Rule > {};

template<>
struct my_action< integer >
{
  template< typename Input >
  static void apply( const Input& in, std::string& v )
  {
    if (v.size() > 0) {
      v += std::string(", I:") + in.string();
    } else {
      v = std::string("I:") + in.string();
    }
  }
};

template<>
struct my_action< real >
{
  template< typename Input >
  static void apply( const Input& in, std::string& v )
  {
    if (v.size() > 0) {
      v += std::string(", R:") + in.string();
    } else {
      v = std::string("R:") + in.string();
    }
  }
};


template< typename Rule >
struct errors
    : public normal< Rule >
{
  static const std::string error_message;

  template< typename Input, typename... States >
  static void raise( const Input& in, States&&... /*unused*/ )
  {
    throw parse_error(error_message, std::vector{in.position()} );
  }
};

template <typename Rule>
const std::string errors<Rule>::error_message = "parse error...";

template <>
const std::string errors<eolf>::error_message = "parse error expecting expression";

}

void parser(const std::string& filename) {
    std::string name;

    const size_t grammar_issues = language::analyze< language::grammar >();

    std::cout << "grammar_issues=" << grammar_issues << '\n';
    language::read_input in(filename);
    try {
      language::parse< language::grammar, language::my_action//, language::errors
                       >( in, name );
    }
    catch(const language::parse_error& e) {
      const auto p = e.positions.front();
      std::cerr << rang::style::bold
                << p.source << ':' << p.line << ':' << p.byte_in_line<< ": "
                << rang::style::reset
                << rang::fgB::red << "error: " << rang::fg::reset
                << rang::style::bold << e.what() << rang::style::reset << '\n'
                << in.line_at( p ) << '\n'
                << std::string( p.byte_in_line, ' ' )
                << rang::fgB::yellow << '^' << rang::fg::reset
                << std::endl;
      std::exit(1);
    }

    std::cout << "Good bye, " << name << "!" << std::endl;
    std::exit(0);
}