← index
MasterX_System/compiler/ETL/interp.cpp
Source: MasterX_System/compiler/ETL/interp.cpp
#include"interp.hpp"

#include"embed_func.hpp"


namespace interp {

    Interpreter::Interpreter(const std::string &lib_path, symbol::SymbolTable &table) : sym_tab{table}, ip{0}, LIB_PATH{lib_path} {

    }
    
    void Interpreter::release() {
        lib::releaseSharedObjects();
    }

    int Interpreter::execute(ir::IRCode &code, bool debug) {
        collectLabels(code);
        if(label_pos.find("init") == label_pos.end()) {
            throw Exception("Error could not find init entry point.\n");
        }
        ip = label_pos["init"];
        while(ip < static_cast<long>(code.size())) {
            const auto instr = code[ip];
            if(debug)
                std::cout << instr.toString() << "\n";

            if(instr.type == ir::InstructionType::LABEL) {
                    executeLabel(instr);
                    ip++;
                    continue;
            }

            switch (instr.type) {
                case ir::InstructionType::ADD:
                    executeAdd(instr);
                    break;
                case ir::InstructionType::SUB:
                    executeSub(instr);
                    break;
                case ir::InstructionType::JUMP:
                    executeJump(instr);
                    continue;
                    break;
                case ir::InstructionType::MUL:
                    executeMul(instr);
                    break;
                case ir::InstructionType::DIV:
                    executeDiv(instr);
                    break;
                case ir::InstructionType::MOD:
                    executeMod(instr);
                    break;
                case ir::InstructionType::AND:
                    executeAnd(instr);
                    break;
                case ir::InstructionType::OR:
                    executeOr(instr);
                    break;
                case ir::InstructionType::XOR:
                    executeXor(instr);
                    break;
                case ir::InstructionType::LSHIFT:
                    executeLShift(instr);
                    break;
                case ir::InstructionType::RSHIFT:
                    executeRShift(instr);
                    break;
                case ir::InstructionType::LOAD_CONST:
                    executeLoadConst(instr);
                    break;
                case ir::InstructionType::LOAD_VAR:
                    executeLoadVar(instr);
                    break;
                case ir::InstructionType::SET:
                    executeSet(instr);
                    break;
                case ir::InstructionType::SET_CONST:
                    executeSetConst(instr);
                    break;
                case ir::InstructionType::ASSIGN:
                    executeAssignment(instr);
                    break;
                case ir::InstructionType::NEG:
                    executeNeg(instr);
                    break;
                case ir::InstructionType::NOT:
                    executeNot(instr);
                    break;
                case ir::InstructionType::LT:
                    executeLt(instr);
                    break;
                case ir::InstructionType::GT:
                    executeGt(instr);
                    break;
                case ir::InstructionType::LE:
                    executeLte(instr);
                    break;
                case ir::InstructionType::GE:
                    executeGte(instr);
                    break;
                case ir::InstructionType::EQ:
                    executeEq(instr);
                    break;
                case ir::InstructionType::NEQ:
                    executeNeq(instr);
                    break;
                case ir::InstructionType::LOGICAL_NOT:
                    executeLogicalNot(instr);
                    break;
                case ir::InstructionType::LOGICAL_AND:
                    executeLogicalAnd(instr);
                    break;
                case ir::InstructionType::LOGICAL_OR:
                    executeLogicalOr(instr);
                    break;
                case ir::InstructionType::CONCAT:
                    executeConcat(instr);
                    break;
                case ir::InstructionType::PARAM:
                case ir::InstructionType::PARAM_STRING:
                case ir::InstructionType::PARAM_POINTER:
                case ir::InstructionType::SUB_LABEL:
                    break;
                
                case ir::InstructionType::RETURN: {
                    executeReturn(instr);
                    continue;   
                }
                break;
                case ir::InstructionType::CALL: {
                    executeCall(instr);
                    continue;
                }
                break;
                default:
                    std::cerr << "Unsupported instruction: " << instr.toString() << std::endl;
                    break;
            }

            ip ++;
        }
        return EXIT_SUCCESS;
    }



