← index
libmx2/cmd/src/command.cpp
Source: libmx2/cmd/src/command.cpp
#include"command.hpp"
#include<fstream>
#include<filesystem>
#include<regex>
#include<sstream>
#include<iomanip>
#include<cstdlib>
#include<random>
#include<algorithm>
#include<cctype>
#include"game_state.hpp"
#include"parser.hpp"
#include"html.hpp"
#include"command_reg.hpp"

#if !defined(__EMSCRIPTEN__) &&  !defined(_WIN32)
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/ioctl.h>
#ifdef __APPLE__
#include<util.h>
#else
#include<pty.h>
#endif
#include<sys/stat.h>
#endif
#if !defined(__EMSCRIPTEN__) && defined(_WIN32)
#include <windows.h>
#endif

#include<thread>
#include<mutex>
#include<atomic>

namespace state {
    GameState *getGameState() {
        static GameState gameState;
        return &gameState;
    }
}

namespace cmd {

    namespace {
        class PathPatternMatcher {
        public:
            static std::vector<std::string> expand(const std::string& token,
                                                   bool matchFiles,
                                                   bool matchDirectories,
                                                   bool keepUnmatched = true) {
                if (!containsWildcard(token)) {
                    return {token};
                }

                std::filesystem::path raw(token);
                const std::filesystem::path cwd = std::filesystem::current_path();
                std::vector<std::filesystem::path> candidates;

                if (raw.is_absolute()) {
                    candidates.push_back(raw.root_path());
                } else {
                    candidates.push_back(cwd);
                }

                std::filesystem::path relative = raw.is_absolute() ? raw.relative_path() : raw;
                std::vector<std::string> segments;
                for (const auto& part : relative) {
                    std::string segment = part.string();
                    if (!segment.empty()) {
                        segments.push_back(segment);
                    }
                }

                for (const auto& segment : segments) {
                    std::vector<std::filesystem::path> nextCandidates;

                    if (segment == ".") {
                        nextCandidates = candidates;
                    } else if (segment == "..") {
                        for (const auto& base : candidates) {
                            nextCandidates.push_back(base.parent_path());
                        }
                    } else if (!containsWildcard(segment)) {
                        for (const auto& base : candidates) {
                            std::filesystem::path candidate = base / segment;
                            if (std::filesystem::exists(candidate)) {
                                nextCandidates.push_back(candidate);
                            }
                        }
                    } else {
                        std::regex matcher = wildcardToRegex(segment);
                        for (const auto& base : candidates) {
                            if (!std::filesystem::exists(base) || !std::filesystem::is_directory(base)) {
                                continue;
                            }
                            try {
                                for (const auto& entry : std::filesystem::directory_iterator(base)) {
                                    if (std::regex_match(entry.path().filename().string(), matcher)) {
                                        nextCandidates.push_back(entry.path());
                                    }
                                }
                            } catch (const std::filesystem::filesystem_error&) {
                            }
                        }
                    }

                    candidates = dedupe(nextCandidates);
                    if (candidates.empty()) {
                        break;
                    }
                }

                std::vector<std::string> expanded;
                for (const auto& path : candidates) {
                    if (!std::filesystem::exists(path)) {
                        continue;
                    }
                    if (std::filesystem::is_directory(path) && !matchDirectories) {
                        continue;
                    }
                    if (std::filesystem::is_regular_file(path) && !matchFiles) {
                        continue;
                    }
                    if (!std::filesystem::is_directory(path) && !std::filesystem::is_regular_file(path)) {
                        continue;
                    }

                    if (raw.is_absolute()) {
                        expanded.push_back(path.lexically_normal().string());
                    } else {
                        std::filesystem::path relativePath = path.lexically_relative(cwd);
                        if (relativePath.empty()) {
                            expanded.push_back(path.lexically_normal().string());
                        } else {
                            expanded.push_back(relativePath.lexically_normal().string());
                        }
                    }
                }

                std::sort(expanded.begin(), expanded.end());
                expanded.erase(std::unique(expanded.begin(), expanded.end()), expanded.end());

                if (expanded.empty() && keepUnmatched) {
                    return {token};
                }
                return expanded;
            }

        private:
            static bool containsWildcard(const std::string& value) {
                return value.find('*') != std::string::npos;
            }

            static std::regex wildcardToRegex(const std::string& wildcardSegment) {
                std::string pattern;
                pattern.reserve(wildcardSegment.size() * 2);
                pattern += '^';

                for (char c : wildcardSegment) {
                    if (c == '*') {
                        pattern += ".*";
                    } else {
                        if (c == '.' || c == '^' || c == '$' || c == '|' || c == '(' || c == ')' ||
                            c == '[' || c == ']' || c == '{' || c == '}' || c == '+' || c == '?' || c == '\\') {
                            pattern += '\\';
                        }
                        pattern += c;
                    }
                }

                pattern += '$';
                return std::regex(pattern);
            }

            static std::vector<std::filesystem::path> dedupe(const std::vector<std::filesystem::path>& values) {
                std::vector<std::filesystem::path> result = values;
                std::sort(result.begin(), result.end(), [](const std::filesystem::path& left,
                                                           const std::filesystem::path& right) {
                    return left.lexically_normal().string() < right.lexically_normal().string();
                });
                result.erase(std::unique(result.begin(), result.end(), [](const std::filesystem::path& left,
                                                                           const std::filesystem::path& right) {
                    return left.lexically_normal().string() == right.lexically_normal().string();
                }), result.end());
                return result;
            }
        };

        class CommandArgumentValidator {
        public:
            static bool ensureQuotedStringOrVariable(const Argument& arg,
                                                     const std::string& commandName,
                                                     std::ostream& output,
                                                     const std::string& usageExample,
                                                     bool allowOptionLiteral = false) {
                if (arg.type == ARG_STRING_LITERAL || arg.type == ARG_COMMAND_SUBST) {
                    return true;
                }

                if (arg.type == ARG_VARIABLE) {
                    try {
                        (void)getVar(arg);
                        return true;
                    } catch (const std::runtime_error&) {
                        output << commandName << ": variable '" << arg.value << "' was not found." << std::endl;
                        output << "If this is a literal path/string, wrap it in quotes." << std::endl;
                        output << "Example: " << usageExample << std::endl;
                        return false;
                    }
                }

                if (arg.type == ARG_LITERAL) {
                    if (isIntegerLiteral(arg.value)) {
                        return true;
                    }
                    if (allowOptionLiteral && !arg.value.empty() && arg.value[0] == '-') {
                        return true;
                    }
                }

                output << commandName << ": unquoted string argument '" << arg.value << "'" << std::endl;
                output << "MXCMD reminder: non-variable string arguments must be wrapped in quotes." << std::endl;
                output << "Example: " << usageExample << std::endl;
                output << "Allowed without quotes: variables (ex: $path) and integers." << std::endl;
                return false;
            }

            static bool ensureAllQuotedStringOrVariable(const std::vector<Argument>& args,
                                                        const std::string& commandName,
                                                        std::ostream& output,
                                                        const std::string& usageExample,
                                                        bool allowOptionLiteral = false) {
                for (const auto& arg : args) {
                    if (!ensureQuotedStringOrVariable(arg, commandName, output, usageExample, allowOptionLiteral)) {
                        return false;
                    }
                }
                return true;
            }

        private:
            static bool isIntegerLiteral(const std::string& value) {
                if (value.empty()) {
                    return false;
                }
                size_t index = (value[0] == '+' || value[0] == '-') ? 1 : 0;
                if (index >= value.size()) {
                    return false;
                }
                for (; index < value.size(); ++index) {
                    if (!std::isdigit(static_cast<unsigned char>(value[index]))) {
                        return false;
                    }
                }
                return true;
            }
        };

        const std::vector<std::pair<std::string, std::string>>& builtinHelpEntries() {
            static const std::vector<std::pair<std::string, std::string>> entries = {
                {"help", "help [\"command\"]"},
                {"echo", "echo \"text\""},
                {"print", "print [--i|--f] value"},
                {"cat", "cat \"file\""},
                {"grep", "grep [--r|--e] \"pattern\" \"file\""},
                {"cd", "cd \"path\""},
                {"ls", "ls \"path\""},
                {"dir", "dir \"path\""},
                {"find", "find \"path\" \"pattern\""},
                {"sort", "sort \"file\""},
                {"pwd", "pwd"},
                {"mkdir", "mkdir [--p] \"dir\""},
                {"cp", "cp [--r] \"src\" \"dest\""},
                {"mv", "mv \"src\" \"dest\""},
                {"touch", "touch \"file\""},
                {"head", "head [--n N] \"file\""},
                {"tail", "tail [--n N] \"file\""},
                {"wc", "wc [--l] [--w] [--c] \"file\""},
                {"sed", "sed [--n] [--i] \"s/old/new/g\" \"file\""},
                {"printf", "printf \"format\" \"arg\" ..."},
                {"stringf", "stringf \"format\" \"arg\" ..."},
                {"test", "test value --eq value"},
                {"cmd", "cmd \"script.mx\" [args...]"},
                {"visual", "visual \"script.mx\" \"output.html\""},
                {"at", "at \"list\" index"},
                {"len", "len \"list\""},
                {"index", "index \"string\" start length"},
                {"strlen", "strlen \"string\""},
                {"strfind", "strfind start \"string\" \"search\""},
                {"strfindr", "strfindr start \"string\" \"search\""},
                {"strtok", "strtok \"string\" \"separator\""},
                {"exec", "exec \"shell command\""},
                {"cmdlist", "cmdlist"},
                {"debug_cmd", "debug_cmd"},
                {"argv", "argv \"length\" | argv index"},
                {"extern", "extern \"library\" \"function\" \"command\""},
                {"extern_cleanup", "extern_cleanup [--all|\"command\"]"},
                {"debug_set", "debug_set \"name\" value"},
                {"debug_get", "debug_get \"name\""},
                {"debug_list", "debug_list"},
                {"debug_clear", "debug_clear \"name\""},
                {"debug_clear_all", "debug_clear_all"},
                {"debug_search", "debug_search \"pattern\""},
                {"debug_dump", "debug_dump"},
                {"list_new", "list_new \"name\""},
                {"list_add", "list_add \"name\" \"value\""},
                {"list_remove", "list_remove \"name\" index"},
                {"list_get", "list_get \"name\" index"},
                {"list_set", "list_set \"name\" index \"value\""},
                {"list_clear", "list_clear \"name\""},
                {"list_erase", "list_erase \"name\""},
                {"list_exists", "list_exists \"name\""},
                {"list_init", "list_init \"name\" size \"value\""},
                {"list_len", "list_len \"name\""},
                {"list_tokens", "list_tokens \"name\""},
                {"list_sort", "list_sort \"name\""},
                {"list_reverse", "list_reverse \"name\""},
                {"list_shuffle", "list_shuffle \"name\""},
                {"list_copy", "list_copy \"from\" \"to\""},
                {"list_pop", "list_pop \"name\""},
                {"list_concat", "list_concat \"name1\" \"name2\""},
                {"rand", "rand min max"},
                {"expr", "expr \"expression\""},
                {"getline", "getline var"},
                {"regex_match", "regex_match \"pattern\" \"string\""},
                {"regex_replace", "regex_replace \"pattern\" \"replacement\" \"string\""},
                {"regex_search", "regex_search \"pattern\" \"string\""},
                {"regex_split", "regex_split \"pattern\" \"string\""},
                {"tokenize", "tokenize \"file\""},
                {"exit", "exit [code]"},
                {"quit", "quit [code]"}
            };
            return entries;
        }
    }

