#ifndef DEF_PARSER_H
#define DEF_PARSER_H

#include <vector>
#include <variant>
#include "tokenize.h"
using namespace std;

/** Grammar:
Prog -> Instruction Prog | Instruction

Instruction -> Statement | ExprStatement; | Expr; | ;

Statement -> // Rien pour l'instant, mais "for", "if" etc
ExprStatement -> 
    | Type Identifier = Expr // AssignedDeclaration
    | Type Identifier // Declaration


Expr ->
    | T
    | T + Expr
    | T - Expr

T ->
    | U
    | U * T
    | U / T
    | U % T

U ->
    | F
    | - U
    | + U

F ->
    | (Expr)
    | Identifier
    | Number
    | Identifier = Expr // Assignment
*/

/**
 * Type de Noeuds
*/
enum class NodeType {
    /* On ne créé pas de nouveau noeud  -> ; Prog */ 
    Prog, //                  -> Instruction Prog
    Epsilon, //               -> ;
    AssignedDeclaration, //   -> Type Identifier = Expr
    Declaration, //           -> Type Identifier
    Plus, //                  -> T + Expr
    Minus, //                 -> T - Expr
    Mult, //                  -> F * T
    Div, //                   -> F / T
    Mod, //                   -> F % T
    UnaryMinus, //            -> -F
    UnaryPlus, //             -> +F
    Assignment //             -> Identifier = Expr
};

struct InnerNode;

/**
 * InnerNode: noeud interne
 * Token: feuille
*/
using Node = variant<InnerNode, Token>;

/**
 * Noeud interne
*/
struct InnerNode {
    NodeType type;
    vector<Node> children;
};

// A Leaf is always corresponding to a Token

/**
 * Node: AST
 * tokens: tokens pas encore parsés
*/
struct ParseReturn {
    Node node;
    vector<Token> tokens;
};

/**
 * Utilisé pour revenir en arrière quand quelque chose n'est pas reconnu
*/
class ParseException : public std::exception {};

/**
 * Parse a list of tokens and return the associated AST
*/
Node parse(vector<Token> tokens);

/**
 * Parse something derivated from Instruction
*/
ParseReturn parse_instruction(vector<Token> tokens);

/**
 * Parse something derivated from Statement
*/
ParseReturn parse_statement(vector<Token> tokens);

/**
 * Parse something derivated from ExprStatement
*/
ParseReturn parse_expr_statement(vector<Token> tokens);

/**
 * Parse something derivated from Expr
*/
ParseReturn parse_expr(vector<Token> tokens);

/**
 * Parse something derivated from T
*/
ParseReturn parse_t(vector<Token> tokens);

/**
 * Parse something derivated from U
*/
ParseReturn parse_u(vector<Token> tokens);

/**
 * Parse something derivated from F
*/
ParseReturn parse_f(vector<Token> tokens);

/**
 * Prints a tree for debugging it
*/
void _debug_print_tree(const Node& node, int depth, const string& prefix);

#endif