#define _POSIX_C_SOURCE 200809L #include "repl.h" #include #include #include #include #include #include #include #include static volatile bool g_interrupted = false; static void sigint_handler(int sig) { (void)sig; g_interrupted = true; } RavaREPL_t* rava_repl_create(void) { RavaREPL_t *repl = calloc(1, sizeof(RavaREPL_t)); if (!repl) return NULL; repl->session = rava_repl_session_create(); if (!repl->session) { free(repl); return NULL; } repl->input = rava_repl_input_create(); if (!repl->input) { rava_repl_session_destroy(repl->session); free(repl); return NULL; } repl->executor = rava_repl_executor_create(repl->session); if (!repl->executor) { rava_repl_input_destroy(repl->input); rava_repl_session_destroy(repl->session); free(repl); return NULL; } repl->running = true; repl->interactive = true; return repl; } void rava_repl_destroy(RavaREPL_t *repl) { if (!repl) return; rava_repl_executor_destroy(repl->executor); rava_repl_input_destroy(repl->input); rava_repl_session_destroy(repl->session); free(repl); } void rava_repl_print_banner(void) { printf("Rava %s Interactive Interpreter\n", RAVA_REPL_VERSION); printf("Type \"%%help\" for commands, \"%%quit\" to exit\n\n"); } void rava_repl_print_prompt(RavaREPL_t *repl, bool continuation) { (void)repl; printf("%s", continuation ? "... " : ">>> "); fflush(stdout); } bool rava_repl_process_line(RavaREPL_t *repl, const char *line) { if (!repl || !line) return true; while (*line && isspace(*line)) line++; if (strlen(line) == 0) return true; if (rava_repl_command_is_command(line)) { char *arg = NULL; RavaREPLCommand_e cmd = rava_repl_command_parse(line, &arg); if (cmd == RAVA_CMD_QUIT) { free(arg); return false; } rava_repl_history_add(repl->session->history, line); bool result = rava_repl_command_execute(repl->session, cmd, arg); free(arg); return result; } RavaREPLInputState_e state = rava_repl_input_append(repl->input, line); if (state == RAVA_REPL_INPUT_COMPLETE) { const char *code = rava_repl_input_get(repl->input); rava_repl_history_add(repl->session->history, code); rava_repl_executor_execute(repl->executor, code); rava_repl_input_reset(repl->input); } else if (state == RAVA_REPL_INPUT_ERROR) { rava_repl_output_error("Input error", "Buffer overflow or invalid input"); rava_repl_input_reset(repl->input); } return true; } static char* read_line_stdin(const char *prompt) { printf("%s", prompt); fflush(stdout); char *line = NULL; size_t len = 0; ssize_t read = getline(&line, &len, stdin); if (read == -1) { free(line); return NULL; } if (read > 0 && line[read - 1] == '\n') { line[read - 1] = '\0'; } return line; } int rava_repl_run(void) { struct sigaction sa; sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); RavaREPL_t *repl = rava_repl_create(); if (!repl) { fprintf(stderr, "Failed to create REPL\n"); return 1; } bool is_tty = isatty(fileno(stdin)); rava_repl_print_banner(); char history_file[256]; const char *home = getenv("HOME"); if (home && is_tty) { snprintf(history_file, sizeof(history_file), "%s/.rava_history", home); rava_repl_history_load(repl->session->history, history_file); read_history(history_file); } if (is_tty) { rl_bind_key('\t', rl_complete); } while (repl->running) { bool continuation = !rava_repl_input_is_empty(repl->input); const char *prompt = continuation ? "... " : ">>> "; char *line; if (is_tty) { line = readline(prompt); } else { line = read_line_stdin(prompt); } if (!line) { if (is_tty) printf("\n"); break; } if (strlen(line) == 0) { free(line); continue; } if (is_tty && strlen(line) > 0) { add_history(line); } g_interrupted = false; repl->running = rava_repl_process_line(repl, line); free(line); } if (home && is_tty) { rava_repl_history_save(repl->session->history, history_file); write_history(history_file); } rava_repl_destroy(repl); return 0; }