<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 "VisitorUtils.hpp"
#include "Variable.hpp"
#include "SemanticalException.hpp"
#include "FunctionContext.hpp"
#include "GetTypeVisitor.hpp"
#include "GetConstantValue.hpp"
#include "mangling.hpp"
#include "Labels.hpp"

#include "tac/Compiler.hpp"
#include "tac/Program.hpp"

#include "ast/SourceFile.hpp"

using namespace eddic;

namespace {

struct IsSingleArgumentVisitor : public boost::static_visitor<bool> {
    ASSIGN(ast::VariableValue, true)
    ASSIGN(ast::Integer, true)
    ASSIGN(ast::True, true)
    ASSIGN(ast::False, true)
    
    ASSIGN(ast::Litteral, false)
    ASSIGN(ast::ArrayValue, false)
    ASSIGN(ast::ComposedValue, false)
    ASSIGN(ast::Minus, false)
    ASSIGN(ast::Plus, false)
    ASSIGN(ast::BuiltinOperator, false)

    //A call to a function returning an int is single argument
    bool operator()(ast::FunctionCall& call) const {
        Type type = call.Content->function->returnType;

        return type.base() == BaseType::INT || type.base() == BaseType::BOOL;
    }
};

//TODO In some cases, it's possible that some of them can be param safe
//Typically when their subcomponents are safe or constants
struct IsParamSafeVisitor : public boost::static_visitor<bool> {
    ASSIGN(ast::VariableValue, true)
    ASSIGN(ast::Integer, true)
    ASSIGN(ast::True, true)
    ASSIGN(ast::False, true)
    ASSIGN(ast::Litteral, true)
    
    ASSIGN(ast::ArrayValue, false)
    ASSIGN(ast::ComposedValue, false)
    ASSIGN(ast::Minus, false)
    ASSIGN(ast::Plus, false)
    ASSIGN(ast::FunctionCall, false)
    ASSIGN(ast::BuiltinOperator, false)
    ASSIGN(ast::SuffixOperation, false)
    ASSIGN(ast::PrefixOperation, false)
};

void performStringOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function, std::shared_ptr<Variable> v1, std::shared_ptr<Variable> v2);
void executeCall(ast::FunctionCall& functionCall, std::shared_ptr<tac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
tac::Argument moveToArgument(ast::Value& value, std::shared_ptr<tac::Function> function);

std::shared_ptr<Variable> performIntOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function){
    assert(value.Content->operations.size() > 0); //This has been enforced by previous phases

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

    auto t1 = function->context->newTemporary(); 

    //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<tac::Quadruple>(t1, left, tac::toOperator(operation.get<0>()), right));
        } else {
            function->add(std::make_shared<tac::Quadruple>(t1, t1, tac::toOperator(operation.get<0>()), right));
        }
    }

    return t1;
}

std::shared_ptr<Variable> performBoolOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function);

tac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, tac::Argument index, std::shared_ptr<tac::Function> function){
    auto temp = function->context->newTemporary();
    auto position = array->position();

    if(position.isGlobal()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, -1 * size(array->type().base())));

        //Compute the offset manually to avoid having ADD then SUB
        //TODO Find a way to make that optimization in the TAC Optimizer
        int offset = size(array->type().base()) * array->type().size();
        offset -= size(BaseType::INT);

        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, offset));
    } else if(position.isStack()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, size(array->type().base())));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, size(BaseType::INT)));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::MUL, -1));
    } else if(position.isParameter()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, size(array->type().base())));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, size(BaseType::INT)));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::MUL, -1));
    }
   
    return temp;
}

tac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, ast::Value& indexValue, std::shared_ptr<tac::Function> function){
    tac::Argument index = moveToArgument(indexValue, function);

    return computeIndexOfArray(array, index, function);
}

std::shared_ptr<Variable> computeLengthOfArray(std::shared_ptr<Variable> array, std::shared_ptr<tac::Function> function){
    auto t1 = function->context->newTemporary();
    
    auto position = array->position();
    if(position.isGlobal() || position.isStack()){
        function->add(std::make_shared<tac::Quadruple>(t1, array->type().size()));
    } else if(position.isParameter()){
        function->add(std::make_shared<tac::Quadruple>(t1, array, tac::Operator::ARRAY, 0));
    }

    return t1;
}

