<commit_before>//=======================================================================
// Copyright <NAME> 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <string>
#include <utility>

#include <boost/range/adaptors.hpp>

#include "assert.hpp"
#include "VisitorUtils.hpp"
#include "Variable.hpp"
#include "SemanticalException.hpp"
#include "FunctionContext.hpp"
#include "mangling.hpp"
#include "Labels.hpp"
#include "Type.hpp"
#include "PerfsTimer.hpp"
#include "GlobalContext.hpp"

#include "mtac/Compiler.hpp"
#include "mtac/Program.hpp"
#include "mtac/Printer.hpp"
#include "mtac/Utils.hpp"

#include "ast/SourceFile.hpp"
#include "ast/GetTypeVisitor.hpp"
#include "ast/TypeTransformer.hpp"
#include "ast/ASTVisitor.hpp"

using namespace eddic;

namespace {

std::shared_ptr<Variable> performBoolOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function);
void performStringOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> v1, std::shared_ptr<Variable> v2);
void execute_call(ast::FunctionCall& functionCall, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
void execute_member_call(ast::MemberFunctionCall& functionCall, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
mtac::Argument moveToArgument(ast::Value& value, std::shared_ptr<mtac::Function> function);
void assign(std::shared_ptr<mtac::Function> function, ast::Assignment& assignment);
std::vector<mtac::Argument> compile_ternary(std::shared_ptr<mtac::Function> function, ast::Ternary& ternary);

template<typename Call>
void pass_arguments(std::shared_ptr<mtac::Function> function, std::shared_ptr<eddic::Function> definition, Call& functionCall);

std::shared_ptr<Variable> performOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> t1, mtac::Operator f(ast::Operator)){
    ASSERT(value.Content->operations.size() > 0, "Operations with no operation should have been transformed before");

    mtac::Argument left = moveToArgument(value.Content->first, function);
    mtac::Argument right;

    //Apply all the operations in chain
    for(unsigned int i = 0; i < value.Content->operations.size(); ++i){
        auto operation = value.Content->operations[i];

        right = moveToArgument(operation.get<1>(), function);
       
        if (i == 0){
            function->add(std::make_shared<mtac::Quadruple>(t1, left, f(operation.get<0>()), right));
        } else {
            function->add(std::make_shared<mtac::Quadruple>(t1, t1, f(operation.get<0>()), right));
        }
    }

    return t1;
}

std::shared_ptr<Variable> performIntOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function){
    return performOperation(value, function, function->context->new_temporary(INT), &mtac::toOperator);
}

std::shared_ptr<Variable> performFloatOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function){
    return performOperation(value, function, function->context->new_temporary(FLOAT), &mtac::toFloatOperator);
}

mtac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, ast::Value indexValue, std::shared_ptr<mtac::Function> function){
    mtac::Argument index = moveToArgument(indexValue, function);
    
    auto temp = function->context->new_temporary(INT);

    function->add(std::make_shared<mtac::Quadruple>(temp, index, mtac::Operator::MUL, array->type()->data_type()->size()));
    function->add(std::make_shared<mtac::Quadruple>(temp, temp, mtac::Operator::ADD, INT->size()));
   
    return temp;
}

template<typename Operation>
std::shared_ptr<Variable> performPrefixOperation(Operation& operation, std::shared_ptr<mtac::Function> function);

template<typename Operation>
std::shared_ptr<Variable> performSuffixOperation(Operation& operation, std::shared_ptr<mtac::Function> function);

struct ToArgumentsVisitor : public boost::static_visitor<std::vector<mtac::Argument>> {
    ToArgumentsVisitor(std::shared_ptr<mtac::Function> f) : function(f) {}
    ToArgumentsVisitor(std::shared_ptr<mtac::Function> f, bool take_address) : function(f), take_address(take_address) {}
    
    mutable std::shared_ptr<mtac::Function> function;
    bool take_address = false;

    result_type operator()(ast::Literal& literal) const {
        return {literal.label, (int) literal.value.size() - 2};
    }

