#ifndef __AST__EXPRESSION_H
#define __AST__EXPRESSION_H

#include <iostream>
#include <stack>
#include <map>

#include "translationcontext.h"


#define BINOP(C,B,LT,RT) \
    class C : public B \
    { \
        private: \
            LT l; \
            RT r; \
        public: \
            C(LT l, RT r); \
            ~C(); \
            void dump(std::ostream& os, int depth) override; \
            llvm::Value* translate(TranslationContext* ctx) override; \
    };


class Expression
{
    public:
        virtual ~Expression();
        virtual void dump(std::ostream& os, int depth) = 0;
        virtual llvm::Value* translate(TranslationContext* ctx) = 0;
};

class BooleanExpression : public Expression {};
class RelationExpression : public BooleanExpression {};
class RValue : public Expression {};
class ArithmeticExpression : public RValue {};
class LValue : public ArithmeticExpression {
    public:
        virtual llvm::Value* translateLValue(TranslationContext* ctx) = 0;
};


class LValueIdentifier : public LValue
{
    public:
        std::string* identifier;
        LValueIdentifier(std::string* identifier);
        ~LValueIdentifier();
        void dump(std::ostream& os, int depth) override;
        llvm::Value* translate(TranslationContext* ctx) override;
        llvm::Value* translateLValue(TranslationContext* ctx) override;
};


#define BOOLEXP(C) \
    BINOP(C, BooleanExpression, BooleanExpression*, BooleanExpression*)
BOOLEXP(BooleanExpressionAnd)
BOOLEXP(BooleanExpressionOr)
class BooleanExpressionNot : public BooleanExpression
{
    public:
        BooleanExpression* expr;
        BooleanExpressionNot(BooleanExpression* expr);
        ~BooleanExpressionNot();
        void dump(std::ostream& os, int depth) override;
        llvm::Value* translate(TranslationContext* ctx) override;
};

#define RELEXP(C) \
    BINOP(C, RelationExpression, ArithmeticExpression*, ArithmeticExpression*)
RELEXP(RelationExpressionLess)
RELEXP(RelationExpressionLessEqual)
RELEXP(RelationExpressionEqual)
RELEXP(RelationExpressionUnequal)
RELEXP(RelationExpressionGreaterEqual)
RELEXP(RelationExpressionGreater)

#define ARITHEXP(C) \
    BINOP(C, ArithmeticExpression, ArithmeticExpression*, ArithmeticExpression*)
ARITHEXP(ArithmeticExpressionPlus)
ARITHEXP(ArithmeticExpressionMinus)
ARITHEXP(ArithmeticExpressionMultiply)
ARITHEXP(ArithmeticExpressionDivide)
ARITHEXP(ArithmeticExpressionModulus)
class ArithmeticExpressionConstant : public ArithmeticExpression
{
    public:
        int value;
        ArithmeticExpressionConstant(int value);
        void dump(std::ostream& os, int depth) override;
        llvm::Value* translate(TranslationContext* ctx) override;
};

#endif
