← index
libmx2/cmd/src/ast.cpp
Source: libmx2/cmd/src/ast.cpp
#include"ast.hpp"
#include"command.hpp"

namespace cmd {

    namespace {
        thread_local std::function<void(const std::string&)> g_updateCallback = nullptr;
    }

    CommandRegistry AstExecutor::registry;
    AstExecutor AstExecutor::instance;
    
    AstExecutor& AstExecutor::getExecutor() {
        return instance;
    } 

    void AstExecutor::setUpdateCallback(std::function<void(const std::string &)> callback) {
        g_updateCallback = std::move(callback);
    }

    void AstExecutor::execUpdateCallback(const std::string &text) {
        if (g_updateCallback) {
            g_updateCallback(text);
        }
    }

    AstExecutor::AstExecutor() {
        if(registry.empty()) {
            registry.registerTypedCommand("help", cmd::helpCommand);
            registry.registerTypedCommand("echo", cmd::echoCommand);
            registry.registerTypedCommand("test",cmd::testCommand);
            registry.registerTypedCommand("cmd", cmd::cmdCommand);
            registry.registerTypedCommand("cat", cmd::catCommand);
            registry.registerTypedCommand("grep", cmd::grepCommand);
            registry.registerCommand("exit", cmd::exitCommand);
            registry.registerCommand("quit", cmd::exitCommand);
            registry.registerCommand("print", cmd::printCommand);
            registry.registerTypedCommand("cd", cmd::cdCommand);
            registry.registerTypedCommand("ls", cmd::listCommand);
            registry.registerTypedCommand("dir", cmd::listCommand);
            registry.registerTypedCommand("find", cmd::findCommand);
            registry.registerTypedCommand("sort", cmd::sortCommand);
            registry.registerCommand("pwd", cmd::pwdCommand);
            registry.registerTypedCommand("mkdir", cmd::mkdirCommand);
            registry.registerTypedCommand("cp", cmd::cpCommand);
            registry.registerTypedCommand("mv", cmd::mvCommand);
            registry.registerTypedCommand("touch", cmd::touchCommand);
            registry.registerCommand("head", cmd::headCommand);
            registry.registerCommand("tail", cmd::tailCommand);
            registry.registerTypedCommand("wc", cmd::wcCommand);
            registry.registerTypedCommand("sed", cmd::sedCommand);
            registry.registerTypedCommand("printf", cmd::printfCommand);
            registry.registerTypedCommand("stringf", cmd::printfCommand);
            registry.registerTypedCommand("debug_set", cmd::debugSet);
            registry.registerTypedCommand("debug_get", cmd::debugGet);
            registry.registerCommand("debug_list", cmd::debugList);
            registry.registerTypedCommand("debug_clear", cmd::debugClear);
            registry.registerCommand("debug_clear_all", cmd::debugClearAll);
            registry.registerCommand("debug_search", cmd::debugSearch);
            registry.registerCommand("debug_dump", cmd::dumpVariables);
            registry.registerTypedCommand("visual", cmd::visualCommand);
            registry.registerTypedCommand("at", cmd::atCommand);
            registry.registerTypedCommand("len", cmd::lenCommand);
            registry.registerTypedCommand("index", cmd::indexCommand);
            registry.registerTypedCommand("strlen", cmd::strlenCommand);
            registry.registerTypedCommand("strfind", cmd::strfindCommand);
            registry.registerTypedCommand("strfindr", cmd::strfindrCommand);
            registry.registerTypedCommand("strtok", cmd::strtokCommand);
            registry.registerTypedCommand("exec", cmd::execCommand);
            registry.registerTypedCommand("cmdlist", cmd::commandListCommand);
            registry.registerTypedCommand("debug_cmd", cmd::commandListCommand);
            registry.registerTypedCommand("argv", cmd::argvCommand);
            registry.registerTypedCommand("extern", cmd::externCommand);
            registry.registerTypedCommand("extern_cleanup", cmd::externCleanupCommand);
            registry.registerTypedCommand("list_new", cmd::newListCommand);
            registry.registerTypedCommand("list_add", cmd::newListAddCommand);
            registry.registerTypedCommand("list_remove", cmd::newListRemoveCommand); 
            registry.registerTypedCommand("list_get", cmd::newListGetCommand);
            registry.registerTypedCommand("list_clear", cmd::newListClearCommand);
            registry.registerTypedCommand("list_add", cmd::newListAddCommand);
            registry.registerTypedCommand("list_remove", cmd::newListRemoveCommand);
            registry.registerTypedCommand("list_set", cmd::newListSetCommand);
            registry.registerTypedCommand("list_erase", cmd::newListClearCommand);
            registry.registerTypedCommand("list_clear", cmd::newListClearAllCommand);
            registry.registerTypedCommand("list_exists", newListExistsCommand);
            registry.registerTypedCommand("list_init", cmd::newListInitCommand);
            registry.registerTypedCommand("list_len", cmd::newListLenCommand);
            registry.registerTypedCommand("list_tokens", cmd::newListTokens);
            registry.registerTypedCommand("list_sort", cmd::newListSortCommand);
            registry.registerTypedCommand("list_reverse", cmd::newListReverseCommand);
            registry.registerTypedCommand("list_shuffle", cmd::newListShuffleCommand);
            registry.registerTypedCommand("list_copy", cmd::newListCopyCommand);
            registry.registerTypedCommand("list_pop", cmd::newListPopCommand);
            registry.registerTypedCommand("list_concat", cmd::concatListCommand);
            registry.registerTypedCommand("rand", cmd::newRandCommand);
            registry.registerTypedCommand("expr", cmd::exprCommand);
            registry.registerTypedCommand("getline", cmd::getLineCommand);
            registry.registerTypedCommand("regex_match", cmd::regexMatchCommand);
            registry.registerTypedCommand("regex_replace", cmd::regexReplaceCommand);
            registry.registerTypedCommand("regex_search", cmd::regexSearchCommand);
            registry.registerTypedCommand("regex_split", cmd::regexSplitCommand);
            registry.registerTypedCommand("tokenize", cmd::tokenizeCommand);
            registry.registerTypedCommand("set", [this](const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
                if (args.size() >= 1) {
                    if (args[0].value == "-e") {
                        setTerm(true);
                        output << "Early termination enabled" << std::endl;
                        return 0;
                    } else if (args[0].value == "+e") {
                        setTerm(false);
                        output << "Early termination disabled" << std::endl;
                        return 0;
                    }
                }
                output << "Usage: set [-e|+e] (enable or disable early termination)" << std::endl;
                return 1;
            });
        }
    }