    std::vector<std::string> argv;
    std::string app_name;    
    std::string cmd_type = "cmd.exe /c ";

    int exitCommand(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        if(args.empty()) {
              throw Exit_Exception(0);
              return 0;
        } else if(args.size() == 1) {
            int exitCode = std::stoi(args[0]);
            throw Exit_Exception(exitCode);
        }
        throw Exit_Exception(0);
        return 0;     
    }

    int helpCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.size() > 1) {
            output << "Usage: help [\"command\"]" << std::endl;
            return 1;
        }

        const auto& entries = builtinHelpEntries();
        std::vector<std::pair<std::string, std::string>> sortedEntries(entries.begin(), entries.end());
        std::sort(sortedEntries.begin(), sortedEntries.end(),
                  [](const auto& left, const auto& right) {
                      return left.first < right.first;
                  });
        if (args.size() == 1) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "help", output, "help \"cd\"")) {
                return 1;
            }
            std::string requested = getVar(args[0]);
            bool found = false;
            for (const auto& entry : sortedEntries) {
                if (entry.first == requested) {
                    output << entry.first << " - " << entry.second << std::endl;
                    found = true;
                }
            }
            if (!found) {
                output << "help: no built-in command named '" << requested << "'" << std::endl;
                output << "Run help with no arguments to list all commands." << std::endl;
                return 1;
            }
            return 0;
        }

        output << "Built-in commands:" << std::endl;
        for (const auto& entry : sortedEntries) {
            output << "  " << std::left << std::setw(16) << entry.first << " " << entry.second << std::endl;
        }
        return 0;
    }
    
    int echoCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& stream) {
        std::ostringstream output;;
        for (size_t i = 0; i < args.size(); i++) {
            try {
                output << getVar(args[i]);
            } catch (const std::runtime_error &e) {
                output << args[i].value;
            }
            
            if (i < args.size() - 1) {
                output << " ";
            }
        }
        output << std::endl;
        
        if(&stream != &std::cout) {
            stream << output.str();
        } else  {
            printf("%s", output.str().c_str());
            fflush(stdout);
        }
        return 0;
    }

    int catCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            std::string line;
            while (std::getline(input, line)) {
                output << line << std::endl;
            }
            return 0; 
        } else {
            bool success = true;
            for (const auto& filename : args) {
                if (!CommandArgumentValidator::ensureQuotedStringOrVariable(filename, "cat", output, "cat \"file.txt\"")) {
                    return 1;
                }
                std::string fileToken = getVar(filename);
                auto expanded = PathPatternMatcher::expand(fileToken, true, false, true);
                for (const auto& filen_ : expanded) {
                    std::ifstream file(filen_);
                    if (!file) {
                        output << "cat: " << filen_ << ": No such file" << std::endl;
                        success = false;
                        continue;
                    }
                    std::string line;
                    while (std::getline(file, line)) {
                        output << line << std::endl;
                    }
                }
            }
    
            fflush(stdout);
            output.flush();
            return success ? 0 : 1; 
        }
    }

    int grepCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "grep: missing pattern" << std::endl;
            return 1;
        }

        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "grep", output, "grep \"pattern\" \"file.txt\"", true)) {
                return 1;
            }
        }
        
        bool useRegex = false;
        int patternIndex = 0;
        bool success = true;
        
        if (!args.empty() && (args[0].value ==  "--r" || args[0].value == "--e")) {
            useRegex = true;
            patternIndex = 1;
            
            if (static_cast<int>(args.size()) <= patternIndex) {
                output << "grep: missing pattern after " << args[0].value << std::endl;
                return 1;
            }
        }
        
        std::string patternStr = getVar(args[patternIndex]);
        std::vector<std::string> fileNames;
        for (size_t i = patternIndex + 1; i < args.size(); i++) {
            std::string file_n = getVar(args[i]);
            auto expanded = PathPatternMatcher::expand(file_n, true, false, true);
            fileNames.insert(fileNames.end(), expanded.begin(), expanded.end());
        }
        
        if (useRegex) {
            try {
                std::regex re(patternStr, std::regex::extended);
                std::string line;
                
                if (!fileNames.empty()) {
                    for (const auto& fileName : fileNames) {
                        std::ifstream file(fileName);
                        if (!file) {
                            output << "grep: " << fileName << ": No such file" << std::endl;
                            success = false;
                            continue;
                        }
                        
                        while (std::getline(file, line)) {
                            if (std::regex_search(line, re)) {
                                output << line << std::endl;
                            }
                        }
                    }
                } else {
                    while (std::getline(input, line)) {
                        if (std::regex_search(line, re)) {
                            output << line << std::endl;
                        }
                    }
                }
            } catch (const std::regex_error& e) {
                output << "grep: invalid regex pattern: " << patternStr << " - " << e.what() << std::endl;
                return 1;
            }
        } else {
            std::string line;
            if (!fileNames.empty()) {
                for (const auto& fileName : fileNames) {
                    std::ifstream file(fileName);
                    if (!file) {
                        output << "grep: " << fileName << ": No such file" << std::endl;
                        success = false;
                        continue;
                    }
                    
                    while (std::getline(file, line)) {
                        if (line.find(patternStr) != std::string::npos) {
                            output << line << std::endl;
                        }
                    }
                }
            } else {
                while (std::getline(input, line)) {
                    if (line.find(patternStr) != std::string::npos) {
                        output << line << std::endl;
                    }
                }
            }
        }
        
        return success ? 0 : 1;
    }

    int printCommand(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "print: missing variable name" << std::endl;
            return 1;
        }

        bool output_integer = false;
        bool success = true;
        
        for (const auto& arg : args) {
            if (arg == "--i") {
                output_integer = true;
                continue;
            } else if(arg == "--f") {
                output_integer = false;
                continue;
            } 
            else {
                if(output_integer == true) {
                    try {
                        output << std::stoi(arg) << std::endl;
                        output_integer = false;
                    } catch (const std::exception& e) {
                        output << "print: can't convert '" << arg << "' to integer" << std::endl;
                        success = false;
                    }
                } else {
                    output << arg << std::endl;
                }
            }
        }
        
        return success ? 0 : 1;
    }

    int cdCommand(const std::vector<Argument> &args, std::istream& input, std::ostream& output) {
        if (args.size() != 1) {
            output << "cd: expected exactly one argument" << std::endl;
            output << "cd: this command requires a single quoted string path." << std::endl;
            return 1;
        }
        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "cd", output, "cd \"sdl\"")) {
            return 1;
        }
        std::string path = getVar(args[0]);
        auto expanded = PathPatternMatcher::expand(path, false, true, false);
        if (expanded.empty()) {
            output << "cd: " << path << ": No matches found" << std::endl;
            return 1;
        }
        if (expanded.size() > 1) {
            output << "cd: " << path << ": matches multiple directories" << std::endl;
            for (const auto& match : expanded) {
                output << "  " << match << std::endl;
            }
            return 1;
        }
        path = expanded.front();
        try {
            std::filesystem::current_path(path);
            return 0;
        } catch (const std::filesystem::filesystem_error& e) {
            output << "cd: " << path << ": " << e.what() << std::endl;
            return 1;
        }
    }

    int listCommand(const std::vector<Argument> &args, std::istream& input, std::ostream& output) {
        std::vector<std::string> targets;
        if (args.empty()) {
            targets.push_back(".");
        } else {
            for (const auto& arg : args) {
                if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "ls", output, "ls \"path\"")) {
                    return 1;
                }
                std::string token = getVar(arg);
                auto expanded = PathPatternMatcher::expand(token, true, true, true);
                targets.insert(targets.end(), expanded.begin(), expanded.end());
            }
        }

        bool success = true;
        try {
            for (const auto& path : targets) {
                if (std::filesystem::is_directory(path)) {
                    for (const auto& entry : std::filesystem::directory_iterator(path)) {
                        output << entry.path().filename().string() << std::endl;
                    }
                } else if (std::filesystem::exists(path)) {
                    output << std::filesystem::path(path).filename().string() << std::endl;
                } else {
                    output << "ls: cannot access '" << path << "': No such file or directory" << std::endl;
                    success = false;
                }
            }
            return success ? 0 : 1;
        } catch (const std::filesystem::filesystem_error& e) {
            output << "ls: " << e.what() << std::endl;
            return 1;
        }
    }

    int sortCommand(const std::vector<Argument> &args, std::istream& input, std::ostream& output) {
        std::vector<std::string> lines;
        std::string line;
        
        if (args.empty()) {
            while (std::getline(input, line)) {
                lines.push_back(line);
            }
        } else {
            for (const auto& filename : args) {
                if (!CommandArgumentValidator::ensureQuotedStringOrVariable(filename, "sort", output, "sort \"file.txt\"")) {
                    return 1;
                }
                std::string token = getVar(filename);
                auto expanded = PathPatternMatcher::expand(token, true, false, true);
                for (const auto& file_n : expanded) {
                    std::ifstream file(file_n);
                    if (!file) {
                        output << "sort: " << file_n << ": No such file" << std::endl;
                        return 1;
                    }
                    while (std::getline(file, line)) {
                        lines.push_back(line);
                    }
                }
            }
        }
        std::sort(lines.begin(), lines.end());
        for (const auto& sortedLine : lines) {
            output << sortedLine << std::endl;
        }
        return 0;
    }

    int findCommand(const std::vector<Argument> &args, std::istream& input, std::ostream& output) {
        if (args.size() < 2) {
            output << "find: expected at least two arguments" << std::endl;
            return 1;
        }
        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "find", output, "find \"path\" \"pattern\"")) {
            return 1;
        }
        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[1], "find", output, "find \"path\" \"pattern\"")) {
            return 1;
        }
        std::string pathToken = getVar(args[0]);
        std::string pattern = getVar(args[1]);
        auto roots = PathPatternMatcher::expand(pathToken, false, true, false);
        if (roots.empty()) {
            output << "find: " << pathToken << ": No matches found" << std::endl;
            return 1;
        }

        bool success = true;
        try {
            for (const auto& root : roots) {
                for (const auto& entry : std::filesystem::recursive_directory_iterator(root)) {
                    if (entry.path().filename().string().find(pattern) != std::string::npos) {
                        output << entry.path().string() << std::endl;
                    }
                }
            }
            return success ? 0 : 1;
        } catch (const std::filesystem::filesystem_error& e) {
            output << "find: " << e.what() << std::endl;
            success = false;
        }
        return success ? 0 : 1;
    }

    int pwdCommand(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        try {
            output << std::filesystem::current_path().string() << std::endl;
            return 0;
        } catch (const std::filesystem::filesystem_error& e) {
            output << "pwd: " << e.what() << std::endl;
            return 1;
        }
    }

    int mkdirCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "mkdir: missing operand" << std::endl;
            return 1;
        }
        bool parents = false;
        std::vector<std::string> dirs;
        bool success = true;
        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "mkdir", output, "mkdir \"new_dir\"", true)) {
                return 1;
            }
            std::string a = getVar(arg);
            if (a == "--p") {
                parents = true;
            } else {
                dirs.push_back(a);
            }
        }
        for (const auto& dir : dirs) {
            try {
                if (parents) {
                    std::filesystem::create_directories(dir);
                } else {
                    std::filesystem::create_directory(dir);
                }
            } catch (const std::filesystem::filesystem_error& e) {
                output << "mkdir: cannot create directory '" << dir << "': " << e.what() << std::endl;
                success = false;
            }
        }
        return success ? 0 : 1;
    }

    int cpCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.size() < 2) {
            output << "cp: missing destination file operand after '" << (args.empty() ? "cp" : args[0].value) << "'" << std::endl;
            return 1;
        }

        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "cp", output, "cp \"src.txt\" \"dest.txt\"", true)) {
                return 1;
            }
        }
        
        bool recursive = false;
        std::vector<std::string> files;
        
        for (const auto& arg : args) {
            std::string a = getVar(arg);
            if (a == "--r" || a == "--R") {
                recursive = true;
            } else {
                files.push_back(a);
            }
        }
        
        if (files.size() < 2) {
            output << "cp: missing destination file operand" << std::endl;
            return 1;
        }
        
        std::string dest = files.back();
        files.pop_back();

        std::vector<std::string> expandedSources;
        for (const auto& src : files) {
            auto expanded = PathPatternMatcher::expand(src, true, true, true);
            expandedSources.insert(expandedSources.end(), expanded.begin(), expanded.end());
        }
        if (expandedSources.empty()) {
            output << "cp: missing source file operand" << std::endl;
            return 1;
        }
        
        bool success = true;
        for (const auto& src : expandedSources) {
            try {
                if (recursive && std::filesystem::is_directory(src)) {
                    std::filesystem::copy(src, dest, std::filesystem::copy_options::recursive);
                } else {
                    std::filesystem::copy(src, dest);
                }
            } catch (const std::filesystem::filesystem_error& e) {
                output << "cp: cannot copy '" << src << "' to '" << dest << "': " << e.what() << std::endl;
                success = false;
            }
        }
        
        return success ? 0 : 1;
    }

    int mvCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.size() < 2) {
            output << "mv: missing destination file operand after '" << (args.empty() ? "mv" : args[0].value) << "'" << std::endl;
            return 1;
        }

        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "mv", output, "mv \"src.txt\" \"dest.txt\"")) {
                return 1;
            }
        }
        
        std::string dest = getVar(args.back());

        std::vector<std::string> sources;
        for (size_t i = 0; i < args.size() - 1; ++i) {
            std::string token = getVar(args[i]);
            auto expanded = PathPatternMatcher::expand(token, true, true, true);
            sources.insert(sources.end(), expanded.begin(), expanded.end());
        }
        if (sources.empty()) {
            output << "mv: missing source file operand" << std::endl;
            return 1;
        }
        
        bool success = true;

        for (const auto& src : sources) {
            try {  
                std::filesystem::rename(src, dest);
            } catch (const std::filesystem::filesystem_error& e) {
                output << "mv: cannot move '" << src << "' to '" << dest << "': " << e.what() << std::endl;
                success = false;
            } 
        }
        
        return success ? 0 : 1;
    }

    int touchCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "touch: missing file operand" << std::endl;
            return 1;
        }
        bool success = true;
        for (const auto& file : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(file, "touch", output, "touch \"file.txt\"")) {
                return 1;
            }
            try {
                std::string token = getVar(file);
                auto expanded = PathPatternMatcher::expand(token, true, true, true);
                for (const auto& f : expanded) {
                    if (!std::filesystem::exists(f)) {
                        std::ofstream(f).close();
                    } else {
                        auto now = std::filesystem::file_time_type::clock::now();
                        std::filesystem::last_write_time(f, now);
                    }
                }
            } catch (const std::filesystem::filesystem_error& e) {
                output << "touch: cannot touch '" << file.value << "': " << e.what() << std::endl;
                success = false;
            } catch(const state::StateException &e) { }
        }

        return success ? 0 : 1;
    }

    int headCommand(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        int numLines = 10;
        std::vector<std::string> files;
        
        for (size_t i = 0; i < args.size(); ++i) {
            if (args[i] == "--n" && i + 1 < args.size()) {
                try {
                    numLines = std::stoi(args[++i]);
                } catch (const std::exception& e) {
                    output << "head: invalid number of lines: '" << args[i] << "'" << std::endl;
                    return 1;
                }
            } else {
                auto expanded = PathPatternMatcher::expand(args[i], true, false, true);
                files.insert(files.end(), expanded.begin(), expanded.end());
            }
        }
        
        bool success = true;
        
        if (files.empty()) {
            std::string line;
            int lineCount = 0;
            while (std::getline(input, line) && lineCount < numLines) {
                output << line << std::endl;
                lineCount++;
            }
        } else {
            for (const auto& file : files) {
                if (files.size() > 1) {
                    output << "==> " << file << " <==" << std::endl;
                }
                
                std::ifstream in(file);
                if (!in) {
                    output << "head: cannot open '" << file << "' for reading" << std::endl;
                    success = false;
                    continue;
                }
                
                std::string line;
                int lineCount = 0;
                while (std::getline(in, line) && lineCount < numLines) {
                    output << line << std::endl;
                    lineCount++;
                }
            }
        }
        
        return success ? 0 : 1;
    }

    int tailCommand(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        int numLines = 10;  
        std::vector<std::string> files;
        for (size_t i = 0; i < args.size(); ++i) {
            if (args[i] == "--n" && i + 1 < args.size()) {
                try {
                    numLines = std::stoi(args[++i]);
                } catch (const std::exception& e) {
                    output << "tail: invalid number of lines: '" << args[i] << "'" << std::endl;
                    return 1;
                }
            } else {
                auto expanded = PathPatternMatcher::expand(args[i], true, false, true);
                files.insert(files.end(), expanded.begin(), expanded.end());
            }
        }
        
        bool success = true;
        
        if (files.empty()) {
            std::vector<std::string> lines;
            std::string line;
            while (std::getline(input, line)) {
                lines.push_back(line);
            }
            
            size_t start = lines.size() > static_cast<size_t>(numLines) ? lines.size() - static_cast<size_t>(numLines) : 0;
            for (size_t i = start; i < lines.size(); ++i) {
                output << lines[i] << std::endl;
            }
        } else {
            for (const auto& file : files) {
                if (files.size() > 1) {
                    output << "==> " << file << " <==" << std::endl;
                }
                
                std::ifstream in(file);
                if (!in) {
                    output << "tail: cannot open '" << file << "' for reading" << std::endl;
                    success = false;
                    continue;
                }
                
                std::vector<std::string> lines;
                std::string line;
                while (std::getline(in, line)) {
                    lines.push_back(line);
                }
                
                size_t start = lines.size() > static_cast<size_t>(numLines) ? lines.size() -static_cast<size_t>(numLines) : 0;
                for (size_t i = start; i < lines.size(); ++i) {
                    output << lines[i] << std::endl;
                }
            }
        }
        
        return success ? 0 : 1;
    }

    int wcCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        bool countLines = false;
        bool countWords = false;
        bool countChars = false;
        std::vector<std::string> files;
    
        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "wc", output, "wc --l \"file.txt\"", true)) {
                return 1;
            }
            std::string a = getVar(arg);
            if (a == "--l") {
                countLines = true;
            } else if (a == "--w") {
                countWords = true;
            } else if (a == "--c") {
                countChars = true;
            } else {
                auto expanded = PathPatternMatcher::expand(a, true, false, true);
                files.insert(files.end(), expanded.begin(), expanded.end());
            }
        }
        
        if (!countLines && !countWords && !countChars) {
            countLines = countWords = countChars = true;
        }
        
        int totalLines = 0;
        int totalWords = 0;
        int totalChars = 0;
        
        auto processStream = [&](std::istream& in, const std::string& name) {
            int lineCount = 0;
            int wordCount = 0;
            int charCount = 0;
            
            std::string line;
            while (std::getline(in, line)) {
                lineCount++;
                charCount += line.length() + 1;  
                
                std::istringstream iss(line);
                std::string word;
                while (iss >> word) {
                    wordCount++;
                }
            }
            
            if (countLines) output << std::setw(8) << lineCount << " ";
            if (countWords) output << std::setw(8) << wordCount << " ";
            if (countChars) output << std::setw(8) << charCount << " ";
            output << name << std::endl;
            
            totalLines += lineCount;
            totalWords += wordCount;
            totalChars += charCount;
        };
        
        if (files.empty()) {
            processStream(input, "");
        } else {
            for (const auto& file : files) {
                std::ifstream in(file);
                if (!in) {
                    output << "wc: " << file << ": No such file or directory" << std::endl;
                    continue;
                }
                processStream(in, file);
            }
            
            if (files.size() > 1) {
                if (countLines) output << std::setw(8) << totalLines << " ";
                if (countWords) output << std::setw(8) << totalWords << " ";
                if (countChars) output << std::setw(8) << totalChars << " ";
                output << "total" << std::endl;
            }
        }

        return 0;
    }

    int sedCommand(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "Usage: sed [options] 's/pattern/replacement/[g]' [file]" << std::endl;
            output << "Options:" << std::endl;
            output << "  -n        Suppress automatic printing of pattern space" << std::endl;
            output << "  -i        Edit files in place" << std::endl;
            return 1;
        }

        bool suppressOutput = false;
        bool editInPlace = false;
        std::string expression;
        std::string filename;

        for (const auto& arg : args) {
            if (!CommandArgumentValidator::ensureQuotedStringOrVariable(arg, "sed", output, "sed \"s/old/new/g\" \"file.txt\"", true)) {
                return 1;
            }
        }
        
        for (const auto& arg : args) {
            std::string a = getVar(arg);
            if (a.size()>2 && a[0] == '-' && a[1] == '-') {
                if (a.find('n') != std::string::npos) {
                    suppressOutput = true;
                }
                if (a.find('i') != std::string::npos) {
                    editInPlace = true;
                }
            } else if (a[0] == 's' && a.find('/') == 1) {
                expression = a;
            } else {
                filename = a;
            }
        }

        if (!filename.empty()) {
            auto expanded = PathPatternMatcher::expand(filename, true, false, false);
            if (expanded.empty()) {
                output << "Error: Cannot open file '" << filename << "'" << std::endl;
                return 1;
            }
            if (expanded.size() > 1) {
                output << "Error: sed filename pattern matches multiple files:" << std::endl;
                for (const auto& match : expanded) {
                    output << "  " << match << std::endl;
                }
                return 1;
            }
            filename = expanded.front();
        }

        if (expression.empty() || expression[0] != 's') {
            output << "Error: Expected substitution expression (s/pattern/replacement/[g])" << std::endl;
            return 1;
        }

        size_t firstSlash = expression.find('/');
        if (firstSlash == std::string::npos) {
            output << "Error: Invalid substitution format" << std::endl;
            return 1;
        }
        
        size_t secondSlash = expression.find('/', firstSlash + 1);
        if (secondSlash == std::string::npos) {
            output << "Error: Invalid substitution format" << std::endl;
            return 1;
        }
        
        size_t thirdSlash = expression.find('/', secondSlash + 1);
        if (thirdSlash == std::string::npos) {
            output << "Error: Invalid substitution format" << std::endl;
            return 1;
        }
        
        std::string pattern = expression.substr(firstSlash + 1, secondSlash - firstSlash - 1);
        std::string replacement = expression.substr(secondSlash + 1, thirdSlash - secondSlash - 1);
        bool globalReplace = thirdSlash < expression.length() - 1 && expression[thirdSlash + 1] == 'g';
        
        try {
            std::regex regexPattern(pattern);
            std::ifstream inFile;
            std::stringstream outputBuffer;
            std::istream* inputStream = &input;
            
            if (!filename.empty()) {
                inFile.open(filename);
                if (!inFile.is_open()) {
                    output << "Error: Cannot open file '" << filename << "'" << std::endl;
                    return 1;
                }
                inputStream = &inFile;
            }
            
            std::string line;
            while (std::getline(*inputStream, line)) {
                std::string result;
                if (globalReplace) {
                    result = std::regex_replace(line, regexPattern, replacement);
                } else {
                    result = std::regex_replace(line, regexPattern, replacement, 
                                               std::regex_constants::format_first_only);
                }
                
                if (!suppressOutput) {
                    outputBuffer << result << std::endl;
                }
            }
            
            if (editInPlace && !filename.empty()) {
                std::ofstream outFile(filename);
                if (!outFile.is_open()) {
                    output << "Error: Cannot write to file '" << filename << "'" << std::endl;
                    return 1;
                }
                outFile << outputBuffer.str();
                outFile.close();
            } else {
                output << outputBuffer.str();
            }
            
            if (inFile.is_open()) {
                inFile.close();
            }
            
        } catch (const std::regex_error& e) {
            output << "Error in regular expression: " << e.what() << std::endl;
            return 1;
        }
        
        return 0;
    }

    int printfCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& stream_output) {
        std::ostringstream output;
        if (args.empty()) {
            output << "Usage: printf FORMAT [ARGUMENTS...]" << std::endl;
            output << "Print ARGUMENTS according to FORMAT" << std::endl;
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "printf", stream_output, "printf \"%s\" \"text\"")) {
            return 1;
        }
        std::vector<std::string> expandedArgs;
        for (const auto& arg : args) {
            expandedArgs.push_back(getVar(arg));
        }
        
        std::string formatStr = parseEscapeSequences(expandedArgs[0]);
        size_t argIndex = 1;
        size_t pos = 0;

        while (pos < formatStr.length()) {
            size_t percentPos = formatStr.find('%', pos);
            if (percentPos == std::string::npos) {
                output << formatStr.substr(pos);
                break;
            }

            output << formatStr.substr(pos, percentPos - pos);
            if (percentPos + 1 >= formatStr.length()) {
                output << '%'; 
                break;
            }

            char specifier = formatStr[percentPos + 1];
            
            std::string widthPrecision;
            size_t specPos = percentPos + 1;
            
            while (specPos < formatStr.length() && 
                   (std::isdigit(formatStr[specPos]) || 
                    formatStr[specPos] == '.' || 
                    formatStr[specPos] == '-' || 
                    formatStr[specPos] == '+')) {
                widthPrecision += formatStr[specPos];
                specPos++;
            }
            
            if (specPos > percentPos + 1) {
                specifier = formatStr[specPos];
            }
            
            switch (specifier) {
                case '%':
                    output << '%';
                    break;
                case 'd':
                case 'i':
                    if (argIndex < expandedArgs.size()) {
                        try {
                            int val = std::stoi(expandedArgs[argIndex++]);
                            output << val;
                        } catch (const std::exception&) {
                            output << "0";
                        }
                    }
                    break;
                case 'f':
                    if (argIndex < expandedArgs.size()) {
                        try {
                            double val = std::stod(expandedArgs[argIndex++]);
                            size_t precision = 6; 
                            size_t dotPos = widthPrecision.find('.');
                            if (dotPos != std::string::npos && dotPos + 1 < widthPrecision.length()) {
                                try {
                                    precision = std::stoul(widthPrecision.substr(dotPos + 1));
                                } catch (...) {}
                            }
                            
                            output << std::fixed << std::setprecision(precision) << val;
                        } catch (const std::exception&) {
                            output << "0.000000";
                        }
                    }
                    break;
                case 's':
                    if (argIndex < expandedArgs.size()) {
                        output << expandedArgs[argIndex++];
                    }
                    break;
                case 'x':
                    if (argIndex < expandedArgs.size()) {
                        try {
                            unsigned int val = std::stoul(expandedArgs[argIndex++]);
                            output << std::hex << val << std::dec;
                        } catch (const std::exception&) {
                            output << "0";
                        }
                    }
                    break;
                case 'X':
                    if (argIndex < expandedArgs.size()) {
                        try {
                            unsigned int val = std::stoul(expandedArgs[argIndex++]);
                            output << std::hex << std::uppercase << val << std::nouppercase << std::dec;
                        } catch (const std::exception&) {
                            output << "0";
                        }
                    }
                    break;
                case 'o':
                    if (argIndex < expandedArgs.size()) {
                        try {
                            unsigned int val = std::stoul(expandedArgs[argIndex++]);
                            output << std::oct << val << std::dec;
                        } catch (const std::exception&) {
                            output << "0";
                        }
                    }
                    break;
                case 'c':
                    if (argIndex < expandedArgs.size()) {
                        if (!expandedArgs[argIndex].empty()) {
                            output << expandedArgs[argIndex][0];
                        }
                        argIndex++;
                    }
                    break;
                default:
                    output << '%' << specifier;
            }
            
            pos = specPos + 1;
        }

        if(&stream_output != &std::cout) {
            stream_output << output.str();
            stream_output.flush();
        } else {
            printf("%s", output.str().c_str());
            fflush(stdout);
        }
        return 0;
    }

    int debugSet(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.size() != 2) {
            output << "Usage: debug set <variable> <value>" << std::endl;
            return 1;
        } 
        
        if(args[0].type != ArgType::ARG_VARIABLE) {
            output << "Usage: debug_set <variable> <value>\n";
            return 1;
        }
        std::string variable = args[0].value;
        std::string value = args[1].value;
        try {
            state::GameState *gameState = state::getGameState();
            if(args[1].type == ArgType::ARG_VARIABLE) {
                value = gameState->getVariable(args[1].value);
            }
            gameState->setVariable(variable, value);
        } catch(const state::StateException &e) {}
        output << "Set Debug " << variable << " to " << value << std::endl;
        return 0;
    }

    int debugGet(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if (args.size() != 1) {
            output << "Usage: debug_get <variable>" << std::endl;
            return 1;
        }
        if(args[0].type != ArgType::ARG_VARIABLE) {
            output << "Useage debug_get <varaible>\n";
            return 1;
        }
        std::string variable = args[0].value;
        state::GameState *gameState = state::getGameState();
        try {
            std::string value = gameState->getVariable(variable);
            
        } catch (const state::StateException &e) {
            output << "Error: " << e.what() << std::endl;
            return 1;
        }       
        return 0;
    }   

    int debugList(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        state::GameState *gameState = state::getGameState();
        gameState->listVariables(output);
        return 0;
    }

    int debugClear(const std::vector<Argument>& args, std::istream& input, std::ostream& output) {
        if(args.empty()) {
            output << "debugClear: Requires argument to clear.\n";
            return 1;
        }
        if(args[0].type != ArgType::ARG_VARIABLE) {
            output << "debugClear: Variable name required\n";
            return 1;
        }
        state::GameState *gameState = state::getGameState();
        gameState->clearVariable(args[0].value);
        output << "Cleared all Debug variables" << std::endl;
        return 0;
    }

    int debugClearAll(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        state::GameState *gameState = state::getGameState();
        gameState->clearVariables();
        output << "Cleared all Debug variables" << std::endl;
        return 0;
    }
 
    int debugSearch(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        int patternIndex = 0;
        std::string pattern = "";
        if (!args.empty() && (args[0] == "-r" || args[0] == "-e")) {
            patternIndex = 1;
            
            if (static_cast<int>(args.size()) <= patternIndex) {
                output << "grep: missing pattern after " << args[0] << std::endl;
                return 1;
            }
        } else if (!args.empty()) {
            pattern = args[0];
        }
        state::GameState *state = state::getGameState();
        state->searchVariables(output, pattern);
        return 0;
    }

    int dumpVariables(const std::vector<std::string>& args, std::istream& input, std::ostream& output) {
        if(!args.empty() && args.size() == 1) {
            state::GameState *state = state::getGameState();
            if(state->dumpVariables(args[0])) {
                output << "var table dumpped to: " << args[0] << "\n";
            } else {
                output << "var table dump failed\n";
                return 1;
            }
        }
        return 0;
    }

    int testCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            return 1; 
        }

        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "test", output, "test \"left\" --eq \"right\"", true)) {
            return 1;
        }
        if (args.size() == 2) {
            const std::string& op = args[0].value;
            const std::string value = getVar(args[1]);
            
            if (op == "--z") {
                return value.empty() ? 0 : 1; 
            } else if (op == "--n") {
                return !value.empty() ? 0 : 1;
            } else if (op == "--e") {
                return std::filesystem::exists(value) ? 0 : 1;
            } else if (op == "--f") {
                return (std::filesystem::exists(value) && 
                       std::filesystem::is_regular_file(value)) ? 0 : 1; 
            } else if (op == "--d") {
                       return (std::filesystem::exists(value) && 
                       std::filesystem::is_directory(value)) ? 0 : 1; 
            }
        }

        if (args.size() >= 3) {
            const std::string leftValue = getVar(args[0]);
            const std::string& op = args[1].value;
            const std::string rightValue = getVar(args[2]);
            if (op == "=") {
                return leftValue == rightValue ? 0 : 1; 
            } else if (op == "!=") {
                return leftValue != rightValue ? 0 : 1; 
            } else if (op == "--eq") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left == right ? 0 : 1; 
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else if (op == "--ne") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left != right ? 0 : 1; 
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else if (op == "--gt") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left > right ? 0 : 1; 
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else if (op == "--ge") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left >= right ? 0 : 1;
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else if (op == "--lt") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left < right ? 0 : 1; 
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else if (op == "--le") {
                try {
                    double left = std::stod(leftValue);
                    double right = std::stod(rightValue);
                    return left <= right ? 0 : 1;
                } catch (const std::exception&) {
                    output << "test: integer expression expected instead i found: " << leftValue << " " << rightValue << std::endl;
                    return 2;
                }
            } else {
                output << "test: unknown operator: " << op << std::endl;
                return 2;
            }
        }
        if (args.size() == 1) {
            return !getVar(args[0]).empty() ? 0 : 1;
        }
        return 1;
    }

    int cmdCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& output) {
        if (args.empty()) {
            output << "cmd: missing file operand" << std::endl;
            throw AstFailure("Exception: Script: execution failed missing operand.\n");
        }

        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "cmd", output, "cmd \"script.mx\"")) {
            throw AstFailure("Exception: Script: execution failed invalid argument format.\n");
        }

        std::string filename = getVar(args[0]);
        if (filename.size() >= 2 && 
            ((filename.front() == '"' && filename.back() == '"') || 
             (filename.front() == '\'' && filename.back() == '\''))) {
            filename = filename.substr(1, filename.size() - 2);
        }

        auto expandedScript = PathPatternMatcher::expand(filename, true, false, false);
        if (expandedScript.empty()) {
            output << "cmd: cannot open file '" << filename << "': No such file or directory" << std::endl;
            throw AstFailure("Exception: Script: " + filename + " execution failed. File not found\n");
        }
        if (expandedScript.size() > 1) {
            output << "cmd: script pattern matches multiple files:" << std::endl;
            for (const auto& match : expandedScript) {
                output << "  " << match << std::endl;
            }
            throw AstFailure("Exception: Script pattern matched multiple files.\n");
        }
        filename = expandedScript.front();

        cmd::AstExecutor &scriptExecutor = cmd::AstExecutor::getExecutor();
        std::filesystem::path resolvedPath(filename);
        if (!resolvedPath.is_absolute()) {
            std::string basePath = scriptExecutor.getPath();
            if (basePath.empty()) {
                basePath = std::filesystem::current_path().string();
            }
            resolvedPath = std::filesystem::path(basePath) / resolvedPath;
        }
        resolvedPath = resolvedPath.lexically_normal();

        cmd::app_name = resolvedPath.string();
        std::ifstream file(resolvedPath);
        if (!file.is_open()) {
            output << "cmd: cannot open file '" << resolvedPath.string() << "': No such file or directory" << std::endl;
            throw AstFailure("Exception: Script: " + resolvedPath.string() + " execution failed. File not found\n");
        }
        std::stringstream buffer;
        buffer << file.rdbuf();
        std::string script = buffer.str();
        if(!script.empty()) {
            script.erase(
                std::remove(script.begin(), script.end(), '\r'),
                    script.end()
            );
        }
        try {
            cmd::argv.clear();
            for(size_t i = 1; i < args.size(); ++i) {
                cmd::argv.push_back(getVar(args[i]));
            }
        } catch(state::StateException &e) {
            throw AstFailure("Exception: Script: State Exception execution failed.\n");
            return 1;
        }

        std::string previousPath = scriptExecutor.getPath();
        std::filesystem::path parent = resolvedPath.parent_path();
        if (parent.empty()) {
            scriptExecutor.setPath(std::filesystem::current_path().string());
        } else {
            scriptExecutor.setPath(parent.string());
        }

        struct CmdModeEnvGuard {
            std::string previous;
            bool hadPrevious = false;

            CmdModeEnvGuard() {
                const char* value = std::getenv("MXCMD_CMD_MODE");
                if (value != nullptr) {
                    hadPrevious = true;
                    previous = value;
                }
#ifdef _WIN32
                _putenv_s("MXCMD_CMD_MODE", "1");
#else
                setenv("MXCMD_CMD_MODE", "1", 1);
#endif
            }

            ~CmdModeEnvGuard() {
                if (hadPrevious) {
#ifdef _WIN32
                    _putenv_s("MXCMD_CMD_MODE", previous.c_str());
#else
                    setenv("MXCMD_CMD_MODE", previous.c_str(), 1);
#endif
                } else {
#ifdef _WIN32
                    _putenv_s("MXCMD_CMD_MODE", "");
#else
                    unsetenv("MXCMD_CMD_MODE");
#endif
                }
            }
        } cmdModeGuard;

        try {
            scan::TString string_buffer(script);
            scan::Scanner scanner(string_buffer);
            cmd::Parser parser(scanner);
            auto ast = parser.parse();

#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
            program_running = 1;
#endif
            scriptExecutor.execute(input, output, ast); 
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
            program_running = 0;
#endif
            scriptExecutor.setPath(previousPath);

            return 0;
        } catch (const scan::ScanExcept &e) {
            scriptExecutor.setPath(previousPath);
            output << "cmd: Scan error in " << resolvedPath.string() << ": " << e.why() << std::endl;
            return 1;
        } catch (const std::exception &e) {
            scriptExecutor.setPath(previousPath);
            output << "cmd: Error in " << resolvedPath.string() << ": " << e.what() << std::endl;
            return 1;
        } 
        catch(const AstFailure &e) {
            scriptExecutor.setPath(previousPath);
            throw AstFailure("Exception: Script: " + resolvedPath.string() + " execution failed: " + std::string(e.what()) + "\n");
        } 
        catch (...) {
            scriptExecutor.setPath(previousPath);
            output << "cmd: Unknown error occurred while executing " << resolvedPath.string() << std::endl;
            return 1;
        }
        return 0;
    }

    int visualCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& output) {
        if(args.empty() || args.size() != 2) {
            output << "Usage: visual script output_filel.\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "visual", output, "visual \"script.mx\" \"out.html\"")) {
            return 1;
        }
        std::string op1 = getVar(args[0]), op2 = getVar(args[1]);
        std::ifstream infile(op1);
        if(!infile.is_open()) {
            output << "Could not open file: " << op1 << "\n";
            return 1;
        }
        std::ostringstream stream;
        stream <<  infile.rdbuf();

        std::ofstream ofile(op2);
        if(!ofile.is_open()) {
            output << "Could not create output file:" << op2 << "\n";
            return 1;
        }

        try {
            scan::TString string_buffer(stream.str());
            scan::Scanner scanner(string_buffer);
            cmd::Parser parser(scanner);
            auto ast = parser.parse();
            html::gen_html(ofile, ast);
            return 0;
        }  
        catch (const scan::ScanExcept &e) {
            output << "cmd: Scan error in " << op1 << ": " << e.why() << std::endl;
            return 1;
        } catch (const std::exception &e) {
            output << "cmd: Error in " << op1 << ": " << e.what() << std::endl;
            return 1;
        }
        catch (...) {
            output << "cmd: Unknown error occurred while executing " << op1 << std::endl;
            return 1;
        }
        return 0;
    }

    int atCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty() || args.size() != 2) {
            output << "Usage: at <list> index\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "at", output, "at \"item1 item2\" 0")) {
            return 1;
        }

        std::string op1= getVar(args[0]);
        std::string op2= getVar(args[1]);
        std::istringstream stream(op1);
        std::vector<std::string> values;
        std::string line;
        while(std::getline(stream, line)) {
            values.push_back(line);
        }
        int index = std::stoi(op2);

        if(index >= 0 && index < static_cast<int>(values.size()))
            output << values.at(index);
        return 0;
    }
    int lenCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty() || args.size() != 1) {
            output << "Usage: len <list>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "len", output, "len \"a b c\"")) {
            return 1;
        }
        std::string op1 = getVar(args[0]);          
        
        if (op1.find('\n') != std::string::npos) {
        
            std::istringstream stream(op1);
            std::vector<std::string> values;
            std::string line;
            while(std::getline(stream, line)) {
                if (!line.empty()) {
                    values.push_back(line);
                }
            }
            output << values.size();
        } else {
        
            std::istringstream stream(op1);
            std::vector<std::string> values;
            std::string word;
            while(stream >> word) {
                values.push_back(word);
            }
            output << values.size();
        }
        return 0;
    }
    
    int indexCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty() || args.size() != 3) {
            output << "Usage: index <string> start leenn\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "index", output, "index \"hello\" 1 3")) {
            return 1;
        }

        std::string operands[3];
        operands[0] = getVar(args[0]);
        operands[1] = getVar(args[1]);
        operands[2] = getVar(args[2]);

        int val[2];
        val[0] = std::stoi(operands[1]);
        val[1] = std::stoi(operands[2]);

        if(val[0] >= 0 && val[0] < static_cast<int>(operands[0].length()))  {
            std::string temp = operands[0].substr(val[0], val[1]);
            output << temp;
            return 0;
        }
        return 1;
    }

    int strlenCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty() || args.size() != 1) {
            output <<  "Usage: strlen <string>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "strlen", output, "strlen \"hello\"")) {
            return 1;
        }
        std::string v = getVar(args[0]);
        output << v.length();
        return 0;
    }

    int strfindCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
         if(args.empty() && args.size() != 3) {
            output << "Usage: <start> <string> <search>\n";
            return 1;
         }
            if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "strfind", output, "strfind 0 \"hello world\" \"world\"")) {
                return 1;
            }

         std::string op1 = getVar(args[0]);
         std::string op2 = getVar(args[1]);
         std::string op3 = getVar(args[2]);

         int index = std::stoi(op1);
         auto pos = op2.find(op3, index);
         if(pos == std::string::npos)
            output << "-1";
        else
            output << pos;

         return 0;
    }

    int strfindrCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
         if(args.empty() && args.size() != 3) {
            output << "Usage: <start> <string> <search>\n";
            return 1;
         }
            if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "strfindr", output, "strfindr 0 \"hello world\" \"world\"")) {
                return 1;
            }

         std::string op1 = getVar(args[0]);
         std::string op2 = getVar(args[1]);
         std::string op3 = getVar(args[2]);

         int index = std::stoi(op1);
         auto pos = op2.rfind(op3, index);
         if(pos == std::string::npos)
            output << "-1";
        else
            output << pos;

         return 0;
    }

    int strtokCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty() || args.size() != 2) {
            output << "Usage: strtok string seperator\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "strtok", output, "strtok \"a,b,c\" \",\"")) {
            return 1;
        }
        std::string op1 = getVar(args[0]);
        std::string sep = getVar(args[1]);
        if(sep.empty()) {
            for(char c : op1) {
                output << c << std::endl;
            }
            return 0;
        }
        size_t pos = 0;
        size_t found;
        while((found = op1.find(sep, pos)) != std::string::npos) {
            output << op1.substr(pos, found - pos) << std::endl;
            pos = found + sep.length();
        }
        if(pos < op1.length()) {
            output << op1.substr(pos) << std::endl;
        }
        return 0;
    }

    int execCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        std::ostringstream all_args;
        for(auto &arg : args) {
            try {
                all_args << getVar(arg) << " ";
            } catch(const std::runtime_error &) {
                all_args << arg.value << " ";
            }
        }
        std::string command_str = all_args.str();