int getStringOffset(std::shared_ptr<Variable> variable){
    return variable->position().isGlobal() ? 4 : -4;
}

struct ToArgumentsVisitor : public boost::static_visitor<std::vector<tac::Argument>> {
    ToArgumentsVisitor(std::shared_ptr<tac::Function> f) : function(f) {}
    
    mutable std::shared_ptr<tac::Function> function;

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

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

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

        switch(builtin.Content->type){
            case ast::BuiltinType::SIZE:
                if(auto* ptr = boost::get<ast::VariableValue>(&value)){
                    auto variable = (*ptr).Content->var;

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

                        //
                        function->add(std::make_shared<tac::Quadruple>(t1, variable, tac::Operator::DOT, 0));

                        return {t1};
                    }

                    assert(false);
                }

                break;
            case ast::BuiltinType::LENGTH:
                //TODO
                break;
        }

        return {1};
    }

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

        switch(type.base()){
            case BaseType::BOOL:
            case BaseType::INT:{
                auto t1 = function->context->newTemporary();

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

                return {t1};
            }
            case BaseType::STRING:{
                auto t1 = function->context->newTemporary();
                auto t2 = function->context->newTemporary();

                executeCall(call, function, t1, t2);

                return {t1, t2};
            }
            default:
                throw SemanticalException("This function doesn't return anything");   
        }
    }

    result_type operator()(ast::VariableValue& value) const {
        auto type = value.Content->var->type();
<commit_msg>Compile the length builtin operator<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 "VisitorUtils.hpp"
#include "Variable.hpp"
#include "SemanticalException.hpp"
#include "FunctionContext.hpp"
#include "GetTypeVisitor.hpp"
#include "GetConstantValue.hpp"
#include "mangling.hpp"
#include "Labels.hpp"

#include "tac/Compiler.hpp"
#include "tac/Program.hpp"

#include "ast/SourceFile.hpp"

using namespace eddic;

namespace {

struct IsSingleArgumentVisitor : public boost::static_visitor<bool> {
    ASSIGN(ast::VariableValue, true)
    ASSIGN(ast::Integer, true)
    ASSIGN(ast::True, true)
    ASSIGN(ast::False, true)
    
    ASSIGN(ast::Litteral, false)
    ASSIGN(ast::ArrayValue, false)
    ASSIGN(ast::ComposedValue, false)
    ASSIGN(ast::Minus, false)
    ASSIGN(ast::Plus, false)
    ASSIGN(ast::BuiltinOperator, false)

    //A call to a function returning an int is single argument
    bool operator()(ast::FunctionCall& call) const {
        Type type = call.Content->function->returnType;

        return type.base() == BaseType::INT || type.base() == BaseType::BOOL;
    }
};

//TODO In some cases, it's possible that some of them can be param safe
//Typically when their subcomponents are safe or constants
struct IsParamSafeVisitor : public boost::static_visitor<bool> {
    ASSIGN(ast::VariableValue, true)
    ASSIGN(ast::Integer, true)
    ASSIGN(ast::True, true)
    ASSIGN(ast::False, true)
    ASSIGN(ast::Litteral, true)
    
    ASSIGN(ast::ArrayValue, false)
    ASSIGN(ast::ComposedValue, false)
    ASSIGN(ast::Minus, false)
    ASSIGN(ast::Plus, false)
    ASSIGN(ast::FunctionCall, false)
    ASSIGN(ast::BuiltinOperator, false)
    ASSIGN(ast::SuffixOperation, false)
    ASSIGN(ast::PrefixOperation, false)
};

void performStringOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function, std::shared_ptr<Variable> v1, std::shared_ptr<Variable> v2);
void executeCall(ast::FunctionCall& functionCall, std::shared_ptr<tac::Function> function, std::shared_ptr<Variable> return_, std::shared_ptr<Variable> return2_);
tac::Argument moveToArgument(ast::Value& value, std::shared_ptr<tac::Function> function);

std::shared_ptr<Variable> performIntOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function){
    assert(value.Content->operations.size() > 0); //This has been enforced by previous phases

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

    auto t1 = function->context->newTemporary(); 

    //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<tac::Quadruple>(t1, left, tac::toOperator(operation.get<0>()), right));
        } else {
            function->add(std::make_shared<tac::Quadruple>(t1, t1, tac::toOperator(operation.get<0>()), right));
        }
    }

    return t1;
}

