#include "expression.hpp"

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

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

symbol_impl* binary_expression::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 + ' ' + e.op + ' ' + e.s2 + "' in :\n\t"
            + serialize(context)
        );
        throw parser_exception();
    } catch (...) {
        throw;
    }
}

std::string binary_expression::serialize(const interpreter_context& context) const {
    auto p = priority(context), p1 = s1.priority(context);
    std::string s;

    if (p.first < p1.first) {
        s += "(" + s1.serialize(context) + ")";
    } else {
        s += s1.serialize(context);
    }

    if (op == "[]") {
        s += "["+s2.serialize(context)+"]";
    } else {
        auto p2 = s2.priority(context);
        s += op;

        if (p.first < p2.first || (p.first == p2.first && p.second < p2.second)) {
            s += "(" + s2.serialize(context) + ")";
        } else {
            s += s2.serialize(context);
        }
    }

    return s;
}

const std::string& binary_expression::static_type_name() {
    static const std::string name = "expression";
    return name;
}

bool binary_expression::is_litteral() const {
    return false;
}

std::pair<std::size_t,std::size_t> binary_expression::priority(const interpreter_context& context) const {
    return context.binary_operator_priority(op);
}




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

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

symbol_impl* unary_expression::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 + "' in :\n\t"
            + serialize(context)
        );
        throw parser_exception();
    } catch (...) {
        throw;
    }
}

std::string unary_expression::serialize(const interpreter_context& context) const {
    if (priority(context) < s.priority(context)) {
        return op + "(" + s.serialize(context) + ")";
    } else {
        return op + s.serialize(context);
    }
}

const std::string& unary_expression::static_type_name() {
    static const std::string name = "expression";
    return name;
}

bool unary_expression::is_litteral() const {
    return false;
}

std::pair<std::size_t,std::size_t> unary_expression::priority(const interpreter_context& context) const {
    return context.unary_operator_priority(op);
}
