#ifndef PEG_GRAMMAR_HPP
#define PEG_GRAMMAR_HPP

#include <pegtl.hpp>

using namespace TAO_PEGTL_NAMESPACE;

namespace language
{
// clang-format off

struct slashslash : TAO_PEGTL_STRING("//") {};
struct slashstar  : TAO_PEGTL_STRING("/*") {};
struct starslash  : TAO_PEGTL_STRING("*/") {};

struct comment
    : sor< if_must< slashslash, until< eolf > >,
           try_catch< slashstar, until< starslash> >,
           // error management
           if_must<at<slashstar>,raise<slashstar>, until< eof> > > {};

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

struct integer
    :  plus< digit > {};
struct INTEGER : seq< integer, ignored >{};

struct exponent
    : seq< one< 'E', 'e' >,
           opt< one< '+', '-' > >,
           plus<digit>
         >{};

struct real
    : sor< seq< sor< seq<
                       plus< digit >,
                       one < '.' >,
                       star< digit >
                       >,
                     seq<
                       one < '.' >,
                       plus< digit >
                       >
                     >,
                opt< exponent >
                >,
           seq< plus< digit, exponent > >
         >{};

struct escaped_c : one< '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' > {};
struct character : if_must_else< one< '\\' >, escaped_c, ascii::any> {};

struct literal : if_must< one< '"' >, until< one< '"' >, character > > {};

struct import_kw :  TAO_PEGTL_KEYWORD("import") {};

struct LITERAL : seq< literal, ignored >{};

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

struct B_set : TAO_PEGTL_KEYWORD("B"){};
struct N_set : TAO_PEGTL_KEYWORD("N"){};
struct Z_set : TAO_PEGTL_KEYWORD("Z"){};
struct R_set : TAO_PEGTL_KEYWORD("R"){};

struct string_type : TAO_PEGTL_KEYWORD("string") {};

struct scalar_type : sor< B_set, R_set, Z_set, N_set >{};

struct vector_type : seq< R_set, ignored, one< '^' >, ignored, integer >{};

struct basic_type : sor< scalar_type, string_type >{};

struct type_specifier : sor< vector_type, basic_type >{};

struct TYPE_SPECIFIER : seq< type_specifier, ignored >{};

struct type_expression : list_must< TYPE_SPECIFIER, seq< one< '*' > > >{};

struct TYPE_EXPRESSION : seq< type_expression, ignored >{};

struct and_kw : TAO_PEGTL_KEYWORD("and") {};
struct or_kw : TAO_PEGTL_KEYWORD("or") {};

struct xor_kw : TAO_PEGTL_KEYWORD("xor") {};

struct not_kw : TAO_PEGTL_KEYWORD("not") {};
struct true_kw : TAO_PEGTL_KEYWORD("true") {};
struct false_kw : TAO_PEGTL_KEYWORD("false") {};

struct BOOL : seq< sor< true_kw, false_kw >, ignored > {};

struct let_kw : TAO_PEGTL_KEYWORD("let") {};
struct LET : seq < let_kw, ignored > {};

struct do_kw : TAO_PEGTL_KEYWORD("do") {};
struct DO : seq < do_kw, ignored > {};

struct while_kw : TAO_PEGTL_KEYWORD("while") {};
struct WHILE : seq < while_kw, ignored > {};

struct for_kw : TAO_PEGTL_KEYWORD("for") {};
struct FOR : seq < for_kw, ignored > {};

struct if_kw : TAO_PEGTL_KEYWORD("if") {};
struct IF : seq < if_kw, ignored > {};

struct else_kw : TAO_PEGTL_KEYWORD("else") {};
struct ELSE : seq < else_kw, ignored > {};

struct break_kw : TAO_PEGTL_KEYWORD("break") {};
struct BREAK : seq < break_kw, ignored > {};

struct continue_kw : TAO_PEGTL_KEYWORD("continue") {};
struct CONTINUE : seq < continue_kw, ignored > {};

struct cout_kw : TAO_PEGTL_KEYWORD("cout") {};
struct cerr_kw : TAO_PEGTL_KEYWORD("cerr") {};
struct clog_kw : TAO_PEGTL_KEYWORD("clog") {};

struct keywork : sor < basic_type, import_kw, true_kw, false_kw, let_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, break_kw, continue_kw, cout_kw, cerr_kw, clog_kw > {};

struct identifier_minus_keyword : minus< identifier, keywork > {};

struct module_name : identifier_minus_keyword {};
struct MODULE_NAME : seq< module_name, ignored > {};

struct name : identifier_minus_keyword {};
struct NAME : seq< name, ignored > {};

struct right_arrow_kw : seq< one< '-' >, one< '>' > > {};
struct RIGHT_ARROW : seq< right_arrow_kw, ignored > {};

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

struct column : one< ':' > {};
struct COLUMN : seq< column , ignored > {};

struct comma : one< ',' > {};
struct COMMA : seq< comma , ignored > {};

struct open_parent : seq< one< '(' >, ignored > {};
struct close_parent : seq< one< ')' >, ignored > {};

struct expression;
struct parented_expression : if_must< open_parent, expression, close_parent >{};

struct function_argument_list : if_must< open_parent, list_must< expression, COMMA >, close_parent >{};
struct function_evaluation : seq< NAME, function_argument_list > {};

struct primary_expression : sor< BOOL, REAL, INTEGER, LITERAL, function_evaluation, NAME, parented_expression > {};

struct unary_plusplus : TAO_PEGTL_STRING("++") {};
struct unary_minusminus : TAO_PEGTL_STRING("--") {};

struct unary_plus :  one< '+' > {};
struct unary_minus : one< '-' > {};

struct unary_not : not_kw {};

struct unary_operator : seq< sor< unary_plusplus, unary_minusminus, unary_plus, unary_minus, unary_not>, ignored > {};

struct post_plusplus : TAO_PEGTL_STRING("++") {};
struct post_minusminus : TAO_PEGTL_STRING("--") {};

struct postfix_operator : seq< sor< post_plusplus, post_minusminus>, ignored > {};

struct postfix_expression : seq< primary_expression, star<postfix_operator> > {};

struct unary_expression : sor< seq< unary_operator, unary_expression >,
                               postfix_expression > {};

struct and_op : seq< and_kw, ignored > {};
struct or_op : seq< or_kw, ignored > {};
struct xor_op : seq< xor_kw, ignored >{};

struct eqeq_op : seq< TAO_PEGTL_STRING("=="), ignored > {};
struct not_eq_op : seq< TAO_PEGTL_STRING("!="), ignored > {};

struct lesser_op : seq< one< '<' >, not_at< one< '<' > >, ignored > {};
struct lesser_or_eq_op : seq< TAO_PEGTL_STRING("<="), ignored > {};
struct greater_op : seq< one< '>' >, not_at< one< '>' > >, ignored > {};
struct greater_or_eq_op : seq< TAO_PEGTL_STRING(">="), ignored > {};

struct shift_left_op : seq< TAO_PEGTL_STRING("<<"), ignored > {};
struct shift_right_op : seq< TAO_PEGTL_STRING(">>"), ignored > {};

struct plus_op : seq< one< '+' >, not_at< one< '+' > >, ignored > {};
struct minus_op : seq< one< '-' >, not_at< one< '-' > >, ignored > {};
struct multiply_op : seq< one< '*' >, ignored > {};
struct divide_op : seq< one< '/' >, ignored > {};

struct eq_op : seq< one<'='>, not_at< one< '=' > >, ignored > {};
struct multiplyeq_op : seq< TAO_PEGTL_STRING("*="), ignored > {};
struct divideeq_op : seq< TAO_PEGTL_STRING("/="), ignored > {};
struct pluseq_op : seq< TAO_PEGTL_STRING("+="), ignored > {};
struct minuseq_op : seq< TAO_PEGTL_STRING("-="), ignored > {};

struct product : list_must< unary_expression, sor< multiply_op, divide_op > > {};

struct sum : list_must< product, sor< plus_op, minus_op > > {};

struct compare : list_must<sum, sor< lesser_or_eq_op, greater_or_eq_op, lesser_op, greater_op > >{};

struct equality : list_must< compare, sor< eqeq_op, not_eq_op > >{};

struct bitwise_xor : list_must< equality, xor_op >{};

struct logical_and : list_must< bitwise_xor, and_op >{};

struct logical_or : list_must< logical_and, or_op >{};

struct expression : logical_or {};

struct tuple_expression : seq< open_parent, expression, plus< if_must< COMMA, expression > >, close_parent >{};

struct expression_list : seq< open_parent, sor< tuple_expression, expression >, plus< if_must< COMMA, sor< tuple_expression, expression > > >, close_parent >{};

struct affect_op : sor< eq_op, multiplyeq_op, divideeq_op, pluseq_op, minuseq_op > {};

struct name_list;

struct affectation : seq< sor< NAME, name_list >, if_must< affect_op,  sor< expression_list, expression > > >{};

struct declaration : if_must< TYPE_EXPRESSION, sor< NAME, name_list>, opt< if_must< seq< one< '=' >, ignored >, sor< expression_list, expression > > > >{};

struct type_mapping : seq< TYPE_EXPRESSION, RIGHT_ARROW, TYPE_EXPRESSION >{};

struct name_list : seq< open_parent, list_must< NAME, COMMA >, close_parent >{};

struct function_definition : seq< sor< name_list, NAME >, RIGHT_ARROW, sor< expression_list, expression > >{};

struct let_declaration : if_must< LET, NAME, COLUMN, type_mapping, COMMA, function_definition >{};

struct open_brace : seq< one< '{' >, ignored >{};
struct close_brace : seq< one< '}' >, ignored >{};

struct instruction_list;

struct braced_instruction_list
    :  sor<try_catch< open_brace, instruction_list, close_brace >,
           // non matching braces management
           if_must< at< one< '{' > >, raise< open_brace >, until< eof > > >{};

struct block : braced_instruction_list {};

struct statement_block;

struct if_statement : if_must< IF, parented_expression,  statement_block, opt< if_must<ELSE, statement_block > > >{};

struct do_while_statement : if_must< DO, statement_block, WHILE, parented_expression >{};

struct while_statement : if_must< WHILE, parented_expression, statement_block >{};

struct for_init : opt< sor< declaration, affectation, expression > >{};
struct for_test : opt< sor< affectation, expression > >{};
struct for_post : opt< sor< affectation, expression > >{};

struct for_statement_block;

struct for_statement : if_must< FOR, open_parent, for_init, SEMICOL, for_test, SEMICOL, for_post, close_parent, for_statement_block >{};

struct ostream_object : seq< sor< cout_kw, cerr_kw, clog_kw >, ignored >{};
struct ostream_statement : seq< ostream_object, star< if_must< shift_left_op, expression, ignored > > >{};

struct instruction
    : sor<if_must< let_declaration, semicol >,
          if_must< declaration, semicol >,
          if_must< affectation, semicol >,
          if_statement,
          if_must<do_while_statement, semicol>,
          while_statement,
          for_statement,
          if_must< ostream_statement, semicol >,
          if_must< BREAK, semicol >,
          if_must< CONTINUE, semicol >,
          if_must< expression, semicol >,
          block,
          semicol>
{};

struct INSTRUCTION : seq<instruction, ignored> {};
struct statement_block : seq< sor< block, instruction >, ignored >{};

struct for_statement_block : seq< sor< braced_instruction_list,
                                      instruction >,
                                 ignored >{};

struct IMPORT : seq< import_kw, ignored >{};

struct import_instruction : if_must< IMPORT, MODULE_NAME , semicol >{};
struct IMPORT_INSTRUCTION : seq<import_instruction, ignored> {};
struct import_list : star< IMPORT_INSTRUCTION > {};

struct instruction_list : star< INSTRUCTION >{};
struct grammar : must<ignored, import_list, instruction_list, eof>{};

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>
inline const std::string errors<Rule>::error_message = "parse error matching "+ demangle(internal::demangle< Rule >());

template <>
inline const std::string errors<language::module_name>::error_message = "parse error, missing module name";
template <>
inline const std::string errors<language::MODULE_NAME>::error_message = "parse error, missing module name";

template <>
inline const std::string errors<language::semicol>::error_message = "parse error, missing ';'";
template <>
inline const std::string errors<language::SEMICOL>::error_message = "parse error, missing ';'";

template <>
inline const std::string errors<language::name>::error_message = "parse error, missing identifier";
template <>
inline const std::string errors<language::NAME>::error_message = "parse error, missing identifier";

template <>
inline const std::string errors<language::expression>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::product>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::sum>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::compare>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::equality>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::bitwise_xor>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::logical_and>::error_message = "parse error, missing expression";
template <>
inline const std::string errors<language::logical_or>::error_message = "parse error, missing expression";

template <>
inline const std::string errors<language::parented_expression>::error_message = "parse error, missing parented expression";

template <>
inline const std::string errors<language::statement_block>::error_message = "parse error, missing instruction";

template <>
inline const std::string errors<language::WHILE>::error_message = "parse error, missing 'while' statement";
template <>
inline const std::string errors<language::while_kw>::error_message = "parse error, missing 'while' statement";

template <>
inline const std::string errors<language::open_parent>::error_message = "parse error, missing open parent '('";

template <>
inline const std::string errors<language::for_statement_block>::error_message = "parse error, missing for-loop body";

template <>
inline const std::string errors<language::slashstar>::error_message = "block comment was never terminated, missing '*/'";

template <>
inline const std::string errors<language::open_brace>::error_message = "open brace was never closed, missing '}'";

// clang-format on

}   // namespace language

#endif   // PEG_GRAMMAR_HPP