    result_type operator()(ast::CharLiteral& literal) const {
        char v = literal.value[1];

        return {static_cast<int>(v)};
    }

    result_type operator()(ast::Integer& integer) const {
        return {integer.value};
    }
    
    result_type operator()(ast::IntegerSuffix& integer) const {
        return {(double) integer.value};
    }
    
    result_type operator()(ast::Float& float_) const {
        return {float_.value};
    }
    
    result_type operator()(ast::False&) const {
        return {0};
    }
    
    result_type operator()(ast::True&) const {
        return {1};
    }
    
    result_type operator()(ast::Null&) const {
        return {0};
    }

    result_type operator()(ast::New& new_) const {
        auto type = visit(ast::TypeTransformer(function->context->global()), new_.Content->type);
    
        auto param = std::make_shared<mtac::Param>(type->size());
        param->std_param = "a";
        param->function = function->context->global()->getFunction("_F5allocI");
        function->add(param);

        auto t1 = function->context->new_temporary(new_pointer_type(INT));

        function->context->global()->addReference("_F5allocI");
        function->add(std::make_shared<mtac::Call>("_F5allocI", function->context->global()->getFunction("_F5allocI"), t1)); 
            
        if(type->is_custom_type()){
            auto struct_ = type->type();
            auto ctor_name = mangle_ctor(new_.Content->values, struct_);

            if(!function->context->global()->exists(ctor_name)){
                assert(new_.Content->values.empty());
            } else {
                auto ctor_function = function->context->global()->getFunction(ctor_name);

                //Pass all normal arguments
                pass_arguments(function, ctor_function, new_);

                auto ctor_param = std::make_shared<mtac::Param>(t1, ctor_function->context->getVariable(ctor_function->parameters[0].name), ctor_function);
                ctor_param->address = true;
                function->add(ctor_param);

                function->context->global()->addReference(ctor_name);
                function->add(std::make_shared<mtac::Call>(ctor_name, ctor_function)); 
            }
        }

        return {t1};
    }

    result_type operator()(ast::BuiltinOperator& builtin) const {
        auto& value = builtin.Content->values[0];

        switch(builtin.Content->type){
            case ast::BuiltinType::SIZE:{
                ASSERT(boost::get<ast::VariableValue>(&value), "The size builtin can only be applied to variable");
                
                auto variable = boost::get<ast::VariableValue>(value).Content->var;

                if(variable->position().isGlobal()){
                    return {variable->type()->elements()};
                } else if(variable->position().isStack()){
                    return {variable->type()->elements()};
                } else if(variable->position().isParameter()){
                    auto t1 = function->context->new_temporary(INT);

                    //The size of the array is at the address pointed by the variable
                    function->add(std::make_shared<mtac::Quadruple>(t1, variable, mtac::Operator::DOT, 0));

                    return {t1};
                }

                ASSERT_PATH_NOT_TAKEN("The variale is not of a valid type");
            }
            case ast::BuiltinType::LENGTH:
                return {visit(*this, value)[1]};
        }

        ASSERT_PATH_NOT_TAKEN("This builtin operator is not handled");
    }

    result_type operator()(ast::FunctionCall& call) const {
        auto type = call.Content->function->returnType;

        if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
            auto t1 = function->context->new_temporary(type);

            execute_call(call, function, t1, {});

            return {t1};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            execute_call(call, function, t1, t2);

            return {t1, t2};
        }
        