    std::string VariableReference::evaluate(const AstExecutor& executor) const {
        auto value = executor.getVariable(name);
        fflush(stdout);
        return value.value_or("");
    }

    double VariableReference::evaluateNumber(const AstExecutor& executor) const {
        auto value = executor.getVariable(name);
        if (!value) return 0.0;
        try {
            return std::stod(value.value());
        } catch (const std::exception&) {
            return 0.0;
        }
    }

    double UnaryExpression::evaluateNumber(const AstExecutor& executor) const {
        if (op == INCREMENT || op == DECREMENT) {
            auto varRef = std::dynamic_pointer_cast<VariableReference>(operand);
            if (!varRef) {
                throw std::runtime_error("Increment/decrement can only be applied to variables");
            }
            
            auto valueOpt = executor.getVariable(varRef->name);
            double currentValue = 0.0;
            if (valueOpt) {
                try {
                    currentValue = std::stod(valueOpt.value());
                } catch (const std::exception&) {
                    
                }
            }
            
            double originalValue = currentValue;
            if (op == INCREMENT) {
                currentValue += 1.0;
            } else { 
                currentValue -= 1.0;
            }
            const_cast<AstExecutor&>(executor).setVariable(varRef->name, std::to_string(currentValue));
            return (position == PREFIX) ? currentValue : originalValue;
        } 
        else if (op == NEGATE) {
            return -operand->evaluateNumber(executor);
        }
        
        throw std::runtime_error("Unknown unary operator");
    }