#if !defined(__EMSCRIPTEN__) && !defined(_WIN32) && defined(__linux__) || defined(__APPLE__)
        int master_fd, slave_fd;
        pid_t pid;
        
        if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
            output << "exec: failed to create pseudo-terminal" << std::endl;
            return 1;
        }
        
        pid = fork();
        if (pid < 0) {
            output << "exec: failed to fork process" << std::endl;
            close(master_fd);
            close(slave_fd);
            return 1;
        }
        
        if (pid == 0) {
            close(master_fd);
            setsid();
            ioctl(slave_fd, TIOCSCTTY, 0);
            
            dup2(slave_fd, STDIN_FILENO);
            dup2(slave_fd, STDOUT_FILENO);
            dup2(slave_fd, STDERR_FILENO);
            close(slave_fd);
            
            execl("/bin/sh", "sh", "-c", command_str.c_str(), NULL);
            exit(127);
        } else {
            close(slave_fd);
            
            int flags = fcntl(master_fd, F_GETFL, 0);
            fcntl(master_fd, F_SETFL, flags | O_NONBLOCK);
            
            output << std::unitbuf;
            
            bool still_running = true;
            std::string line_buffer;
            
            while (still_running) {
                if (cmd::AstExecutor::getExecutor().checkInterrupt()) {
                    std::cout << "exec: interrupting process" << std::endl;
                    if(killpg(pid, SIGINT) < 0) {
                        output << "exec: failed to kill process" << std::endl;
                    }
                    usleep(100000); 
                    if (waitpid(pid, NULL, WNOHANG) == 0) {
                        killpg(pid, SIGKILL);
                    }
                    close(master_fd);
                    still_running = false;
                    AstExecutor::getExecutor().setInterruptValue(false);
                    break;
                }
                
                fd_set read_fds;
                FD_ZERO(&read_fds);
                FD_SET(master_fd, &read_fds);
                
                struct timeval tv;
                tv.tv_sec = 0;
                tv.tv_usec = 10000;
                
                int ret = select(master_fd + 1, &read_fds, NULL, NULL, &tv);
                
                if (ret > 0) {
                    char buffer[4096];
                    ssize_t bytes_read;
                    
                    while ((bytes_read = read(master_fd, buffer, sizeof(buffer) - 1)) > 0) {
                        buffer[bytes_read] = '\0';
                        line_buffer += buffer;
                        
                        size_t pos = 0;
                        while ((pos = line_buffer.find('\n')) != std::string::npos) {
                            std::string line = line_buffer.substr(0, pos + 1);
                            if(&output == &std::cout) {
                                output << line;
                                output.flush();
                            } else {
                                AstExecutor::getExecutor().execUpdateCallback(line);
                            }
                            line_buffer.erase(0, pos + 1);
                        } 
                        if (line_buffer.length() > 40) {
                            if(&output == &std::cout) {
                                output << line_buffer;
                                output.flush();
                            } else {
                                AstExecutor::getExecutor().execUpdateCallback(line_buffer);
                            }
                            line_buffer.clear();
                        }
                    }   
                    if (bytes_read == 0) {
                        if (!line_buffer.empty()) {
                            if(&output == &std::cout) {
                                output << line_buffer;
                                output.flush();
                            } else {
                                AstExecutor::getExecutor().execUpdateCallback(line_buffer);
                            }
                        }
                        still_running = false;
                    }
                    
                    if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
                        still_running = false;
                    }
                    
                } else if (ret == 0) {
                    int status;
                    pid_t result = waitpid(pid, &status, WNOHANG);
                    if (result != 0) {
                        if (!line_buffer.empty()) {
                            if(&output == &std::cout) {
                                output << line_buffer;
                                output.flush();
                            } else {
                                AstExecutor::getExecutor().execUpdateCallback(line_buffer);
                            }
                        }
                        still_running = false;
                    }
                } else {
    
                    if (errno != EINTR) {
                        break;
                    }
                }
            }
        }
        int status;
        waitpid(pid, &status, 0);
        close(master_fd);    
        output << std::nounitbuf;
        return WEXITSTATUS(status);
