From fda4936771831605db13a3f7b5d85bb49db3998c Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 4 Oct 2026 18:09:43 +0000 Subject: refactor eval --- include/error.h | 3 + src/eval.cc | 242 ----------------------------------------------- src/eval/eval.cc | 61 ++++++++++++ src/eval/eval_exe.cc | 159 +++++++++++++++++++++++++++++++ src/eval/eval_tree_cd.cc | 56 +++++++++++ 5 files changed, 279 insertions(+), 242 deletions(-) delete mode 100644 src/eval.cc create mode 100644 src/eval/eval.cc create mode 100644 src/eval/eval_exe.cc create mode 100644 src/eval/eval_tree_cd.cc diff --git a/include/error.h b/include/error.h index d8be4e5..d058ec4 100644 --- a/include/error.h +++ b/include/error.h @@ -17,6 +17,9 @@ enum class error_code AST_EXIST_LEFT_CHILLD, AST_EXIST_RIGHT_CHILD, + // eval + EVAL_FAIL_TO_CREATE_PIPE, + }; class T_error : public std::runtime_error { diff --git a/src/eval.cc b/src/eval.cc deleted file mode 100644 index 9db9b9d..0000000 --- a/src/eval.cc +++ /dev/null @@ -1,242 +0,0 @@ -#include "eval.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "magic_enum/magic_enum.hpp" - -#include "error.h" -#include "lang_analysis/lexer_class.h" -#include "lang_analysis/ast.h" -#include "lang_analysis/token.h" -#include "lang_analysis/enum_type.h" -#include "lang_analysis/symbol_property.h" -#include "lang_analysis/parseline.h" -#include "handler.h" - -using namespace std; - -extern char **environ; - -namespace ov4 -{ - -int eval_exe(const string &s, bool is_async) -{ - return eval_exe(s, is_async, nullptr, string::npos); -} - -int eval_exe(const string &s, bool is_async, const T_lexer *lexer_instance, size_t i) -{ - loggerln("eval_exe: receive {}", s); - - bool task_type_is_subshell = false; - const char *raw_input = nullptr; - char **argv = nullptr; - const char *unified_cmd_c = nullptr; - string unified_cmd, s_wrap_eliminated; - - // subshell type - if (lexer_instance != nullptr) - { - task_type_is_subshell = true; - unified_cmd = "SUBSHELL"; - raw_input = unified_cmd.c_str(); - } - // command type - else - { - task_type_is_subshell = false; - s_wrap_eliminated = s.substr(0, s.find('\n')); // eliminate \n - raw_input = s_wrap_eliminated.c_str(); - - // NB: remember to delete - argv = parseline(s); - - if (argv == nullptr || argv[0] == nullptr) { operator delete(argv); return 0; } - - if (exe_bultin_command(argv)) { operator delete(argv); return 0; } - - unified_cmd = find_cmd(argv[0]); - unified_cmd_c = unified_cmd.c_str(); - } - - // block_io signals can stop shell - // when child is at foreground, parent shell is at background - sigprocmask(SIG_BLOCK, &block_io, nullptr); - - sigset_t prev; - block_all(&prev); - - shell_pgid = getpgid(0); - pid_t pid = fork(); - - if (pid == 0) // child - { - setpgid(0, 0); - if (!is_async) // foreground - tcsetpgrp(tty_fd, getpgrp()); - - sigprocmask(SIG_SETMASK, &prev, nullptr); - - // program inside execve may use SIGTTIN/SIGTTOU - // so just restore in child - sigprocmask(SIG_UNBLOCK, &block_io, nullptr); - - // subshell - if (task_type_is_subshell) - { - eval_tree_cd(i, *lexer_instance, true); - exit(0); - } - - // normal command - execve(unified_cmd_c, argv, environ); - - // execve: if success, never returns - - cout << raw_input << flush; - switch (errno) - { - case ENOTDIR: - println(": A component of the path prefix is not a directory."); - _exit(errno); - break; - - case ENAMETOOLONG: - println(": A component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters."); - _exit(errno); - break; - - case ENOENT: - println(": The new process file does not exist."); - _exit(errno); - break; - - case EACCES: - println(": The new process file mode denies execute permission."); - _exit(errno); - break; - - default: - println(": Error, code: {}", errno); - _exit(errno); - } - } - - // parent - - operator delete(argv); - argv = nullptr; - - exit_required_pid = pid; - - if (!is_async) // foreground - { - addjob(pid, FG, raw_input); - sigset_t prev_with_sigchld_blocked = prev; - sigaddset(&prev_with_sigchld_blocked, SIGCHLD); - sigprocmask(SIG_SETMASK, &prev_with_sigchld_blocked, NULL); - waitfg(pid); - sigprocmask(SIG_SETMASK, &prev, NULL); - - return exit_code; - } - else // background - { - addjob(pid, BG, raw_input); - sigprocmask(SIG_SETMASK, &prev, NULL); - cout << "[" << pid2jid(pid) << "] (" << pid << ") " << raw_input; - - // POSIX: - // async command returns 0 immediately - // TODO: recurring async - return 0; - } - - - - return 0; -} - -int eval_tree_cd(size_t i, const T_lexer &lexer_instance, bool inside_subshell) -{ - // TODO: async - - int ret = -1; - - println("eval_tree_cd: processing {} subshell {}", i, (lexer_instance.ast[i].subshell ? "TRUE" : "FALSE")); - - // if it's a subshell, and current AST's subshell status is not subshell - // (subshell should be forkerd already, then set inside_subshell = true) - // fork itself - if (lexer_instance.ast[i].subshell && !inside_subshell) - { - eval_exe("", false, &lexer_instance, i); - return 0; - } - - if (lexer_instance.ast[i].token_type == TEXT) - { - ret = eval_exe(lexer_instance.ast[i].command_text, false); - return ret; - } - - if (lexer_instance.ast[i].left != string::npos) - { - ret = eval_tree_cd(lexer_instance.ast[i].left, lexer_instance, false); - } - - if (lexer_instance.ast[i].right != string::npos) - { - if (lexer_instance.ast[i].token_type == LOGIC_AND && ret != 0) return ret; - if (lexer_instance.ast[i].token_type == LOGIC_OR && ret == 0) return ret; - - ret = eval_tree_cd(lexer_instance.ast[i].right, lexer_instance, false); - } - return ret; -} - -/* - * eval - Evaluate the command line that the user has just typed in - * - * If the user has requested a built-in command (quit, jobs, bg or fg) - * then execute it immediately. Otherwise, fork a child process and - * run the job in the context of the child. If the job is running in - * the foreground, wait for it to terminate and then return. Note: - * each child process must have a unique process group ID so that our - * background children don't receive SIGINT (SIGTSTP) from the kernel - * when we type ctrl-c (ctrl-z) at the keyboard. -*/ -void eval(char *raw_input) -{ - string s = raw_input; - T_lexer lexer_instance; - - try - { - lexer_instance.tokenizer(s); - lexer_instance.parse(0, lexer_instance.token.size(), lexer_instance.alloc_ast()); - } - catch(const T_error &e) - { - cerr << "Error: " << magic_enum::enum_name(e.code) << endl; - cerr << "what: " << e.what() << endl; - return; - } - - eval_tree_cd(0, lexer_instance, false); - return; -} - -} diff --git a/src/eval/eval.cc b/src/eval/eval.cc new file mode 100644 index 0000000..83eccd7 --- /dev/null +++ b/src/eval/eval.cc @@ -0,0 +1,61 @@ +#include "eval.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "magic_enum/magic_enum.hpp" + +#include "error.h" +#include "lang_analysis/lexer_class.h" +#include "lang_analysis/ast.h" +#include "lang_analysis/token.h" +#include "handler.h" + +using namespace std; + +namespace ov4 +{ + +/* + * eval - Evaluate the command line that the user has just typed in + * + * If the user has requested a built-in command (quit, jobs, bg or fg) + * then execute it immediately. Otherwise, fork a child process and + * run the job in the context of the child. If the job is running in + * the foreground, wait for it to terminate and then return. Note: + * each child process must have a unique process group ID so that our + * background children don't receive SIGINT (SIGTSTP) from the kernel + * when we type ctrl-c (ctrl-z) at the keyboard. +*/ +void eval(char *raw_input) +{ + string s = raw_input; + T_lexer lexer_instance; + + try + { + lexer_instance.tokenizer(s); + lexer_instance.parse(0, lexer_instance.token.size(), lexer_instance.alloc_ast()); + } + catch(const T_error &e) + { + cerr << "Error: " << magic_enum::enum_name(e.code) << endl; + cerr << "what: " << e.what() << endl; + return; + } + + eval_tree_cd(0, lexer_instance, false); + return; +} + +} diff --git a/src/eval/eval_exe.cc b/src/eval/eval_exe.cc new file mode 100644 index 0000000..534b878 --- /dev/null +++ b/src/eval/eval_exe.cc @@ -0,0 +1,159 @@ +#include "eval.h" + +#include + +#include "error.h" +#include "lang_analysis/lexer_class.h" +#include "lang_analysis/ast.h" +#include "lang_analysis/token.h" +#include "lang_analysis/enum_type.h" +#include "lang_analysis/symbol_property.h" +#include "lang_analysis/parseline.h" +#include "util/io.h" + +using namespace std; + +extern char **environ; + +namespace ov4 +{ + +int eval_exe(const string &s, bool is_async) +{ + return eval_exe(s, is_async, nullptr, string::npos); +} + +int eval_exe(const string &s, bool is_async, const T_lexer *lexer_instance, size_t i) +{ + loggerln("eval_exe: receive {}", s); + + bool task_type_is_subshell = false; + const char *raw_input = nullptr; + char **argv = nullptr; + const char *unified_cmd_c = nullptr; + string unified_cmd, s_wrap_eliminated; + + // subshell type + if (lexer_instance != nullptr) + { + task_type_is_subshell = true; + unified_cmd = "SUBSHELL"; + raw_input = unified_cmd.c_str(); + } + // command type + else + { + task_type_is_subshell = false; + s_wrap_eliminated = s.substr(0, s.find('\n')); // eliminate \n + raw_input = s_wrap_eliminated.c_str(); + + // NB: remember to delete + argv = parseline(s); + + if (argv == nullptr || argv[0] == nullptr) { operator delete(argv); return 0; } + + if (exe_bultin_command(argv)) { operator delete(argv); return 0; } + + unified_cmd = find_cmd(argv[0]); + unified_cmd_c = unified_cmd.c_str(); + } + + // block_io signals can stop shell + // when child is at foreground, parent shell is at background + sigprocmask(SIG_BLOCK, &block_io, nullptr); + + sigset_t prev; + block_all(&prev); + + shell_pgid = getpgid(0); + pid_t pid = fork(); + + if (pid == 0) // child + { + setpgid(0, 0); + if (!is_async) // foreground + tcsetpgrp(tty_fd, getpgrp()); + + sigprocmask(SIG_SETMASK, &prev, nullptr); + + // program inside execve may use SIGTTIN/SIGTTOU + // so just restore in child + sigprocmask(SIG_UNBLOCK, &block_io, nullptr); + + // subshell + if (task_type_is_subshell) + { + eval_tree_cd(i, *lexer_instance, true); + exit(0); + } + + // normal command + execve(unified_cmd_c, argv, environ); + + // execve: if success, never returns + + cout << raw_input << flush; + switch (errno) + { + case ENOTDIR: + println(": A component of the path prefix is not a directory."); + _exit(errno); + break; + + case ENAMETOOLONG: + println(": A component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters."); + _exit(errno); + break; + + case ENOENT: + println(": The new process file does not exist."); + _exit(errno); + break; + + case EACCES: + println(": The new process file mode denies execute permission."); + _exit(errno); + break; + + default: + println(": Error, code: {}", errno); + _exit(errno); + } + } + + // parent + + operator delete(argv); + argv = nullptr; + + exit_required_pid = pid; + + if (!is_async) // foreground + { + addjob(pid, FG, raw_input); + sigset_t prev_with_sigchld_blocked = prev; + sigaddset(&prev_with_sigchld_blocked, SIGCHLD); + sigprocmask(SIG_SETMASK, &prev_with_sigchld_blocked, NULL); + waitfg(pid); + sigprocmask(SIG_SETMASK, &prev, NULL); + + return exit_code; + } + else // background + { + addjob(pid, BG, raw_input); + sigprocmask(SIG_SETMASK, &prev, NULL); + cout << "[" << pid2jid(pid) << "] (" << pid << ") " << raw_input; + + // POSIX: + // async command returns 0 immediately + // TODO: recurring async + return 0; + } + + + + return 0; +} + +} \ No newline at end of file diff --git a/src/eval/eval_tree_cd.cc b/src/eval/eval_tree_cd.cc new file mode 100644 index 0000000..7d7c1af --- /dev/null +++ b/src/eval/eval_tree_cd.cc @@ -0,0 +1,56 @@ +#include "eval.h" + +#include + +#include "error.h" +#include "lang_analysis/lexer_class.h" +#include "lang_analysis/ast.h" +#include "lang_analysis/token.h" +#include "lang_analysis/enum_type.h" +#include "lang_analysis/symbol_property.h" +#include "util/io.h" + +using namespace std; + +namespace ov4 +{ + +int eval_tree_cd(size_t i, const T_lexer &lexer_instance, bool inside_subshell) +{ + // TODO: async + + int ret = -1; + + println("eval_tree_cd: processing {} subshell {}", i, (lexer_instance.ast[i].subshell ? "TRUE" : "FALSE")); + + // if it's a subshell, and current AST's subshell status is not subshell + // (subshell should be forkerd already, then set inside_subshell = true) + // fork itself + if (lexer_instance.ast[i].subshell && !inside_subshell) + { + eval_exe("", false, &lexer_instance, i); + return 0; + } + + if (lexer_instance.ast[i].token_type == TEXT) + { + ret = eval_exe(lexer_instance.ast[i].command_text, false); + return ret; + } + + if (lexer_instance.ast[i].left != string::npos) + { + ret = eval_tree_cd(lexer_instance.ast[i].left, lexer_instance, false); + } + + if (lexer_instance.ast[i].right != string::npos) + { + if (lexer_instance.ast[i].token_type == LOGIC_AND && ret != 0) return ret; + if (lexer_instance.ast[i].token_type == LOGIC_OR && ret == 0) return ret; + + ret = eval_tree_cd(lexer_instance.ast[i].right, lexer_instance, false); + } + return ret; +} + +} \ No newline at end of file -- cgit v1.3.1-4-g156e