#ifndef EXPRESSION_HPP
#define EXPRESSION_HPP

#include "symbol.hpp"
#include "context.hpp"

class binary_expression : public symbol_impl {
    symbol s1, s2;
    std::string op;

public :
    binary_expression(symbol&& s1, const std::string& op, symbol&& s2) :
        s1(std::move(s1)), s2(std::move(s2)), op(op) {}

    binary_expression* clone() const {
        return new binary_expression(s1.clone(), op, s2.clone());
    }

    symbol_impl* evaluate(const interpreter_context& context) const {
        try {
            return context.binary_operator(op, s1, s2);
        } catch (no_binary_operator_match_exception& e) {
            context.msg(
                "error: no match for operator '" + e.s1.type() + ' '
                + e.op + ' ' + e.s2.type() + "' in :\n\t" + serialize(context)
            );
            throw parser_exception();
        } catch (...) {
            throw;
        }
    }

    std::string serialize(const interpreter_context& context) const {
        // TODO : remove unneccessary parenthesis
        return "(" + s1.serialize(context) + op + s2.serialize(context) + ")";
    }

    const std::string& type() const {
        return type_name;
    }

    bool is_litteral() const {
        return false;
    }

    static const std::string type_name;
};

class unary_expression : public symbol_impl {
    symbol s;
    std::string op;

public :
    unary_expression(const std::string& op, symbol&& s) :
       s(std::move(s)), op(op) {}

    unary_expression* clone() const {
        return new unary_expression(op, s.clone());
    }

    symbol_impl* evaluate(const interpreter_context& context) const {
        try {
            return context.unary_operator(op, s);
        } catch (no_unary_operator_match_exception& e) {
            context.msg(
                "error: no match for operator '" + e.op + e.s.type() +
                "' in :\n\t" + serialize(context)
            );
            throw parser_exception();
        } catch (...) {
            throw;
        }
    }

    std::string serialize(const interpreter_context& context) const {
        // TODO : remove unneccessary parenthesis
        return "(" + op + s.serialize(context) + ")";
    }

    const std::string& type() const {
        return type_name;
    }

    bool is_litteral() const {
        return false;
    }

    static const std::string type_name;
};

#endif