    void Interpreter::collectLabels(const ir::IRCode &code) {
        int ip_id = 0;
        std::string curFunc;
        std::string curDefine;

        lf_table.addFunction("printf", lib::func_table["printf"]);
        lf_table.addFunction("sprintf", lib::func_table["sprintf"]);
#ifdef WITH_STATIC_SDL

        lib::initStatic();
#else

        #ifdef WITH_SDL
        lib::initSharedObject(LIB_PATH + "/libsdl_rt", "libsdl_rt_initTable");
        #endif
        lib::loadSharedObjects(LIB_PATH, LIB_PATH + "/etl-lib.txt"); // config file

#endif


        while(ip_id < code.size()) {
            const auto instr = code[ip_id];
            if(instr.type == ir::InstructionType::SUB_LABEL) {
                sub_labels[instr.dest] = ip_id;
                ip_id++;
                continue;
            } else if(instr.type == ir::InstructionType::LABEL) {
                label_pos[instr.dest] = ip_id;
                curFunc = instr.dest;
                ftable.enterFunction(curFunc);
                ip_id++;
                continue;
            } else if(instr.type == ir::InstructionType::PARAM) {
                ftable.addParam(instr.dest, ast::VarType::NUMBER);
            } else if(instr.type == ir::InstructionType::PARAM_STRING) {
                ftable.addParam(instr.dest, ast::VarType::STRING);
            } else if(instr.type == ir::InstructionType::PARAM_POINTER) {
                ftable.addParam(instr.dest, ast::VarType::POINTER);
            } else if(instr.type == ir::InstructionType::DEFINE) {
                curDefine = instr.dest;
                if(lib::func_table.find(instr.dest) != lib::func_table.end()) {
                    lf_table.addFunction(instr.dest, lib::func_table[instr.dest]);
                }
            } else if(instr.type == ir::InstructionType::DEF_PARAM) {
                lf_table.defineInteger(curDefine, instr.dest);
            } else if(instr.type == ir::InstructionType::DEF_PARAM_STRING) {
                lf_table.defineString(curDefine, instr.dest);
            } else if(instr.type == ir::InstructionType::DEF_PARAM_POINTER) {
                lf_table.definePointer(curDefine, instr.dest);
            }
            ip_id ++;
        }
    }

    void Interpreter::outputDebugInfo(std::ostream &out) {
        out << "Variales [ strings ]\n";
        for(auto &i : string_variables) {
            for(auto &x : i.second) {
                out << i.first << " [ " << x.first << ", " << x.second << " ]\n";
            }
        }
        out << "Variables [ numbers ]\n";
        for(auto &i : numeric_variables) {
            for(auto &x : i.second) {
                out << i.first << " [ " << x.first << ", " << x.second << " ]\n";
            }
        }
        out << "Variables [ pointers ]\n";
        for(auto &i : pointer_variables) {
            for(auto &x : i.second) {
                out << i.first << " [ " << x.first << ", " << std::hex << x.second << " ]\n";
            }
        }
        std::cout << "Labels: {\n";
        for(auto &i : label_pos) {
            out << i.second << " = " << i.first << "\n";
        }
        std::cout << "}\n";
        std::cout << "Sub Labels: {\n";
        for(auto &s : sub_labels) {
            out << s.second << " = " << s.first << "\n";
        }
        std::cout << "}\n";
        lf_table.print(std::cout);
    }

    void Interpreter::executeAdd(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 + val2;
    }

    void Interpreter::executeSub(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 - val2;
    }

    void Interpreter::executeMul(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 * val2;
    }

    void Interpreter::executeDiv(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        if(val2 == 0) {
            throw Exception("Divison By Zero");
        }
        numeric_variables[curFunction][instr.dest] = val1 / val2;
    }

    void Interpreter::executeMod(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 % val2;
    }

    void Interpreter::executeAnd(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 & val2;
    
    }

    void Interpreter::executeOr(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 | val2;
    }

    void Interpreter::executeXor(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 ^ val2;
    }

    void Interpreter::executeLShift(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 << val2;
    }

    void Interpreter::executeRShift(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 >> val2;
    }
    
    void Interpreter::executeLabel(const ir::IRInstruction &instr) {
        curFunction = instr.dest;
    }