        ASSERT_PATH_NOT_TAKEN("Unhandled function return type");
    }
    
    result_type operator()(ast::MemberFunctionCall& call) const {
        auto type = call.Content->function->returnType;

        if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
            auto t1 = function->context->new_temporary(type);

            execute_member_call(call, function, t1, {});

            return {t1};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            execute_member_call(call, function, t1, t2);

            return {t1, t2};
        }
        
        ASSERT_PATH_NOT_TAKEN("Unhandled function return type");
    }

    result_type operator()(ast::Assignment& assignment) const {
        ASSERT(assignment.Content->op == ast::Operator::ASSIGN, "Compound assignment should be transformed into Assignment");

        assign(function, assignment);

        return visit(*this, assignment.Content->left_value);
    }

    result_type operator()(ast::Ternary& ternary) const {
        return compile_ternary(function, ternary);
    }

    result_type get_member(unsigned int offset, std::shared_ptr<const Type> member_type, std::shared_ptr<Variable> var) const {
        if(member_type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            function->add(std::make_shared<mtac::Quadruple>(t1, var, mtac::Operator::DOT, offset));
            function->add(std::make_shared<mtac::Quadruple>(t2, var, mtac::Operator::DOT, offset + INT->size()));

            return {t1, t2};
        } else {
            auto temp = function->context->new_temporary(member_type);

            if(member_type == FLOAT){
                function->add(std::make_shared<mtac::Quadruple>(temp, var, mtac::Operator::FDOT, offset));
            } else if(member_type == INT || member_type == CHAR || member_type == BOOL || member_type->is_pointer()){
                function->add(std::make_shared<mtac::Quadruple>(temp, var, mtac::Operator::DOT, offset));
            } else {
                ASSERT_PATH_NOT_TAKEN("Unhandled type");
            }

            return {temp};
        }
    }
   
    result_type operator()(std::shared_ptr<Variable> var) const {
        return {var};
    }

    result_type operator()(ast::VariableValue& value) const {
        if(value.Content->memberNames.empty()){
            if(take_address){
                return {value.Content->var};
            }

            auto type = value.Content->var->type();

            //If it's a const, we just have to replace it by its constant value
            if(type->is_const()){
                auto val = value.Content->var->val();
                auto nc_type = type->non_const();

                if(nc_type == INT || nc_type == BOOL){
                    return {boost::get<int>(val)};
                } else if(nc_type == CHAR){
                    return {boost::get<char>(val)};        
                } else if(nc_type == FLOAT){
                    return {boost::get<double>(val)};        
                } else if(nc_type == STRING){
                    auto value = boost::get<std::pair<std::string, int>>(val);

                    return {value.first, value.second};
                } 

                ASSERT_PATH_NOT_TAKEN("void is not a type");
            } else if(type->is_array() || type->is_pointer()){
                return {value.Content->var};
            } else {
                if(type == INT || type == CHAR || type == BOOL || type == FLOAT){
                    return {value.Content->var};
                } else if(type == STRING){
                    auto temp = value.Content->context->new_temporary(INT);
                    function->add(std::make_shared<mtac::Quadruple>(temp, value.Content->var, mtac::Operator::DOT, INT->size()));

                    return {value.Content->var, temp};
                } else if(type->is_custom_type()) {
                    //If we are here, it means that we want to pass it by reference
                    return {value.Content->var};
                } else {
                    ASSERT_PATH_NOT_TAKEN("Unhandled type");
                }
            }
        } else {
            std::shared_ptr<const Type> member_type;
            unsigned int offset = 0;
            boost::tie(offset, member_type) = mtac::compute_member(function->context->global(), value.variable(), value.Content->memberNames);

            if(take_address){
                auto temp = value.Content->context->new_temporary(INT);
                
                function->add(std::make_shared<mtac::Quadruple>(temp, value.Content->var, mtac::Operator::PDOT, offset));

                return {temp};
            }

            return get_member(offset, member_type, value.Content->var);
        }
    }

    result_type dereference_variable(std::shared_ptr<Variable> variable, std::shared_ptr<const Type> type) const {
        if(type == INT || type == CHAR || type == BOOL){
            auto temp = function->context->new_temporary(type);

            function->add(std::make_shared<mtac::Quadruple>(temp, variable, mtac::Operator::DOT, 0));

            return {temp};
        } else if(type == FLOAT){
            auto temp = function->context->new_temporary(type);

            function->add(std::make_shared<mtac::Quadruple>(temp, variable, mtac::Operator::FDOT, 0));

            return {temp};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            function->add(std::make_shared<mtac::Quadruple>(t1, variable, mtac::Operator::DOT, 0));
            function->add(std::make_shared<mtac::Quadruple>(t2, variable, mtac::Operator::DOT, INT->size()));

            return {t1, t2};
        } else {
            ASSERT_PATH_NOT_TAKEN("Unhandled type");
        }
    }

    template<typename Value>
    result_type dereference_sub(Value& value) const {
        auto values = visit_non_variant(*this, value);

        ASSERT(mtac::isVariable(values[0]), "The visitor should return a temporary variable");

        auto variable = boost::get<std::shared_ptr<Variable>>(values[0]);
        return dereference_variable(variable, visit_non_variant(ast::GetTypeVisitor(), value)->data_type());
    }
    
    result_type operator()(ast::DereferenceValue& dereference_value) const {
        if(auto* ptr = boost::get<ast::VariableValue>(&dereference_value.Content->ref)){
            auto& value = *ptr;

            if(value.Content->memberNames.empty()){
                auto type = value.variable()->type()->data_type();
                return dereference_variable(value.variable(), type);
            } else {
                return dereference_sub(value);
            }
        } else if(auto* ptr = boost::get<ast::ArrayValue>(&dereference_value.Content->ref)){
            return dereference_sub(*ptr);
        } else {
            ASSERT_PATH_NOT_TAKEN("Unhandled dereference left value type");
        }
    }

    result_type operator()(ast::PrefixOperation& operation) const {
        return {performPrefixOperation(operation, function)};
    }

    result_type operator()(ast::SuffixOperation& operation) const {
        return {performSuffixOperation(operation, function)};
    }

    result_type operator()(ast::ArrayValue& array) const {
        auto index = computeIndexOfArray(array.Content->var, array.Content->indexValue, function); 

        if(array.Content->memberNames.empty()){
            auto type = array.Content->var->type()->data_type();
            
            if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
                auto temp = array.Content->context->new_temporary(type);
                function->add(std::make_shared<mtac::Quadruple>(temp, array.Content->var, mtac::Operator::DOT, index));
                return {temp};
            } else if (type == STRING){
                auto t1 = array.Content->context->new_temporary(INT);
                function->add(std::make_shared<mtac::Quadruple>(t1, array.Content->var, mtac::Operator::DOT, index));

                auto t2 = array.Content->context->new_temporary(INT);
                auto t3 = array.Content->context->new_temporary(INT);

                //Assign the second part of the string
                function->add(std::make_shared<mtac::Quadruple>(t3, index, mtac::Operator::ADD, INT->size()));
                function->add(std::make_shared<mtac::Quadruple>(t2, array.Content->var, mtac::Operator::DOT, t3));

                return {t1, t2};
            } else {
                ASSERT_PATH_NOT_TAKEN("void is not a variable");
            }
        } else {
            auto temp = array.Content->context->new_temporary(INT);
            function->add(std::make_shared<mtac::Quadruple>(temp, array.Content->var, mtac::Operator::PDOT, index));
            
            auto member_info = mtac::compute_member(function->context->global(), array.Content->var, array.Content->memberNames);
            return get_member(member_info.first, member_info.second, temp);
        }<commit_msg>Fix prototype of compilation for char at<commit_after>//=======================================================================