#elif defined(_WIN32) && !defined(__EMSCRIPTEN__)
    std::istringstream iss(command_str);
    std::string first_word;
    iss >> first_word;
    
    if (first_word.ends_with(".exe") || first_word.ends_with(".EXE")) {
        SECURITY_ATTRIBUTES sa;
        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = TRUE;
        sa.lpSecurityDescriptor = NULL;

        HANDLE hStdOutRead, hStdOutWrite;
        if (!CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 0)) {
            output << "exec: failed to create output pipe" << std::endl;
            return 1;
        }
        SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0);

        HANDLE hStdInRead, hStdInWrite;
        if (!CreatePipe(&hStdInRead, &hStdInWrite, &sa, 0)) {
            CloseHandle(hStdOutRead);
            CloseHandle(hStdOutWrite);
            output << "exec: failed to create input pipe" << std::endl;
            return 1;
        }
        SetHandleInformation(hStdInWrite, HANDLE_FLAG_INHERIT, 0);

        STARTUPINFO si;
        ZeroMemory(&si, sizeof(STARTUPINFO));
        si.cb = sizeof(STARTUPINFO);
        si.hStdInput = hStdInRead;
        si.hStdOutput = hStdOutWrite;
        si.hStdError = hStdOutWrite;
        si.dwFlags |= STARTF_USESTDHANDLES;

        PROCESS_INFORMATION pi;
        ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

        if (!CreateProcess(NULL, const_cast<LPSTR>(command_str.c_str()), 
                          NULL, NULL, TRUE, CREATE_NO_WINDOW, 
                          NULL, NULL, &si, &pi)) {
            CloseHandle(hStdOutRead);
            CloseHandle(hStdOutWrite);
            CloseHandle(hStdInRead);
            CloseHandle(hStdInWrite);
            output << "exec: failed to create process: " << command_str << std::endl;
            return 1;
        }

        CloseHandle(hStdOutWrite);
        CloseHandle(hStdInRead);
        CloseHandle(hStdInWrite);

        bool isStdOut = (&output == &std::cout);
        
        bool still_running = true;
        
        while (still_running) {
            DWORD bytesAvailable = 0;
            if (!PeekNamedPipe(hStdOutRead, NULL, 0, NULL, &bytesAvailable, NULL)) {
                still_running = false;
                break;
            }
            
            if (bytesAvailable > 0) {
                char buffer[256];
                DWORD bytesRead = 0;
                
                if (ReadFile(hStdOutRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) {
                    buffer[bytesRead] = '\0';

                    if (isStdOut) {
                        printf("%s", buffer);
                        fflush(stdout);
                    } else {
                        output << buffer;
                        output.flush();
                    }
                } else {
                    still_running = false;
                    break;
                }
            } else {
                DWORD currentChildExitCode;
                if (GetExitCodeProcess(pi.hProcess, &currentChildExitCode)) {
                    if (currentChildExitCode != STILL_ACTIVE) {
                        still_running = false;
                    }
                } else {
                    still_running = false;
                    break;
                }
                
                if (still_running) {
                    Sleep(10);
                }
            }
        }
        
        DWORD final_child_exit_code = 0;
        WaitForSingleObject(pi.hProcess, INFINITE);
        GetExitCodeProcess(pi.hProcess, &final_child_exit_code);
        
        CloseHandle(hStdOutRead);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        
        return static_cast<int>(final_child_exit_code);
    } else {
        std::string fullCommand = cmd::cmd_type + " " + command_str;
        
        SECURITY_ATTRIBUTES sa;
        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = TRUE;
        sa.lpSecurityDescriptor = NULL;

        HANDLE hStdOutRead, hStdOutWrite;
        if (!CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 0)) {
            output << "exec: failed to create output pipe" << std::endl;
            return 1;
        }
        SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0);

        HANDLE hStdInRead, hStdInWrite;
        if (!CreatePipe(&hStdInRead, &hStdInWrite, &sa, 0)) {
            CloseHandle(hStdOutRead);
            CloseHandle(hStdOutWrite);
            output << "exec: failed to create input pipe" << std::endl;
            return 1;
        }
        SetHandleInformation(hStdInWrite, HANDLE_FLAG_INHERIT, 0);

        STARTUPINFO si;
        ZeroMemory(&si, sizeof(STARTUPINFO));
        si.cb = sizeof(STARTUPINFO);
        si.hStdInput = hStdInRead;
        si.hStdOutput = hStdOutWrite;
        si.hStdError = hStdOutWrite;
        si.dwFlags |= STARTF_USESTDHANDLES;

        PROCESS_INFORMATION pi;
        ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

        if (!CreateProcess(NULL, const_cast<LPSTR>(fullCommand.c_str()), 
                          NULL, NULL, TRUE, CREATE_NO_WINDOW, 
                          NULL, NULL, &si, &pi)) {
                             DWORD error = GetLastError();
            printf("exec: failed to create process: %s Error: %ld\n ",fullCommand.c_str(), error);
            fflush(stdout);
   
            CloseHandle(hStdOutRead);
            CloseHandle(hStdOutWrite);
            CloseHandle(hStdInRead);
            CloseHandle(hStdInWrite);
            output << "exec: failed to create process: " << fullCommand << std::endl;
            return 1;
        }

        CloseHandle(hStdOutWrite);
        CloseHandle(hStdInRead);
        CloseHandle(hStdInWrite);

        
        bool isStdOut = (&output == &std::cout);
        bool still_running = true;
        
        while (still_running) {
            DWORD bytesAvailable = 0;
            if (!PeekNamedPipe(hStdOutRead, NULL, 0, NULL, &bytesAvailable, NULL)) {
                still_running = false;
                break;
            }
            
            if (bytesAvailable > 0) {
                char buffer[256];
                DWORD bytesRead = 0;
                
                if (ReadFile(hStdOutRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) {
                    buffer[bytesRead] = '\0';

                    if (isStdOut) {
                        printf("%s", buffer);
                        fflush(stdout);
                    } else {
                        output << buffer;
                        output.flush();
                    }
                } else {
                    still_running = false;
                    break;
                }
            } else {
                DWORD currentChildExitCode;
                if (GetExitCodeProcess(pi.hProcess, &currentChildExitCode)) {
                    if (currentChildExitCode != STILL_ACTIVE) {
                        still_running = false;
                    }
                } else {
                    still_running = false;
                    break;
                }
                
                if (still_running) {
                    Sleep(10);
                }
            }
        }
        
        DWORD final_child_exit_code = 0;
        WaitForSingleObject(pi.hProcess, INFINITE);
        GetExitCodeProcess(pi.hProcess, &final_child_exit_code);
        
        CloseHandle(hStdOutRead);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        
        return static_cast<int>(final_child_exit_code);
    }