    void Interpreter::executeLoadConst(const ir::IRInstruction &instr) {

        if(instr.op1[0] == '\"') {
            sym_tab.enter(instr.dest);
            auto it = sym_tab.lookup(instr.dest);
            if(it.has_value()) {
                symbol::Symbol *s = it.value();
                s->name = instr.dest;
                s->value = instr.op1;
                s->vtype = ast::VarType::STRING;
                string_variables[curFunction][s->name] = s->value;
            }
        } else {
            sym_tab.enter(instr.dest);
            auto it = sym_tab.lookup(instr.dest);
            if(it.has_value()) {
               symbol::Symbol *s = it.value();
               s->name = instr.dest;
               s->value = instr.op1;
               s->vtype = ast::VarType::NUMBER;
               numeric_variables[curFunction][s->name] = std::stol(instr.op1);
            }
        }
    }

    void Interpreter::executeSetConst(const ir::IRInstruction &instr) {
        auto loc = sym_tab.lookup(instr.dest);
        if(instr.op1[0] == '\"') {
            string_variables[curFunction][instr.dest] = instr.op1;
        } else {
            numeric_variables[curFunction][instr.dest] = std::stol(instr.op1);
        }
    }

    void Interpreter::executeAssignment(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc_src = sym_tab.lookup(instr.op1);
        auto loc_dst = sym_tab.lookup(instr.dest);
        if(loc_src.has_value()) {
            if(loc_src.value()->vtype == ast::VarType::STRING) {
                string_variables[curFunction][instr.dest] = string_variables[curFunction][instr.op1];
            } else if(loc_src.value()->vtype == ast::VarType::NUMBER) {
                numeric_variables[curFunction][instr.dest] = numeric_variables[curFunction][instr.op1];
            } else if(loc_src.value()->vtype == ast::VarType::POINTER) {
                pointer_variables[curFunction][instr.dest] =  pointer_variables[curFunction][instr.op1];   
            }
            loc_dst.value()->vtype = loc_src.value()->vtype;
        }
    }
    