// Copyright <NAME> 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <string>
#include <utility>

#include <boost/range/adaptors.hpp>

#include "assert.hpp"
#include "VisitorUtils.hpp"
#include "Variable.hpp"
#include "SemanticalException.hpp"
#include "FunctionContext.hpp"
#include "mangling.hpp"
#include "Labels.hpp"
#include "Type.hpp"
#include "PerfsTimer.hpp"
#include "GlobalContext.hpp"

#include "mtac/Compiler.hpp"
#include "mtac/Program.hpp"
#include "mtac/Printer.hpp"
#include "mtac/Utils.hpp"

#include "ast/SourceFile.hpp"
#include "ast/GetTypeVisitor.hpp"
#include "ast/TypeTransformer.hpp"
#include "ast/ASTVisitor.hpp"

using namespace eddic;

namespace {

std::shared_ptr<Variable> performBoolOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function);
void performStringOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> v1, std::shared_ptr<Variable> v2);
void execute_call(ast::FunctionCall& functionCall, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
void execute_member_call(ast::MemberFunctionCall& functionCall, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
mtac::Argument moveToArgument(ast::Value& value, std::shared_ptr<mtac::Function> function);
void assign(std::shared_ptr<mtac::Function> function, ast::Assignment& assignment);
std::vector<mtac::Argument> compile_ternary(std::shared_ptr<mtac::Function> function, ast::Ternary& ternary);

template<typename Call>
void pass_arguments(std::shared_ptr<mtac::Function> function, std::shared_ptr<eddic::Function> definition, Call& functionCall);

std::shared_ptr<Variable> performOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function, std::shared_ptr<Variable> t1, mtac::Operator f(ast::Operator)){
    ASSERT(value.Content->operations.size() > 0, "Operations with no operation should have been transformed before");

    mtac::Argument left = moveToArgument(value.Content->first, function);
    mtac::Argument right;

    //Apply all the operations in chain
    for(unsigned int i = 0; i < value.Content->operations.size(); ++i){
        auto operation = value.Content->operations[i];

        right = moveToArgument(operation.get<1>(), function);
       
        if (i == 0){
            function->add(std::make_shared<mtac::Quadruple>(t1, left, f(operation.get<0>()), right));
        } else {
            function->add(std::make_shared<mtac::Quadruple>(t1, t1, f(operation.get<0>()), right));
        }
    }

    return t1;
}

std::shared_ptr<Variable> performIntOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function){
    return performOperation(value, function, function->context->new_temporary(INT), &mtac::toOperator);
}