#endif
    }

    int commandListCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        AstExecutor::printCommandInfo(output);
        return 0;
    }

    int argvCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            output << "Usage: argv <index>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "argv", output, "argv \"length\"")) {
            return 1;
        }
        if(args[0].type == ArgType::ARG_STRING_LITERAL && args[0].value == "length") {
            output << argv.size();
            return 0;
        } else if(args[0].type == ArgType::ARG_STRING_LITERAL && args[0].value == "all") {
            for (size_t i = 0; i < argv.size(); ++i) {
                output << argv.at(i) << "\n";
            }
            return 0;
        } else if(args[0].type == ArgType::ARG_STRING_LITERAL && args[0].value == "app") {
            output << app_name;
            return 0;
        } 
        else if(args[0].type == ArgType::ARG_STRING_LITERAL) {
            output << "argv: invalid argument: " << args[0].value << "\n";
            return 1;
        }
        int index = std::stoi(getVar(args[0]));
        if (index >= 0 && index < static_cast<int>(argv.size())) {
            output << argv.at(index);
        } else {
            output << "argv: index out of range\n";
            return 1;
        }
        return 0;
    }

    int externCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
 
        if(args.empty() || args.size() != 3) {
            output << "Usage: extern <library> <function> <command_name>\n";
            output << "You have: ";
            for(auto &arg : args) {
                output << arg.value << " ";
            }
            output << "\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "extern", output, "extern \"libname.so\" \"func\" \"cmdname\"")) {
            return 1;
        }
        std::string libPath = getVar(args[0]);
        std::string funcName = getVar(args[1]);
        std::string cmdName = getVar(args[2]);

        std::string libLookup = libPath;

        std::vector<std::string> lookupCandidates;
        auto addCandidate = [&lookupCandidates](const std::filesystem::path& value) {
            std::string normalized = value.lexically_normal().string();
            for (const auto& existing : lookupCandidates) {
                if (existing == normalized) {
                    return;
                }
            }
            lookupCandidates.push_back(normalized);
        };

        try {
            std::filesystem::path requested(libLookup);
            if (requested.is_absolute()) {
                addCandidate(requested);
            } else {
                addCandidate(std::filesystem::absolute(requested));
            }

            std::filesystem::path appDir = std::filesystem::path(app_name).parent_path();
            if (!appDir.empty()) {
                addCandidate(std::filesystem::absolute(appDir / requested));
                addCandidate(std::filesystem::absolute(appDir / requested.filename()));
            }

#if defined(__linux__)
            addCandidate(std::filesystem::path("/usr/local/lib") / requested);
            addCandidate(std::filesystem::path("/usr/local/lib") / requested.filename());
            if (!requested.has_extension()) {
                std::string baseName = requested.filename().string();
                if (baseName.rfind("lib", 0) == 0) {
                    addCandidate(std::filesystem::path("/usr/local/lib") / (baseName + ".so"));
                } else {
                    addCandidate(std::filesystem::path("/usr/local/lib") / ("lib" + baseName + ".so"));
                }
            }
#elif defined(__APPLE__)
            addCandidate(std::filesystem::path("/usr/local/lib") / requested);
            addCandidate(std::filesystem::path("/usr/local/lib") / requested.filename());
            if (!requested.has_extension()) {
                std::string baseName = requested.filename().string();
                if (baseName.rfind("lib", 0) == 0) {
                    addCandidate(std::filesystem::path("/usr/local/lib") / (baseName + ".dylib"));
                } else {
                    addCandidate(std::filesystem::path("/usr/local/lib") / ("lib" + baseName + ".dylib"));
                }
            }
#endif

#ifdef _WIN32
            char modulePath[MAX_PATH] = {0};
            DWORD modulePathLen = GetModuleFileNameA(nullptr, modulePath, MAX_PATH);
            if (modulePathLen > 0 && modulePathLen < MAX_PATH) {
                std::filesystem::path exeDir = std::filesystem::path(modulePath).parent_path();
                addCandidate(std::filesystem::absolute(exeDir / requested));
                addCandidate(std::filesystem::absolute(exeDir / requested.filename()));
            }
#endif
        } catch (...) {
            lookupCandidates.push_back(libLookup);
        }

        std::vector<std::string> orderedCandidates;
