#ifndef PARSER_EXPRESION_H
#define PARSER_EXPRESION_H

#include "lexer.h"
#include "parser_aux.h"
#include <vector>

struct expresion {
   virtual ~expresion( ) = default;
};

struct expresion_termino : expresion {
   token t;

   expresion_termino(token tp)
   : t(tp) {
   }
};

struct expresion_prefija : expresion {
   token operador;
   expresion* ex;

   expresion_prefija(token t, expresion* e)
   : operador(t), ex(e) {
   }
};

struct expresion_binaria : expresion {
   expresion* izq;
   token operador;
   expresion* der;

   expresion_binaria(expresion* i, token t, expresion* d)
   : izq(i), operador(t), der(d) {
   }
};

struct expresion_posfija : expresion {    // nos faltó éste por culpa del operador '
   expresion* ex;
   token operador;

   expresion_posfija(expresion* e, token t)
   : ex(e), operador(t) {
   }
};

struct expresion_indexacion : expresion {
   expresion* izq;
   expresion* dentro;

   expresion_indexacion(expresion* i, expresion* d)
   : izq(i), dentro(d) {
   }
};

struct expresion_llamada : expresion {
   expresion* izq;
   std::vector<expresion*> dentro;

   expresion_llamada(expresion* i, const std::vector<expresion*>& d)
   : izq(i), dentro(d) {
   }
};

struct expresion_secuencia : expresion {
   std::vector<expresion*> ex;

   expresion_secuencia(const std::vector<expresion*>& e)
   : ex(e) {
   }
};

expresion* parser_expr(const token*&);

std::vector<expresion*> parser_lista_expr(const token*& p) {
   // tarea 5
   // std::vector<expresion*> res;
   // ...
   // return res;
}

expresion* parser_expr_primaria(const token*& p) {
   if (es_termino(p)) {
      return new expresion_termino(*p++);
   } else if (p->tipo == CORCHETE_IZQ) {
      ++p;
      std::vector<expresion*> ex = parser_lista_expr(p);
      espera(p, CORCHETE_DER);
      return new expresion_secuencia(ex);
   } else {
      espera(p, PARENTESIS_IZQ);
      expresion* ex = parser_expr(p);
      espera(p, PARENTESIS_DER);
      return ex;
   }
}

expresion* parser_expr_unaria(const token*& p) {
   if (es_operador_prefijo(p)) {
      token t = *p++;
      return new expresion_prefija(t, parser_expr_unaria(p));
   } else {
      expresion* ex = parser_expr_primaria(p);
      while (es_operador_posfijo(p)) {
         if (p->tipo == CORCHETE_IZQ) {
            ++p;
            ex = new expresion_indexacion(ex, parser_expr(p));
            espera(p, CORCHETE_DER);
         } else if (p->tipo == PARENTESIS_IZQ) {
            ++p;
            ex = new expresion_llamada(ex, parser_lista_expr(p));
            espera(p, PARENTESIS_DER);
         } else {
            ex = new expresion_posfija(ex, *p++);
         }
      }
      return ex;
   }
}

expresion* parser_expr_n_aria(const token*& p, int prec) {
   expresion* ex = parser_expr_unaria(p);
   while (es_operador_binario(p) && precedencia(p) >= prec) {
      token t = *p++;
      ex = new expresion_binaria(ex, t,
         parser_expr_n_aria(p, precedencia(t.tipo) + asociatividad(t.tipo))
      );
   }
   return ex;
}

expresion* parser_expr(const token*& p) {
   return parser_expr_n_aria(p, 0);
}

#endif