std::shared_ptr<Variable> performFloatOperation(ast::Expression& value, std::shared_ptr<mtac::Function> function){
    return performOperation(value, function, function->context->new_temporary(FLOAT), &mtac::toFloatOperator);
}

mtac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, ast::Value indexValue, std::shared_ptr<mtac::Function> function){
    mtac::Argument index = moveToArgument(indexValue, function);
    
    auto temp = function->context->new_temporary(INT);

    function->add(std::make_shared<mtac::Quadruple>(temp, index, mtac::Operator::MUL, array->type()->data_type()->size()));
    function->add(std::make_shared<mtac::Quadruple>(temp, temp, mtac::Operator::ADD, INT->size()));
   
    return temp;
}

template<typename Operation>
std::shared_ptr<Variable> performPrefixOperation(Operation& operation, std::shared_ptr<mtac::Function> function);

template<typename Operation>
std::shared_ptr<Variable> performSuffixOperation(Operation& operation, std::shared_ptr<mtac::Function> function);

struct ToArgumentsVisitor : public boost::static_visitor<std::vector<mtac::Argument>> {
    ToArgumentsVisitor(std::shared_ptr<mtac::Function> f) : function(f) {}
    ToArgumentsVisitor(std::shared_ptr<mtac::Function> f, bool take_address) : function(f), take_address(take_address) {}
    
    mutable std::shared_ptr<mtac::Function> function;
    bool take_address = false;

    result_type operator()(ast::Literal& literal) const {
        return {literal.label, (int) literal.value.size() - 2};
    }

    result_type operator()(ast::CharLiteral& literal) const {
        char v = literal.value[1];

        return {static_cast<int>(v)};
    }

    result_type operator()(ast::Integer& integer) const {
        return {integer.value};
    }
    
    result_type operator()(ast::IntegerSuffix& integer) const {
        return {(double) integer.value};
    }
    
    result_type operator()(ast::Float& float_) const {
        return {float_.value};
    }
    
    result_type operator()(ast::False&) const {
        return {0};
    }
    