    void Interpreter::executeLoadVar(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.op1);
        sym_tab.enter(instr.dest);
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_op1 = sym_tab.lookup(instr.op1);
        if(loc_dest.has_value()) {
            if(loc_op1.value()->vtype == ast::VarType::STRING) {
                string_variables[curFunction][instr.dest] = string_variables[curFunction][instr.op1];
            } else if(loc_op1.value()->vtype == ast::VarType::NUMBER) {
                numeric_variables[curFunction][instr.dest] = numeric_variables[curFunction][instr.op1];
            } else if(loc_op1.value()->vtype == ast::VarType::POINTER) {
                pointer_variables[curFunction][instr.dest] = pointer_variables[curFunction][instr.op1];
            }
            loc_dest.value()->vtype = loc_op1.value()->vtype;
        }
    }
    void Interpreter::executeSet(const ir::IRInstruction &instr) {
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value()) {
            if(loc.value()->vtype == ast::VarType::STRING) {
                string_variables[curFunction][instr.dest] = string_variables[curFunction][instr.op1];
            } else if(loc.value()->vtype == ast::VarType::NUMBER) {
                numeric_variables[curFunction][instr.dest] = numeric_variables[curFunction][instr.op1];
            } else if(loc.value()->vtype == ast::VarType::POINTER) {
                pointer_variables[curFunction][instr.dest] = pointer_variables[curFunction][instr.op1];
            }
        }
    }
    
    void Interpreter::executeNeg(const ir::IRInstruction  &instr) {
        sym_tab.enter(instr.dest);      
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_src = sym_tab.lookup(instr.op1);
        if(loc_dest.has_value() && loc_src.has_value() && loc_src.value()->vtype == ast::VarType::NUMBER) {
            numeric_variables[curFunction][instr.dest] = -numeric_variables[curFunction][instr.op1];
            loc_dest.value()->vtype = ast::VarType::NUMBER;
        } else {
            std::ostringstream stream;
            stream << "Neg Requires NUMBER variable: " << instr.op1 << "\n";
            throw Exception(stream.str());
        }
    }

    void Interpreter::executeNot(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);      
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_src = sym_tab.lookup(instr.op1);
        if(loc_dest.has_value() && loc_src.has_value() && loc_src.value()->vtype == ast::VarType::NUMBER) {
            numeric_variables[curFunction][instr.dest] = ~numeric_variables[curFunction][instr.op1];
            loc_dest.value()->vtype = ast::VarType::NUMBER;
        } else {
            std::ostringstream stream;
            stream << "Not Requires NUMBER variable: " << instr.op1 << "\n";
            throw Exception(stream.str());
        }
    }

    void Interpreter::executeLogicalNot(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);      
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_src = sym_tab.lookup(instr.op1);
        if(loc_dest.has_value() && loc_src.has_value()) {
            numeric_variables[curFunction][instr.dest] = !numeric_variables[curFunction][instr.op1];
        }
    }

    void Interpreter::executeLogicalAnd(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_op1 = sym_tab.lookup(instr.op1);
        auto loc_op2 = sym_tab.lookup(instr.op2);

        if(loc_op1.has_value() && loc_op2.has_value()) {
            if(loc_op1.value()->vtype == ast::VarType::NUMBER && loc_op2.value()->vtype == ast::VarType::NUMBER)
                numeric_variables[curFunction][instr.dest] = numeric_variables[curFunction][instr.op1] && numeric_variables[curFunction][instr.op2];
            else {
                std::ostringstream stream;
                stream << "Incorrect Variable type for Logical And &&: " << instr.op1 << " && " << instr.op2 << "\n";
                throw Exception(stream.str());    
            }

        } else {
            std::ostringstream stream;
            stream << "Undefined variable in " << instr.op1 << " && " << instr.op2;
            throw Exception(stream.str());
        }
    }
    void Interpreter::executeLogicalOr(const ir::IRInstruction &instr) {
       sym_tab.enter(instr.dest);
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_op1 = sym_tab.lookup(instr.op1);
        auto loc_op2 = sym_tab.lookup(instr.op2);

        if(loc_op1.has_value() && loc_op2.has_value()) {

            if(loc_op1.value()->vtype == ast::VarType::NUMBER && loc_op2.value()->vtype == ast::VarType::NUMBER)
                numeric_variables[curFunction][instr.dest] = numeric_variables[curFunction][instr.op1] || numeric_variables[curFunction][instr.op2];
            else {
                std::ostringstream stream;
                stream << "Incorrect Variable type for Logical Or ||: " << instr.op1 << " || " << instr.op2 << "\n";
                throw Exception(stream.str());
            }

        } else {
            std::ostringstream stream;
            stream << "Undefined variable in " << instr.op1 << " || " << instr.op2;
            throw Exception(stream.str());
        }
    }
        


    void Interpreter::executeCall(const ir::IRInstruction &instr) {
        
        if(ftable.isDefined(instr.functionName)) {        
            Function &func = ftable.getFunction(instr.functionName);
            sym_tab.enter(instr.dest);
            for(size_t i = 0; i < func.arg_names.size(); ++i) {
                std::string curScope = sym_tab.curScope();
                sym_tab.enterScope(instr.functionName);
                switch(func.arg_types[i]) {
                    case ast::VarType::NUMBER: {
                        sym_tab.enter(func.arg_names[i]);
                        auto loc = sym_tab.lookup(func.arg_names[i]);
                        if(loc.has_value()) {
                            loc.value()->vtype = ast::VarType::NUMBER;
                            numeric_variables[func.functionName][func.arg_names[i]] = numeric_variables[curFunction][instr.args[i]];
                        }
                    }
                    break;
                    case ast::VarType::STRING: {
                        sym_tab.enter(func.arg_names[i]);
                        auto loc = sym_tab.lookup(func.arg_names[i]);
                        if(loc.has_value()) {
                            loc.value()->vtype = ast::VarType::STRING;
                            string_variables[func.functionName][func.arg_names[i]] = string_variables[curFunction][instr.args[i]];
                        }
                    }
                    break;
                    case ast::VarType::POINTER: {
                        sym_tab.enter(func.arg_names[i]);
                        auto loc = sym_tab.lookup(func.arg_names[i]);
                        if(loc.has_value()) {
                            loc.value()->vtype = ast::VarType::POINTER;
                            pointer_variables[func.functionName][func.arg_names[i]] = pointer_variables[curFunction][instr.args[i]];
                            //std::cout << (long) pointer_variables[func.functionName][func.arg_names[i]];

                           
                        }
                    }
                    break;
                    default:
                    break;
                }
            }
            long pos = label_pos[instr.functionName];
            call_stack.push_back({curFunction, instr.dest, ip});
            ip = pos;      
        } else {
            Functor *f = lf_table.getFunction(instr.functionName);
            if(f == nullptr && instr.functionName != "printf") {
                std::ostringstream stream;
                stream << "Function: " << instr.functionName << " not defined!\n";
                throw Exception(stream.str());
            }
            if(instr.functionName != "printf" && instr.functionName != "sprintf" && f->int_vars.size() != instr.args.size()) {
                std::ostringstream stream;
                stream << "Function: " << instr.functionName << " requires: " << f->int_vars.size() << " arguments, found: " << instr.args.size();
                throw Exception(stream.str());
            }
            std::vector<Var> v;

            if(instr.functionName != "printf" && instr.functionName != "sprintf") {
                for(size_t i = 0; i < f->int_vars.size(); ++i) {
                    auto loc = sym_tab.lookup(instr.args[i]);
                    if(!loc.has_value()) {
                        std::ostringstream stream;
                        stream << instr.args[i] << " not found in symbol table!\n";
                        throw Exception(stream.str());
                    }
                    v.push_back(Var(instr.args[i], loc.value()->vtype));
                    size_t off = v.size()-1;
                    switch(v[off].type) {
                        case ast::VarType::NUMBER:
                        v[off].numeric_value = numeric_variables[curFunction][instr.args[i]];
                        break;
                        case ast::VarType::STRING:
                        v[off].string_value = stripQuotes(string_variables[curFunction][instr.args[i]]);
                        break;
                        case ast::VarType::POINTER:
                        v[off].ptr_value = pointer_variables[curFunction][instr.args[i]];
                        break;
                        default:
                        break;
                    }
                }
            } else {
                size_t offset = 0;
                if(instr.functionName == "printf" && instr.args.size() >= 1) {
                    v.push_back(Var(instr.args[0], ast::VarType::STRING));
                    size_t off = v.size()-1;
                    v[off].string_value = stripQuotes(string_variables[curFunction][instr.args[0]]);
                    offset = 1;
                }
                for(size_t i = offset; i < instr.args.size(); ++i) {
                    auto loc = sym_tab.lookup(instr.args[i]);
                    if(!loc.has_value()) {
                        std::ostringstream stream;
                        stream << instr.args[i] << " not found in symbol table!\n";
                        throw Exception(stream.str());
                    }
                    v.push_back(Var(instr.args[i], loc.value()->vtype));
                    size_t off = v.size()-1;
                    switch(v[off].type) {
                        case ast::VarType::NUMBER:
                        v[off].numeric_value = numeric_variables[curFunction][instr.args[i]];
                        break;
                        case ast::VarType::STRING:
                        v[off].string_value = stripQuotes(string_variables[curFunction][instr.args[i]]);
                        break;
                        case ast::VarType::POINTER:
                        v[off].ptr_value = pointer_variables[curFunction][instr.args[i]];
                        break;
                        default:
                        break;
                    }
                }
            }
            sym_tab.enter(instr.dest);
            Var v_ = std::move(lf_table.callFunction(instr.functionName, v));
            auto loc = sym_tab.lookup(instr.dest);
            switch(v_.type) {
                case ast::VarType::NUMBER:
                numeric_variables[curFunction][instr.dest] = v_.numeric_value;
                break;
                case ast::VarType::STRING:
                string_variables[curFunction][instr.dest] = v_.string_value;
                break;
                case ast::VarType::POINTER:
                pointer_variables[curFunction][instr.dest] = v_.ptr_value;
                break;
                default:
                break;
            }
            loc.value()->vtype = v_.type;
            ip++;
        }
    }
    
    void Interpreter::executeReturn(const ir::IRInstruction &instr) {
        if(call_stack.empty()) {
                auto loc = sym_tab.lookup(instr.dest);
                if(loc.has_value()) {
                    if(loc.value()->vtype == ast::VarType::NUMBER) {
                        throw Exit_Exception(numeric_variables[curFunction][instr.dest]);
                    } else {
                        std::ostringstream stream;
                        stream << "Final return should return a number for status " << instr.dest << "...\n";
                        throw Exception(stream.str());
                    }
                }
            } else {
                auto pos = call_stack.back();
                call_stack.pop_back();
                std::string curScope = sym_tab.curScope();
                auto rt_var = sym_tab.lookup(instr.dest);

                if(rt_var.has_value()) {
                    sym_tab.enterScope(pos.fname);
                    sym_tab.enter(pos.rt_name);
                    auto rt_dest = sym_tab.lookup(pos.rt_name);
                    if(rt_dest.has_value()) {
                        switch(rt_var.value()->vtype) {
                            case ast::VarType::NUMBER:
                                numeric_variables[pos.fname][pos.rt_name] = numeric_variables[curFunction][instr.dest];
                            break;
                            case ast::VarType::STRING:
                                string_variables[pos.fname][pos.rt_name] = string_variables[curFunction][instr.dest];
                            break;
                            case ast::VarType::POINTER:
                                pointer_variables[pos.fname][pos.rt_name] = pointer_variables[curFunction][instr.dest];
                                
                            break;
                            default:
                            break;
                        }
                        rt_dest.value()->vtype = rt_var.value()->vtype;
                    }
                }
                ip = pos.pos+1;
                curFunction = pos.fname;
           }
    }
    void Interpreter::executeJump(const ir::IRInstruction &instr) {
        if(instr.op1.empty() && instr.op2.empty()) {
            ip = sub_labels[instr.dest];
            return;
        }
        long op1 = getIntegerValue(instr.op1);
        if(op1 == 0) {
            ip = sub_labels[instr.dest];
        } else {
            ip++;
        }
    }

    long Interpreter::getIntegerValue(const std::string &operand) {
        if(isdigit(operand[0])) {
            return std::stol(operand);
        }
        return numeric_variables[curFunction][operand];
    }

    void Interpreter::executeConcat(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc_dest = sym_tab.lookup(instr.dest);
        auto loc_op1 = sym_tab.lookup(instr.op1);
        auto loc_op2 = sym_tab.lookup(instr.op2);
        if(loc_op1.has_value() && loc_op2.has_value() && loc_op1.value()->vtype == ast::VarType::STRING && loc_op2.value()->vtype == ast::VarType::STRING) {
            std::ostringstream stream;
            stream << '\"' << stripQuotes(string_variables[curFunction][instr.op1]) << stripQuotes(string_variables[curFunction][instr.op2]) << '\"';
            string_variables[curFunction][instr.dest] = stream.str();
            loc_dest.value()->vtype = ast::VarType::STRING;
        }
    }

    
    void Interpreter::executeLt(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 < val2;
    }
    
    void Interpreter::executeGt(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 > val2;
    }
    
    void Interpreter::executeLte(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 <= val2;
    }
    
    void Interpreter::executeGte(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 >= val2;
    }
    
    void Interpreter::executeEq(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 == val2;
    }
    
    void Interpreter::executeNeq(const ir::IRInstruction &instr) {
        sym_tab.enter(instr.dest);
        auto loc = sym_tab.lookup(instr.dest);
        if(loc.has_value())
            loc.value()->vtype = ast::VarType::NUMBER;
        long val1 = getIntegerValue(instr.op1);
        long val2 = getIntegerValue(instr.op2);
        numeric_variables[curFunction][instr.dest] = val1 != val2;
    }

    std::string Interpreter::stripQuotes(const std::string &value) {
        if(value[0] == '\"' && value.back() == '\"') {
            return value.substr(1, value.length()-2);
        } 
        return value; // nothing to do

    }


    void FunctionTable::enterFunction(const std::string &name) {
        curFunction = name;
        func[curFunction].functionName = name;
    }
    
    void FunctionTable::addParam(const std::string &name, ast::VarType type) {
        func[curFunction].arg_names.push_back(name);
        func[curFunction].arg_types.push_back(type);
    }

    Function &FunctionTable::getFunction(const std::string &name) {
        return func[name];
    }

    bool FunctionTable::isDefined(const std::string &f) {
        return func.find(f) != func.end();
    }

}