     void AstExecutor::executeNode(const std::shared_ptr<cmd::Node>& node, std::istream& input, std::ostream& output) {
        if (returnSignal) {
            return;
        }
        if (auto returnStmt = std::dynamic_pointer_cast<cmd::Return>(node)) {
            executeReturn(returnStmt, input, output);
        } else if (auto cmdDef = std::dynamic_pointer_cast<cmd::CommandDefinition>(node)) {
            executeCommandDefinition(cmdDef, input, output);
        } else if (auto cmd = std::dynamic_pointer_cast<cmd::Command>(node)) {
            executeCommand(cmd, input, output);
        } 
        else if (auto seq = std::dynamic_pointer_cast<cmd::Sequence>(node)) {
            executeSequence(seq, input, output);
        }
        else if (auto pipe = std::dynamic_pointer_cast<cmd::Pipeline>(node)) {
            executePipeline(pipe, input, output);
        }
        else if (auto redir = std::dynamic_pointer_cast<cmd::Redirection>(node)) {
            executeRedirection(redir, input, output);
        }
        else if (auto varAssign = std::dynamic_pointer_cast<cmd::VariableAssignment>(node)) {
            executeVariableAssignment(varAssign, input, output);
        }
        else if (auto logicalAnd = std::dynamic_pointer_cast<cmd::LogicalAnd>(node)) {
            executeLogicalAnd(logicalAnd, input, output);
        }
        else if (auto logicalNot = std::dynamic_pointer_cast<LogicalNot>(node)) {
            executeLogicalNot(logicalNot, input, output);
        }
        else if (auto logicalOr = std::dynamic_pointer_cast<cmd::LogicalOr>(node)) {
            executeLogicalOr(logicalOr, input, output);
        }
        else if (auto expr = std::dynamic_pointer_cast<cmd::Expression>(node)) {
            if (auto unaryExpr = std::dynamic_pointer_cast<cmd::UnaryExpression>(expr)) {
                unaryExpr->evaluateNumber(*this); 
            } else {
                expr->evaluate(*this);  
            }
        }
        else if (auto cmdSubst = std::dynamic_pointer_cast<cmd::CommandSubstitution>(node)) {
            output << cmdSubst->evaluate(*this);
        }
        else if (auto ifStmt = std::dynamic_pointer_cast<cmd::IfStatement>(node)) {
            executeIfStatement(ifStmt, input, output);
        }
        else if (auto whileStmt = std::dynamic_pointer_cast<cmd::WhileStatement>(node)) {
            executeWhileStatement(whileStmt, input, output);
        }
        else if (auto forStmt = std::dynamic_pointer_cast<cmd::ForStatement>(node)) {
            executeForStatement(forStmt, input, output);
        }
        else if (auto breakStmt = std::dynamic_pointer_cast<cmd::Break>(node)) {
            throw BreakException();
        }
        else if (auto continueStmt = std::dynamic_pointer_cast<cmd::Continue>(node)) {
            throw ContinueException();
        }
        else {
            throw std::runtime_error("Unknown node type");
        }
    }
        

 

     std::string parseEscapeSequences(const std::string& input) {
        std::string result;
        for (size_t i = 0; i < input.length(); ++i) {
            if (input[i] == '\\' && i + 1 < input.length()) {
                switch (input[i + 1]) {
                    case 'n': result += '\n'; break;
                    case 't': result += '\t'; break;
                    case 'r': result += '\r'; break;
                    case 'b': result += '\b'; break;
                    case 'f': result += '\f'; break;
                    case 'v': result += '\v'; break;
                    case 'a': result += '\a'; break;
                    case '\\': result += '\\'; break;
                    case '\'': result += '\''; break;
                    case '"': result += '"'; break;
                    case '0': result += '\0'; break;
                    default: result += input[i + 1];
                }
                ++i;
            } else {
                result += input[i];
            }
        }
        return result;
    }
    
    std::string getArgTypeString(const Argument &arg) {
        switch(arg.type) {
            case ArgType::ARG_VARIABLE: return "Variable";
            case ArgType::ARG_STRING_LITERAL: return "String";
            case ArgType::ARG_LITERAL: return "Literal";      
            case ArgType::ARG_COMMAND_SUBST: return "Command Substitution";
            default: return "Unknown";
        }
        return "Unknown";
    }

    std::string getVar(const Argument &arg) {
        std::string a = arg.value;
        if(arg.type == ArgType::ARG_VARIABLE) {
            try {
                state::GameState  *s = state::getGameState();
                a = s->getVariable(arg.value);
                if(!a.empty())
                    a = parseEscapeSequences(a);
                
            } catch(const state::StateException &) {
                throw std::runtime_error("Variable name: " + arg.value + " not found its type: " +  getArgTypeString(arg));
            }
        } else if (arg.type == ARG_COMMAND_SUBST && arg.cmdNode) {
            AstExecutor &executor  = AstExecutor::getExecutor();
            std::stringstream input, output;
            executor.executeDirectly(arg.cmdNode, input, output);
            std::string result = output.str();
            while (!result.empty() && (result.back() == '\n' || result.back() == '\r')) {
                result.pop_back();
            }
            return result;
        }
        
        return a;
    }

}