    result_type operator()(ast::True&) const {
        return {1};
    }
    
    result_type operator()(ast::Null&) const {
        return {0};
    }

    result_type operator()(ast::New& new_) const {
        auto type = visit(ast::TypeTransformer(function->context->global()), new_.Content->type);
    
        auto param = std::make_shared<mtac::Param>(type->size());
        param->std_param = "a";
        param->function = function->context->global()->getFunction("_F5allocI");
        function->add(param);

        auto t1 = function->context->new_temporary(new_pointer_type(INT));

        function->context->global()->addReference("_F5allocI");
        function->add(std::make_shared<mtac::Call>("_F5allocI", function->context->global()->getFunction("_F5allocI"), t1)); 
            
        if(type->is_custom_type()){
            auto struct_ = type->type();
            auto ctor_name = mangle_ctor(new_.Content->values, struct_);

            if(!function->context->global()->exists(ctor_name)){
                assert(new_.Content->values.empty());
            } else {
                auto ctor_function = function->context->global()->getFunction(ctor_name);

                //Pass all normal arguments
                pass_arguments(function, ctor_function, new_);

                auto ctor_param = std::make_shared<mtac::Param>(t1, ctor_function->context->getVariable(ctor_function->parameters[0].name), ctor_function);
                ctor_param->address = true;
                function->add(ctor_param);

                function->context->global()->addReference(ctor_name);
                function->add(std::make_shared<mtac::Call>(ctor_name, ctor_function)); 
            }
        }

        return {t1};
    }

    result_type operator()(ast::BuiltinOperator& builtin) const {
        auto& value = builtin.Content->values[0];

        switch(builtin.Content->type){
            case ast::BuiltinType::SIZE:{
                ASSERT(boost::get<ast::VariableValue>(&value), "The size builtin can only be applied to variable");
                
                auto variable = boost::get<ast::VariableValue>(value).Content->var;

                if(variable->position().isGlobal()){
                    return {variable->type()->elements()};
                } else if(variable->position().isStack()){
                    return {variable->type()->elements()};
                } else if(variable->position().isParameter()){
                    auto t1 = function->context->new_temporary(INT);

                    //The size of the array is at the address pointed by the variable
                    function->add(std::make_shared<mtac::Quadruple>(t1, variable, mtac::Operator::DOT, 0));

                    return {t1};
                }

                ASSERT_PATH_NOT_TAKEN("The variale is not of a valid type");
            }
            case ast::BuiltinType::LENGTH:
                return {visit(*this, value)[1]};
        }

        ASSERT_PATH_NOT_TAKEN("This builtin operator is not handled");
    }

    result_type operator()(ast::FunctionCall& call) const {
        auto type = call.Content->function->returnType;

        if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
            auto t1 = function->context->new_temporary(type);

            execute_call(call, function, t1, {});

            return {t1};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            execute_call(call, function, t1, t2);

            return {t1, t2};
        }
        