#ifdef _WIN32
        for (const auto& candidate : lookupCandidates) {
            try {
                std::filesystem::path dllPath = std::filesystem::path(candidate + ".dll");
                if (std::filesystem::exists(dllPath)) {
                    orderedCandidates.push_back(candidate);
                }
            } catch (...) {
            }
        }
        for (const auto& candidate : lookupCandidates) {
            if (std::find(orderedCandidates.begin(), orderedCandidates.end(), candidate) == orderedCandidates.end()) {
                orderedCandidates.push_back(candidate);
            }
        }
#else
        orderedCandidates = lookupCandidates;
#endif

        auto &reg = AstExecutor::getRegistry();
        std::shared_ptr<Library> lib;
        std::string loadedLibraryPath = libPath;
        for (const auto& candidate : orderedCandidates) {
            std::shared_ptr<Library> &candidateLib = reg.setLibrary(candidate);
            if (candidateLib) {
                lib = candidateLib;
                loadedLibraryPath = candidate;
                break;
            }
        }

        if(!lib) {
            std::cerr << "extern: failed to load library " << libPath << "\n";
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
            program_running = 0;
#else
            //exit(0);
#endif
            return 1;
        }
        if(!lib->hasSymbol(funcName)) {
            std::cerr << "extern: function " << funcName << " not found in library " << libPath << "\n";
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
            program_running = 0;
#else
            //exit(0);
#endif
            return 1;
        }
        ExternCommandInfo info;
        info.library = lib;
        info.func = lib->getFunction<plugin_func_t>(funcName);
        info.functionName = funcName;
        info.libraryPath = loadedLibraryPath;

        if(info.func) {
            reg.registerExternCommand(cmdName, info);            
            return 0;
        } else {
            std::cerr << "extern: failed to get function pointer for " << funcName << "\n";
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
            program_running = 0;
#else
            //exit(0);
#endif
            return 1;
        }
    }

    int externCleanupCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        auto& reg = AstExecutor::getRegistry();

        if (args.empty()) {
            std::size_t removedCommands = reg.clearExternCommands();
            std::size_t removedLibraries = reg.pruneLibraries();
            output << "extern_cleanup: removed " << removedCommands << " command(s), released "
                   << removedLibraries << " librar(y/ies)" << std::endl;
            return 0;
        }

        if (args.size() != 1) {
            output << "Usage: extern_cleanup [--all|<command_name>]" << std::endl;
            return 1;
        }

        if (!CommandArgumentValidator::ensureQuotedStringOrVariable(args[0], "extern_cleanup", output, "extern_cleanup \"command_name\"", true)) {
            return 1;
        }

        std::string arg = getVar(args[0]);
        if (arg == "--all" || arg == "all") {
            std::size_t removedCommands = reg.clearExternCommands();
            std::size_t removedLibraries = reg.pruneLibraries();
            output << "extern_cleanup: removed " << removedCommands << " command(s), released "
                   << removedLibraries << " librar(y/ies)" << std::endl;
            return 0;
        }

        bool removed = reg.unregisterExternCommand(arg);
        std::size_t removedLibraries = reg.pruneLibraries();
        if (!removed) {
            output << "extern_cleanup: command not found: " << arg << std::endl;
            return 1;
        }

        output << "extern_cleanup: removed command '" << arg << "', released "
               << removedLibraries << " librar(y/ies)" << std::endl;
        return 0;
    }

    
    int newListCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            output << "Usage: list_new <name>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_new", output, "list_new \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (gameState->hasList(name)) {
            output << "Error: List '" << name << "' already exists." << std::endl;
            return 1;
        }
        gameState->createList(name);
        if(args.size() > 2) {
            std::string value = getVar(args[2]);
            std::string value2 = getVar(args[1]);
            gameState->initList(name, value, std::stoul(value2));
        }
        return 0;
    }
    int newListAddCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.size() < 2) {
            output << "Usage: list_add <name> <value>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_add", output, "list_add \"my_list\" \"value\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        std::string value = getVar(args[1]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            output << "Error: List '" << name << "' already exists." << std::endl;
            return 1;
        }
        gameState->addToList(name, value);
        return 0;
    }
    int newListRemoveCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.size() < 2) {
            output << "Usage: list_remove <name> <value>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_remove", output, "list_remove \"my_list\" 0")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        std::string value = getVar(args[1]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            output << "Error: List '" << name << "' already exists." << std::endl;
            return 1;
        }
        size_t val = std::stoul(value);
        gameState->removeFromList(name, val);
        return 0;
    }
    int newListGetCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.size() < 2) {
            throw cmd::AstFailure("Usage: list_get <name> <index>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_get", output, "list_get \"my_list\" 0")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        std::string indexStr = getVar(args[1]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        int index = std::stoi(indexStr);
        std::string value = gameState->getFromList(name, index);
        if (value.empty()) {
            throw cmd::AstFailure( "Error: Index out of range.");
            return 1;
        }
        output << value;
        return 0;
    }

    int newListSetCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.size() < 3) {
            output << "Usage: list_set <name> <index> <value>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_set", output, "list_set \"my_list\" 0 \"value\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        std::string indexStr = getVar(args[1]);
        std::string value = getVar(args[2]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            output << "Error: List '" << name << "' does not exist." << std::endl;
            return 1;
        }
        size_t index = std::stoul(indexStr);
        gameState->setList(name, index, value);
        return 0;
    }
    
    int newListClearCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            output << "Usage: list_clear <name>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_clear", output, "list_clear \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            output << "Error: List '" << name << "' does not exist." << std::endl;
            return 1;
        }
        gameState->clearList(name);
        return 0;
    }
    int newListClearAllCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        state::GameState *gameState = state::getGameState();
        gameState->clearAllLists();
        return 0;
    }
    int newListExistsCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_exists <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_exists", output, "list_exists \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (gameState->hasList(name)) {
            output << "0";
        } else {
            output << "1";
        }
        return 0;
    }
    int newListInitCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty()) {
            output << "Usage: list_init <name> <size> <value>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_init", output, "list_init \"my_list\" 10 \"value\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        std::string value = getVar(args[2]);
        std::string sizeStr = getVar(args[1]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            output << "Error: List '" << name << "' does not exist." << std::endl;
            return 1;
        }
        size_t size = std::stoul(sizeStr);
        gameState->initList(name, value, size);
        return 0;
    }

    int newListLenCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_len <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_len", output, "list_len \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        size_t len = gameState->getListLength(name);
        output << len;
        return 0;
    }

    int newListTokens(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty()) {
            throw cmd::AstFailure("Usage: list_tokens <name>");
            return 1;
        }   
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_tokens", output, "list_tokens \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        for(size_t i = 0; i < gameState->getListLength(name); ++i) {
            std::string value = gameState->getFromList(name, i);
            output << value << "\n";
        }
        return 0;
    }

    
    int newListSortCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_sort <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_sort", output, "list_sort \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]); 
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        gameState->sortList(name);  
        return 0;
    }
    int newListReverseCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_reverse <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_reverse", output, "list_reverse \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        gameState->reverseList(name);   
        return 0;
    }
    int newListShuffleCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream  &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_shuffle <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_shuffle", output, "list_shuffle \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]); 
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }   
        gameState->shuffleList(name);
        return 0;
    }

    int newListCopyCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_copy <name>");
            return 1;
        }
        if(args.size() < 2) {
            throw cmd::AstFailure("Usage: list_copy <name> <name2>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_copy", output, "list_copy \"list1\" \"list2\"")) {
            return 1;
        }
        std::string name1 = getVar(args[0]);
        std::string name2 = getVar(args[1]);

        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name1)) {
            throw cmd::AstFailure("Error: List '" + name1 + "' does not exist.");
            return 1;
        }
        gameState->copyList(name1, name2);
        return 0;
    }

    int newListPopCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_pop <name>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_pop", output, "list_pop \"my_list\"")) {
            return 1;
        }
        std::string name = getVar(args[0]);
        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name)) {
            throw cmd::AstFailure("Error: List '" + name + "' does not exist.");
            return 1;
        }
        gameState->popList(name);
        return 0;
    }

    int concatListCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw cmd::AstFailure("Usage: list_concat <name1> <name2>");
            return 1;
        }
        if(args.size() < 2) {
            throw cmd::AstFailure("Usage: list_concat <name1> <name2> <name3>");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "list_concat", output, "list_concat \"list1\" \"list2\"")) {
            return 1;
        }
        std::string name1 = getVar(args[0]);
        std::string name2 = getVar(args[1]);
        

        state::GameState *gameState = state::getGameState();
        if (!gameState->hasList(name1)) {
            throw cmd::AstFailure("Error: List '" + name1 + "' does not exist.");
            return 1;
        }
        if(!gameState->hasList(name2)) {
            throw cmd::AstFailure("Error: List '" + name2 + "' does not exist.");
            return 1;
        }
        gameState->concatList(name1, name2);
        return 0;
    }

    int newRandCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if(args.empty()) {
            throw cmd::AstFailure("Usage: rand <min> <max>");
            return 1;
        }
        std::string minStr = getVar(args[0]);
        std::string maxStr = getVar(args[1]);
        int min = std::stoi(minStr);
        int max = std::stoi(maxStr);
        if(min > max) {
            throw cmd::AstFailure("Usage: rand <min> <max>");
            return 1;
        }
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(min, max);
        int randomNum = dis(gen);
        output << randomNum;    
        return 0;
    }

    namespace expr_parser {
        
        enum class ExprTokenType {
            END, NUMBER, PLUS, MINUS, MUL, DIV, MOD, LPAREN, RPAREN,
            PLUSEQ, MINUSEQ, XOR, OR, AND,
            LOGICAL_AND, LOGICAL_OR, NOT,
            EQ, NEQ, GT, GTE, LT, LTE  
        };

        struct ExprToken {
            ExprTokenType type;
            int value; 
        };

        class ExprLexer {
        public:
            ExprLexer(const std::string& input) : scanner(input), pos(0) {
                scanner.scan();
                next();
            }

            void next() {
                if (pos >= scanner.size()) {
                    current = {ExprTokenType::END, 0};
                    return;
                }
                auto t = scanner[pos++];
                auto val = t.getTokenValue();
                if (t.getTokenType() == types::TokenType::TT_NUM) {
                    current = {ExprTokenType::NUMBER, std::stoi(val)};
                } else if (val == "+") {
                    current = {ExprTokenType::PLUS, 0};
                } else if (val == "-") {
                    current = {ExprTokenType::MINUS, 0};
                } else if (val == "*") {
                    current = {ExprTokenType::MUL, 0};
                } else if (val == "/") {
                    current = {ExprTokenType::DIV, 0};
                } else if (val == "%") {
                    current = {ExprTokenType::MOD, 0};
                } else if (val == "(") {
                    current = {ExprTokenType::LPAREN, 0};
                } else if (val == ")") {
                    current = {ExprTokenType::RPAREN, 0};
                } else if (val == "+=") {
                    current = {ExprTokenType::PLUSEQ, 0};
                } else if (val == "-=") {
                    current = {ExprTokenType::MINUSEQ, 0};
                } else if (val == "^") {
                    current = {ExprTokenType::XOR, 0};
                } else if (val == "|") {
                    current = {ExprTokenType::OR, 0};
                } else if (val == "&") {
                    current = {ExprTokenType::AND, 0};
                } else if (val == "&&") {
                    current = {ExprTokenType::LOGICAL_AND, 0};
                } else if (val == "||") {
                    current = {ExprTokenType::LOGICAL_OR, 0};
                } else if (val == "!") {
                    current = {ExprTokenType::NOT, 0};
                } 
                else if (val == "=") {
                    current = {ExprTokenType::EQ, 0};
                } else if (val == "!=") {
                    current = {ExprTokenType::NEQ, 0};
                } else if (val == ">") {
                    current = {ExprTokenType::GT, 0};
                } else if (val == ">=") {
                    current = {ExprTokenType::GTE, 0};
                } else if (val == "<") {
                    current = {ExprTokenType::LT, 0};
                } else if (val == "<=") {
                    current = {ExprTokenType::LTE, 0};
                }
                else {
                    current = {ExprTokenType::END, 0};
                }
            }

            ExprToken peek() const { return current; }
            void consume() { next(); }

        private:
            scan::Scanner scanner;
            size_t pos;
            ExprToken current;
        };

        class ExprParser {
        public:
            ExprParser(ExprLexer& lexer) : lexer(lexer) {}
            int parse() { return parseLogical(); }

        private:
            ExprLexer& lexer;

            int parseAdd() {
                int val = parseTerm();
                while (lexer.peek().type == ExprTokenType::PLUS ||
                    lexer.peek().type == ExprTokenType::MINUS) {
                    if (lexer.peek().type == ExprTokenType::PLUS) {
                        lexer.consume();
                        val += parseTerm();
                    } else {
                        lexer.consume();
                        val -= parseTerm();
                    }
                }
                return val;
            }

            int parseBitwise() {
                int val = parseAdd();  
                while (lexer.peek().type == ExprTokenType::XOR ||
                    lexer.peek().type == ExprTokenType::OR  ||
                    lexer.peek().type == ExprTokenType::AND) {
                    if (lexer.peek().type == ExprTokenType::XOR) {
                        lexer.consume();
                        val ^= parseAdd();
                    } else if (lexer.peek().type == ExprTokenType::OR) {
                        lexer.consume();
                        val |= parseAdd();
                    } else {  
                        lexer.consume();
                        val &= parseAdd();
                    }
                }
                return val;
            }

            int parseComparison() {
                int val = parseBitwise();
                while (lexer.peek().type == ExprTokenType::EQ ||
                       lexer.peek().type == ExprTokenType::NEQ ||
                       lexer.peek().type == ExprTokenType::GT ||
                       lexer.peek().type == ExprTokenType::GTE ||
                       lexer.peek().type == ExprTokenType::LT ||
                       lexer.peek().type == ExprTokenType::LTE) {
                    if (lexer.peek().type == ExprTokenType::EQ) {
                        lexer.consume();
                        val = (val == parseBitwise());
                    } else if (lexer.peek().type == ExprTokenType::NEQ) {
                        lexer.consume();
                        val = (val != parseBitwise());
                    } else if (lexer.peek().type == ExprTokenType::GT) {
                        lexer.consume();
                        val = (val > parseBitwise());
                    } else if (lexer.peek().type == ExprTokenType::GTE) {
                        lexer.consume();
                        val = (val >= parseBitwise());
                    } else if (lexer.peek().type == ExprTokenType::LT) {
                        lexer.consume();
                        val = (val < parseBitwise());
                    } else {  
                        lexer.consume();
                        val = (val <= parseBitwise());
                    }
                }
                return val;
            }

            int parseLogical() {
                int val = parseComparison();
                while (lexer.peek().type == ExprTokenType::LOGICAL_OR ||
                       lexer.peek().type == ExprTokenType::LOGICAL_AND) {
                    if (lexer.peek().type == ExprTokenType::LOGICAL_OR) {
                        lexer.consume();
                        val = val || parseComparison();
                    } else {
                        lexer.consume();
                        val = val && parseComparison();
                    }
                }
                return val;
            }

            int parseTerm() {
                int val = parseFactor();
                while (lexer.peek().type == ExprTokenType::MUL ||
                    lexer.peek().type == ExprTokenType::DIV ||
                    lexer.peek().type == ExprTokenType::MOD) {
                    if (lexer.peek().type == ExprTokenType::MUL) {
                        lexer.consume();
                        val *= parseFactor();
                    } else if (lexer.peek().type == ExprTokenType::DIV) {
                        lexer.consume();
                        int d = parseFactor();
                        if (d == 0) throw std::runtime_error("Division by zero");
                        val /= d;
                    } else {
                        lexer.consume();
                        int d = parseFactor();
                        if (d == 0) throw std::runtime_error("Modulo by zero");
                        val %= d;
                    }
                }
                return val;
            }

            int parseFactor() {
                if (lexer.peek().type == ExprTokenType::NOT) {
                    lexer.consume();
                    return !parseFactor();
                } else if (lexer.peek().type == ExprTokenType::MINUS) {
                    lexer.consume();
                    return -parseFactor();
                } else if (lexer.peek().type == ExprTokenType::NUMBER) {
                    int v = lexer.peek().value;
                    lexer.consume();
                    return v;
                } else if (lexer.peek().type == ExprTokenType::LPAREN) {
                    lexer.consume();
                    int v = parseLogical();
                    if (lexer.peek().type != ExprTokenType::RPAREN)
                        throw std::runtime_error("Expected ')'");
                    lexer.consume();
                    return v;
                }
                throw std::runtime_error("Unexpected token");
            }
        };
    }

    int exprCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            throw AstFailure("Usage: expr <expression>\n");
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "expr", output, "expr \"1 + 2 * 3\"")) {
            return 1;
        }
        std::string expression = getVar(args[0]);
        try {
            expr_parser::ExprLexer lexer(expression);
            expr_parser::ExprParser parser(lexer);
            int result = parser.parse();
            output << result;
            return 0;
        } catch (const std::exception& e) {
            throw AstFailure("expr: error: " + std::string(e.what()));
            return 1;
        }
    }

    int getLineCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty()) {
            output << "Usage: getline var\n";
            return 1;
        }
        std::string line;
        std::getline(input, line);
        if(line.empty()) {
            return 0;
        }

        state::GameState *gameState = state::getGameState();
        std::string var = args[0].value;
        gameState->setVariable(var, line);
        return 0;
    }

    int regexMatchCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty() || args.size() != 2) {
            output << "Usage: regex_match <pattern> <string>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "regex_match", output, "regex_match \"pattern\" \"text\"")) {
            return 1;
        }
        std::string pattern = getVar(args[0]);
        std::string str = getVar(args[1]);
        std::regex re(pattern);
        if (std::regex_match(str, re)) {
            output << "1";
        } else {
            output << "0";
        }
        return 0;
    }
    int regexReplaceCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty() || args.size() != 3) {
            output << "Usage: regex_replace <pattern> <replacement> <string>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "regex_replace", output, "regex_replace \"pattern\" \"replacement\" \"text\"")) {
            return 1;
        }
        std::string pattern = getVar(args[0]);
        std::string replacement = getVar(args[1]);
        std::string str = getVar(args[2]);
        std::regex re(pattern);
        std::string result = std::regex_replace(str, re, replacement);
        output << result;
        return 0;
    }
    int regexSearchCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty() || args.size() != 2) {
            output << "Usage: regex_search <pattern> <string>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "regex_search", output, "regex_search \"pattern\" \"text\"")) {
            return 1;
        }
        std::string pattern = getVar(args[0]);
        std::string str = getVar(args[1]);
        std::regex re(pattern);
        if (std::regex_search(str, re)) {
            output << "1";
        } else {
            output << "0";
        }
        return 0;
    }
    int regexSplitCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream &output) {
        if (args.empty() || args.size() != 2) {
            output << "Usage: regex_split <pattern> <string>\n";
            return 1;
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "regex_split", output, "regex_split \"pattern\" \"text\"")) {
            return 1;
        }
        std::string pattern = getVar(args[0]);
        std::string str = getVar(args[1]);
        std::regex re(pattern);
        std::sregex_token_iterator it(str.begin(), str.end(), re, -1);
        std::sregex_token_iterator end;
        for (; it != end; ++it) {
            output << *it << "\n";
        }
        return 0;
    }

    int tokenizeCommand(const std::vector<cmd::Argument>& args, std::istream& input, std::ostream& output) {
        if(args.empty()) {
            output << "Usage: tokenize <filename>\n";
            return 1; 
        }
        if (!CommandArgumentValidator::ensureAllQuotedStringOrVariable(args, "tokenize", output, "tokenize \"script.mx\"")) {
            return 1;
        }
        if(args.size() > 0) {
            std::string filename = getVar(args[0]);
            std::ostringstream stream;
            std::ifstream file(filename);
            if(!file.is_open()) {
                output << "tokenize: Failure to load file: " <<  filename << "\n";
                return 1;
            }
            stream << file.rdbuf();
            std::string fileContent = stream.str();
            if(!fileContent.empty()) {
                fileContent.erase(
                    std::remove(fileContent.begin(), fileContent.end(), '\r'),
                        fileContent.end()
                );
            }
            try {
                scan::Scanner scanner(fileContent);
                scanner.scan();
                output << "Idx | Type           | Value\n";
                output  << "----+----------------+--------------------------\n";
                for (size_t i = 0; i < scanner.size(); ++i) {
                    std::ostringstream data;
                    types::print_type_TokenType(data,scanner[i].getTokenType());
                    output << std::setw(3) << i << " | "
                        << std::setw(14) << data.str() << " | "
                        << scanner[i].getTokenValue() << "\n";
                }        
            } catch(scan::ScanExcept &e) {
                output << e.why() << "\n";
                return 1;
            }
            return 0;
        }
        return 1;
    }
}