std::shared_ptr<Variable> performBoolOperation(ast::ComposedValue& value, std::shared_ptr<tac::Function> function);

tac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, tac::Argument index, std::shared_ptr<tac::Function> function){
    auto temp = function->context->newTemporary();
    auto position = array->position();

    if(position.isGlobal()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, -1 * size(array->type().base())));

        //Compute the offset manually to avoid having ADD then SUB
        //TODO Find a way to make that optimization in the TAC Optimizer
        int offset = size(array->type().base()) * array->type().size();
        offset -= size(BaseType::INT);

        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, offset));
    } else if(position.isStack()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, size(array->type().base())));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, size(BaseType::INT)));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::MUL, -1));
    } else if(position.isParameter()){
        function->add(std::make_shared<tac::Quadruple>(temp, index, tac::Operator::MUL, size(array->type().base())));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::ADD, size(BaseType::INT)));
        function->add(std::make_shared<tac::Quadruple>(temp, temp, tac::Operator::MUL, -1));
    }
   
    return temp;
}

tac::Argument computeIndexOfArray(std::shared_ptr<Variable> array, ast::Value& indexValue, std::shared_ptr<tac::Function> function){
    tac::Argument index = moveToArgument(indexValue, function);

    return computeIndexOfArray(array, index, function);
}

std::shared_ptr<Variable> computeLengthOfArray(std::shared_ptr<Variable> array, std::shared_ptr<tac::Function> function){
    auto t1 = function->context->newTemporary();
    
    auto position = array->position();
    if(position.isGlobal() || position.isStack()){
        function->add(std::make_shared<tac::Quadruple>(t1, array->type().size()));
    } else if(position.isParameter()){
        function->add(std::make_shared<tac::Quadruple>(t1, array, tac::Operator::ARRAY, 0));
    }

    return t1;
}

int getStringOffset(std::shared_ptr<Variable> variable){
    return variable->position().isGlobal() ? 4 : -4;
}

struct ToArgumentsVisitor : public boost::static_visitor<std::vector<tac::Argument>> {
    ToArgumentsVisitor(std::shared_ptr<tac::Function> f) : function(f) {}
    
    mutable std::shared_ptr<tac::Function> function;

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

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

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

        switch(builtin.Content->type){
            case ast::BuiltinType::SIZE:
                if(auto* ptr = boost::get<ast::VariableValue>(&value)){
                    auto variable = (*ptr).Content->var;

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

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

                        return {t1};
                    }
                }
                    
                assert(false);

                break;
            case ast::BuiltinType::LENGTH:
                return {visit(*this, value)[1]};
            default:
                assert(false);
        }
    }

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

        switch(type.base()){
            case BaseType::BOOL:
            case BaseType::INT:{
                auto t1 = function->context->newTemporary();

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

                return {t1};
            }
            case BaseType::STRING:{
                auto t1 = function->context->newTemporary();
                auto t2 = function->context->newTemporary();

                executeCall(call, function, t1, t2);

                return {t1, t2};
            }
            default:
                throw SemanticalException("This function doesn't return anything");   
        }
    }

    result_type operator()(ast::VariableValue& value) const {
        auto type = value.Content->var->type();

{'fraction_non_alphanumeric': 0.16575075341251552, 'fraction_numerical': 0.006145482479465816, 'mean_word_length': 3.3361516781962592, 'pattern_counts': {'":': 0, '<': 93, '<?xml version=': 0, '>': 201, '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': '4186612', 'n_tokens_mistral': 5113, 'n_tokens_neox': 4914, 'n_words': 984}