        ASSERT_PATH_NOT_TAKEN("Unhandled function return type");
    }
    
    result_type operator()(ast::MemberFunctionCall& call) const {
        auto type = call.Content->function->returnType;

        if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
            auto t1 = function->context->new_temporary(type);

            execute_member_call(call, function, t1, {});

            return {t1};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            execute_member_call(call, function, t1, t2);

            return {t1, t2};
        }
        
        ASSERT_PATH_NOT_TAKEN("Unhandled function return type");
    }

    result_type operator()(ast::Assignment& assignment) const {
        ASSERT(assignment.Content->op == ast::Operator::ASSIGN, "Compound assignment should be transformed into Assignment");

        assign(function, assignment);

        return visit(*this, assignment.Content->left_value);
    }

    result_type operator()(ast::Ternary& ternary) const {
        return compile_ternary(function, ternary);
    }

    result_type get_member(unsigned int offset, std::shared_ptr<const Type> member_type, std::shared_ptr<Variable> var) const {
        if(member_type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            function->add(std::make_shared<mtac::Quadruple>(t1, var, mtac::Operator::DOT, offset));
            function->add(std::make_shared<mtac::Quadruple>(t2, var, mtac::Operator::DOT, offset + INT->size()));

            return {t1, t2};
        } else {
            auto temp = function->context->new_temporary(member_type);

            if(member_type == FLOAT){
                function->add(std::make_shared<mtac::Quadruple>(temp, var, mtac::Operator::FDOT, offset));
            } else if(member_type == INT || member_type == CHAR || member_type == BOOL || member_type->is_pointer()){
                function->add(std::make_shared<mtac::Quadruple>(temp, var, mtac::Operator::DOT, offset));
            } else {
                ASSERT_PATH_NOT_TAKEN("Unhandled type");
            }

            return {temp};
        }
    }
   
    result_type operator()(std::shared_ptr<Variable> var) const {
        return {var};
    }

    result_type operator()(ast::VariableValue& value) const {
        if(value.Content->memberNames.empty()){
            if(take_address){
                return {value.Content->var};
            }

            auto type = value.Content->var->type();

            //If it's a const, we just have to replace it by its constant value
            if(type->is_const()){
                auto val = value.Content->var->val();
                auto nc_type = type->non_const();

                if(nc_type == INT || nc_type == BOOL){
                    return {boost::get<int>(val)};
                } else if(nc_type == CHAR){
                    return {boost::get<char>(val)};        
                } else if(nc_type == FLOAT){
                    return {boost::get<double>(val)};        
                } else if(nc_type == STRING){
                    auto value = boost::get<std::pair<std::string, int>>(val);

                    return {value.first, value.second};
                } 

                ASSERT_PATH_NOT_TAKEN("void is not a type");
            } else if(type->is_array() || type->is_pointer()){
                return {value.Content->var};
            } else {
                if(type == INT || type == CHAR || type == BOOL || type == FLOAT){
                    return {value.Content->var};
                } else if(type == STRING){
                    auto temp = value.Content->context->new_temporary(INT);
                    function->add(std::make_shared<mtac::Quadruple>(temp, value.Content->var, mtac::Operator::DOT, INT->size()));

                    return {value.Content->var, temp};
                } else if(type->is_custom_type()) {
                    //If we are here, it means that we want to pass it by reference
                    return {value.Content->var};
                } else {
                    ASSERT_PATH_NOT_TAKEN("Unhandled type");
                }
            }
        } else {
            std::shared_ptr<const Type> member_type;
            unsigned int offset = 0;
            boost::tie(offset, member_type) = mtac::compute_member(function->context->global(), value.variable(), value.Content->memberNames);

            if(take_address){
                auto temp = value.Content->context->new_temporary(INT);
                
                function->add(std::make_shared<mtac::Quadruple>(temp, value.Content->var, mtac::Operator::PDOT, offset));

                return {temp};
            }

            return get_member(offset, member_type, value.Content->var);
        }
    }

    result_type dereference_variable(std::shared_ptr<Variable> variable, std::shared_ptr<const Type> type) const {
        if(type == INT || type == CHAR || type == BOOL){
            auto temp = function->context->new_temporary(type);

            function->add(std::make_shared<mtac::Quadruple>(temp, variable, mtac::Operator::DOT, 0));

            return {temp};
        } else if(type == FLOAT){
            auto temp = function->context->new_temporary(type);

            function->add(std::make_shared<mtac::Quadruple>(temp, variable, mtac::Operator::FDOT, 0));

            return {temp};
        } else if(type == STRING){
            auto t1 = function->context->new_temporary(INT);
            auto t2 = function->context->new_temporary(INT);

            function->add(std::make_shared<mtac::Quadruple>(t1, variable, mtac::Operator::DOT, 0));
            function->add(std::make_shared<mtac::Quadruple>(t2, variable, mtac::Operator::DOT, INT->size()));

            return {t1, t2};
        } else {
            ASSERT_PATH_NOT_TAKEN("Unhandled type");
        }
    }

    template<typename Value>
    result_type dereference_sub(Value& value) const {
        auto values = visit_non_variant(*this, value);

        ASSERT(mtac::isVariable(values[0]), "The visitor should return a temporary variable");

        auto variable = boost::get<std::shared_ptr<Variable>>(values[0]);
        return dereference_variable(variable, visit_non_variant(ast::GetTypeVisitor(), value)->data_type());
    }
    
    result_type operator()(ast::DereferenceValue& dereference_value) const {
        if(auto* ptr = boost::get<ast::VariableValue>(&dereference_value.Content->ref)){
            auto& value = *ptr;

            if(value.Content->memberNames.empty()){
                auto type = value.variable()->type()->data_type();
                return dereference_variable(value.variable(), type);
            } else {
                return dereference_sub(value);
            }
        } else if(auto* ptr = boost::get<ast::ArrayValue>(&dereference_value.Content->ref)){
            return dereference_sub(*ptr);
        } else {
            ASSERT_PATH_NOT_TAKEN("Unhandled dereference left value type");
        }
    }

    result_type operator()(ast::PrefixOperation& operation) const {
        return {performPrefixOperation(operation, function)};
    }

    result_type operator()(ast::SuffixOperation& operation) const {
        return {performSuffixOperation(operation, function)};
    }

    result_type operator()(ast::ArrayValue& array) const {
        if(array.Content->var->type() == STRING){
            auto index = moveToArgument(array.Content->indexValue, function);
            auto pointer_temp = function->context->new_temporary(new_pointer_type(INT));
            auto t1 = function->context->new_temporary(INT);

            //Get the label
            function->add(std::make_shared<mtac::Quadruple>(pointer_temp, array.Content->var, mtac::Operator::DOT, INT->size()));

            //Get the specified char
            function->add(std::make_shared<mtac::Quadruple>(t1, pointer_temp, mtac::Operator::PDOT, index));

            return {t1};
        } else if(array.Content->memberNames.empty()){
        auto index = computeIndexOfArray(array.Content->var, array.Content->indexValue, function); 
            auto type = array.Content->var->type()->data_type();
            
            if(type == BOOL || type == CHAR || type == INT || type == FLOAT || type->is_pointer()){
                auto temp = array.Content->context->new_temporary(type);
                function->add(std::make_shared<mtac::Quadruple>(temp, array.Content->var, mtac::Operator::DOT, index));
                return {temp};
            } else if (type == STRING){
                auto t1 = array.Content->context->new_temporary(INT);
                function->add(std::make_shared<mtac::Quadruple>(t1, array.Content->var, mtac::Operator::DOT, index));

                auto t2 = array.Content->context->new_temporary(INT);
                auto t3 = array.Content->context->new_temporary(INT);

                //Assign the second part of the string
                function->add(std::make_shared<mtac::Quadruple>(t3, index, mtac::Operator::ADD, INT->size()));
                function->add(std::make_shared<mtac::Quadruple>(t2, array.Content->var, mtac::Operator::DOT, t3));

                return {t1, t2};
            } else {
                ASSERT_PATH_NOT_TAKEN("void is not a variable");
            }
        } else {
        auto index = computeIndexOfArray(array.Content->var, array.Content->indexValue, function); 
            auto temp = array.Content->context->new_temporary(INT);
            function->add(std::make_shared<mtac::Quadruple>(temp, array.Content->var, mtac::Operator::PDOT, index));
            
            auto member_info = mtac::compute_member(function->context->global(), array.Content->var, array.Content->memberNames);
            return get_member(member_info.first, member_info.second, temp);
        }
{'fraction_non_alphanumeric': 0.15113234539908765, 'fraction_numerical': 0.00493967122843955, 'mean_word_length': 2.8427549009438855, 'pattern_counts': {'":': 0, '<': 183, '<?xml version=': 0, '>': 554, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6235696', 'n_tokens_mistral': 10778, 'n_tokens_neox': 10372, 'n_words': 1987}