Page MenuHomePhorge

repl.c
No OneTemporary

Size
23 KB
Referenced Files
None
Subscribers
None
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <conio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#include <direct.h>
#define STDIN_FILENO 0
#define mkdir_p(path) _mkdir(path)
#else
#include <termios.h>
#include <unistd.h>
#define mkdir_p(path) mkdir(path, 0755)
#endif
#include "ant.h"
#include "repl.h"
#include "reactor.h"
#include "runtime.h"
#include "internal.h"
#include "silver/ast.h"
#include "silver/engine.h"
#include "modules/io.h"
#define MAX_HISTORY 512
#define MAX_LINE_LENGTH 4096
#define MAX_MULTILINE_LENGTH 65536
#define INPUT char *line, int *pos, int *len, key_event_t *key, history_t *hist, const char *prompt
static volatile sig_atomic_t ctrl_c_pressed = 0;
typedef struct {
char **lines;
int count;
int capacity;
int current;
} history_t;
typedef enum {
CMD_OK,
CMD_EXIT,
CMD_NOT_FOUND
} cmd_result_t;
typedef struct {
const char *name;
const char *description;
bool has_arg;
cmd_result_t (*handler)(ant_t *js, history_t *history, const char *arg);
} repl_command_t;
typedef struct {
char *name;
size_t len;
} repl_decl_name_t;
typedef struct {
repl_decl_name_t *items;
size_t count;
size_t cap;
} repl_decl_registry_t;
typedef struct {
const char **names;
uint32_t *lens;
size_t count;
size_t cap;
} repl_decl_pending_t;
static repl_decl_registry_t *g_repl_decl_registry = NULL;
static void sigint_handler(int sig) { ctrl_c_pressed++; }
static inline void repl_clear_exception_state(ant_t *js) {
js->thrown_exists = false;
js->thrown_value = js_mkundef();
}
static void repl_decl_registry_free(repl_decl_registry_t *reg) {
if (!reg) return;
for (size_t i = 0; i < reg->count; i++)
free(reg->items[i].name);
free(reg->items);
reg->items = NULL;
reg->count = 0;
reg->cap = 0;
}
static bool repl_decl_registry_contains(
const repl_decl_registry_t *reg,
const char *name, uint32_t len
) {
if (!reg || !name) return false;
for (size_t i = 0; i < reg->count; i++) {
if (
reg->items[i].len == (size_t)len
&& memcmp(reg->items[i].name, name, (size_t)len) == 0
) return true;
}
return false;
}
static bool repl_decl_registry_add(
ant_t *js, repl_decl_registry_t *reg,
const char *name, uint32_t len
) {
if (!reg || !name) return true;
if (repl_decl_registry_contains(reg, name, len)) return true;
if (reg->count >= reg->cap) {
size_t new_cap = reg->cap ? reg->cap * 2 : 32;
repl_decl_name_t *ni = realloc(reg->items, new_cap * sizeof(*ni));
if (!ni) {
js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory");
return false;
}
reg->items = ni;
reg->cap = new_cap;
}
char *copy = malloc((size_t)len + 1);
if (!copy) {
js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory");
return false;
}
memcpy(copy, name, (size_t)len);
copy[len] = '\0';
reg->items[reg->count++] = (repl_decl_name_t){ .name = copy, .len = (size_t)len };
return true;
}
static void repl_decl_pending_free(repl_decl_pending_t *p) {
if (!p) return;
free(p->names);
free(p->lens);
p->names = NULL;
p->lens = NULL;
p->count = 0;
p->cap = 0;
}
static bool repl_decl_pending_contains(
const repl_decl_pending_t *p,
const char *name, uint32_t len
) {
if (!p || !name) return false;
for (size_t i = 0; i < p->count; i++)
if (p->lens[i] == len && memcmp(p->names[i], name, (size_t)len) == 0) return true;
return false;
}
static bool repl_decl_pending_push(
ant_t *js, repl_decl_pending_t *p,
const char *name, uint32_t len
) {
if (!p || !name || len == 0) return true;
if (repl_decl_pending_contains(p, name, len)) return true;
if (p->count >= p->cap) {
size_t new_cap = p->cap ? p->cap * 2 : 16;
const char **nn = realloc(p->names, new_cap * sizeof(*nn));
if (!nn) {
js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory");
return false;
}
uint32_t *nl = realloc(p->lens, new_cap * sizeof(*nl));
if (!nl) {
p->names = nn;
js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory");
return false;
}
p->names = nn;
p->lens = nl;
p->cap = new_cap;
}
p->names[p->count] = name;
p->lens[p->count] = len;
p->count++;
return true;
}
static bool repl_collect_pattern_names(ant_t *js, sv_ast_t *pat, repl_decl_pending_t *p) {
if (!pat) return true;
switch (pat->type) {
case N_IDENT:
return repl_decl_pending_push(js, p, pat->str, pat->len);
case N_ASSIGN_PAT:
case N_ASSIGN:
return repl_collect_pattern_names(js, pat->left, p);
case N_REST:
case N_SPREAD:
return repl_collect_pattern_names(js, pat->right, p);
case N_ARRAY:
case N_ARRAY_PAT:
for (int i = 0; i < pat->args.count; i++) {
if (!repl_collect_pattern_names(js, pat->args.items[i], p)) return false;
}
return true;
case N_OBJECT:
case N_OBJECT_PAT:
for (int i = 0; i < pat->args.count; i++) {
sv_ast_t *prop = pat->args.items[i];
if (!prop) continue;
if (prop->type == N_PROPERTY) {
if (!repl_collect_pattern_names(js, prop->right, p)) return false;
} else if (prop->type == N_REST || prop->type == N_SPREAD) {
if (!repl_collect_pattern_names(js, prop->right, p)) return false;
}
}
return true;
default: return true;
}
}
static bool repl_collect_top_level_decls(ant_t *js, sv_ast_t *stmt, repl_decl_pending_t *p) {
if (!stmt) return true;
sv_ast_t *node = (stmt->type == N_EXPORT) ? stmt->left : stmt;
if (!node) return true;
if (node->type == N_VAR && node->var_kind != VAR_VAR) {
for (int i = 0; i < node->args.count; i++) {
sv_ast_t *decl = node->args.items[i];
if (!decl || decl->type != N_VARDECL || !decl->left) continue;
if (!repl_collect_pattern_names(js, decl->left, p)) return false;
}
return true;
}
if (node->type == N_CLASS && node->str && node->len > 0)
return repl_decl_pending_push(js, p, node->str, node->len);
if (node->type == N_IMPORT_DECL) {
for (int i = 0; i < node->args.count; i++) {
sv_ast_t *spec = node->args.items[i];
if (!spec || spec->type != N_IMPORT_SPEC || !spec->right) continue;
if (spec->right->type != N_IDENT) continue;
if (!repl_decl_pending_push(js, p, spec->right->str, spec->right->len)) return false;
}
}
return true;
}
static bool repl_precheck_and_commit_lexicals(
ant_t *js, repl_decl_registry_t *reg,
const char *code, size_t len
) {
if (!js || !reg || !code || len == 0) return true;
code_arena_mark_t mark = code_arena_mark();
repl_decl_pending_t pending = {0};
bool ok = true;
repl_clear_exception_state(js);
sv_ast_t *program = sv_parse(js, code, (jsoff_t)len, false);
if (!program || js->thrown_exists) {
ok = true;
goto done;
}
for (int i = 0; i < program->args.count; i++) {
if (
!repl_collect_top_level_decls(
js, program->args.items[i], &pending)
) { ok = false; goto done; }
}
for (size_t i = 0; i < pending.count; i++) {
if (repl_decl_registry_contains(reg, pending.names[i], pending.lens[i])) {
js_mkerr_typed(
js, JS_ERR_SYNTAX, "Identifier '%.*s' has already been declared",
(int)pending.lens[i], pending.names[i]
);
ok = false; goto done;
}
}
for (size_t i = 0; i < pending.count; i++) {
if (
!repl_decl_registry_add(
js, reg, pending.names[i], pending.lens[i])
) { ok = false; goto done; }
}
done:
code_arena_rewind(mark);
repl_decl_pending_free(&pending);
if (ok && js->thrown_exists)
repl_clear_exception_state(js);
return ok;
}
typedef enum {
REPL_PRINT_INTERACTIVE,
REPL_PRINT_LOAD,
} repl_print_mode_t;
static void repl_eval_chunk(
ant_t *js, repl_decl_registry_t *decl_registry,
const char *code, size_t len,
repl_print_mode_t print_mode
) {
if (!repl_precheck_and_commit_lexicals(js, decl_registry, code, len)) {
print_uncaught_throw(js);
return;
}
repl_clear_exception_state(js);
jsval_t result = js_eval_bytecode_repl(js, code, len);
js_run_event_loop(js);
if (print_uncaught_throw(js)) return;
if (print_mode == REPL_PRINT_INTERACTIVE) {
print_repl_value(js, result, stdout);
return;
}
if (vtype(result) == T_ERR) fprintf(stderr, "%s\n", js_str(js, result));
else if (vtype(result) != T_UNDEF) printf("%s\n", js_str(js, result));
}
static cmd_result_t cmd_help(ant_t *js, history_t *history, const char *arg);
static cmd_result_t cmd_exit(ant_t *js, history_t *history, const char *arg);
static cmd_result_t cmd_load(ant_t *js, history_t *history, const char *arg);
static cmd_result_t cmd_save(ant_t *js, history_t *history, const char *arg);
static cmd_result_t cmd_stats(ant_t *js, history_t *history, const char *arg);
static const repl_command_t commands[] = {
{ "help", "Show this help message", false, cmd_help },
{ "exit", "Exit the REPL", false, cmd_exit },
{ "load", "Load JS from a file into the REPL session", true, cmd_load },
{ "save", "Save all evaluated commands in this REPL session to a file", true, cmd_save },
{ "stats", "Show memory statistics", false, cmd_stats },
{ NULL, NULL, false, NULL }
};
static cmd_result_t cmd_help(ant_t *js, history_t *history, const char *arg) {
for (const repl_command_t *cmd = commands; cmd->name; cmd++) {
printf(" .%-7s - %s\n", cmd->name, cmd->description);
}
printf("\nPress Ctrl+C to abort current expression.\n");
return CMD_OK;
}
static cmd_result_t cmd_exit(ant_t *js, history_t *history, const char *arg) {
return CMD_EXIT;
}
static cmd_result_t cmd_load(ant_t *js, history_t *history, const char *arg) {
(void)history;
if (!arg || *arg == '\0') {
fprintf(stderr, "Usage: .load <filename>\n");
return CMD_OK;
}
FILE *fp = fopen(arg, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open file: %s\n", arg);
return CMD_OK;
}
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *file_buffer = malloc(file_size + 1);
if (file_buffer) {
size_t len = fread(file_buffer, 1, file_size, fp);
file_buffer[len] = '\0';
repl_eval_chunk(
js, g_repl_decl_registry,
file_buffer, len, REPL_PRINT_LOAD
);
free(file_buffer);
}
fclose(fp);
return CMD_OK;
}
static cmd_result_t cmd_save(ant_t *js, history_t *history, const char *arg) {
(void)js;
if (!arg || *arg == '\0') {
fprintf(stderr, "Usage: .save <filename>\n");
return CMD_OK;
}
FILE *fp = fopen(arg, "w");
if (fp == NULL) {
fprintf(stderr, "Failed to open file for writing: %s\n", arg);
return CMD_OK;
}
for (int i = 0; i < history->count; i++) {
fprintf(fp, "%s\n", history->lines[i]);
}
fclose(fp);
printf("Session saved to %s\n", arg);
return CMD_OK;
}
static cmd_result_t cmd_stats(ant_t *js, history_t *history, const char *arg) {
jsval_t stats_fn = js_get(js, rt->ant_obj, "stats");
jsval_t result = sv_vm_call(js->vm, js, stats_fn, js_mkundef(), NULL, 0, NULL, false);
console_print(js, &result, 1, NULL, stdout);
return CMD_OK;
}
static cmd_result_t execute_command(ant_t *js, history_t *history, const char *line) {
const char *cmd_start = line + 1;
for (const repl_command_t *cmd = commands; cmd->name; cmd++) {
size_t n = strlen(cmd->name);
if (strncmp(cmd_start, cmd->name, n) != 0) continue;
char next = cmd_start[n];
if (cmd->has_arg && (next == ' ' || next == '\0')) {
const char *arg = cmd_start + n;
while (*arg == ' ') arg++;
return cmd->handler(js, history, arg);
}
if (!cmd->has_arg && next == '\0') return cmd->handler(js, history, NULL);
}
return CMD_NOT_FOUND;
}
static void history_init(history_t *hist) {
hist->capacity = MAX_HISTORY;
hist->lines = malloc(sizeof(char*) * hist->capacity);
hist->count = 0;
hist->current = -1;
}
static void history_add(history_t *hist, const char *line) {
if (strlen(line) == 0) return;
if (hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) return;
if (hist->count >= hist->capacity) {
free(hist->lines[0]);
memmove(hist->lines, hist->lines + 1, sizeof(char*) * (hist->capacity - 1));
hist->count--;
}
hist->lines[hist->count++] = strdup(line);
hist->current = hist->count;
}
static const char* history_prev(history_t *hist) {
if (hist->count == 0) return NULL;
if (hist->current > 0) hist->current--;
return hist->lines[hist->current];
}
static const char* history_next(history_t *hist) {
if (hist->count == 0) return NULL;
if (hist->current < hist->count - 1) {
hist->current++;
return hist->lines[hist->current];
}
hist->current = hist->count;
return "";
}
static void history_free(history_t *hist) {
for (int i = 0; i < hist->count; i++) free(hist->lines[i]);
free(hist->lines);
}
static char* get_history_path(void) {
const char *home = getenv("HOME");
if (!home) home = getenv("USERPROFILE");
if (!home) return NULL;
size_t len = strlen(home) + 32;
char *path = malloc(len);
snprintf(path, len, "%s/.ant", home);
mkdir_p(path);
snprintf(path, len, "%s/.ant/repl_history", home);
return path;
}
static void history_load(history_t *hist) {
char *path = get_history_path();
if (!path) return;
FILE *fp = fopen(path, "r");
free(path);
if (!fp) return;
char line[MAX_LINE_LENGTH];
while (fgets(line, sizeof(line), fp)) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0';
if (line[0]) history_add(hist, line);
}
fclose(fp);
}
static void history_save(history_t *hist) {
char *path = get_history_path();
if (!path) return;
FILE *fp = fopen(path, "w");
free(path);
if (!fp) return;
for (int i = 0; i < hist->count; i++) {
fprintf(fp, "%s\n", hist->lines[i]);
}
fclose(fp);
}
typedef enum {
KEY_NONE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,
KEY_BACKSPACE, KEY_ENTER, KEY_EOF, KEY_CHAR
} key_type_t;
typedef struct { key_type_t type; int ch; } key_event_t;
typedef void (*key_handler_t)(INPUT);
static void cursor_move(int *pos, int len, int dir) {
if (dir < 0 && *pos > 0) { printf("\033[D"); fflush(stdout); (*pos)--; }
else if (dir > 0 && *pos < len) { printf("\033[C"); fflush(stdout); (*pos)++; }
}
static void line_set(char *line, int *pos, int *len, const char *str, const char *prompt) {
printf("\r\033[K%s%s", prompt, str);
fflush(stdout); strcpy(line, str);
*len = (int)strlen(line); *pos = *len;
}
static void line_backspace(char *line, int *pos, int *len) {
if (*pos <= 0) return;
memmove(line + *pos - 1, line + *pos, *len - *pos + 1);
(*pos)--; (*len)--;
printf("\b\033[K%s", line + *pos);
for (int i = 0; i < *len - *pos; i++) printf("\033[D");
fflush(stdout);
}
static void line_insert(char *line, int *pos, int *len, int c) {
if (*len >= MAX_LINE_LENGTH - 1) return;
memmove(line + *pos + 1, line + *pos, *len - *pos + 1);
line[*pos] = (char)c;
(*pos)++; (*len)++;
printf("%c%s", c, line + *pos);
for (int i = 0; i < *len - *pos; i++) printf("\033[D");
fflush(stdout);
}
#ifdef _WIN32
static key_event_t read_key(void) {
if (ctrl_c_pressed > 0) return (key_event_t){ KEY_EOF, 0 };
int c = _getch();
if (c == 0 || c == 0xE0) {
int ext = _getch();
switch (ext) {
case 72: return (key_event_t){ KEY_UP, 0 };
case 80: return (key_event_t){ KEY_DOWN, 0 };
case 77: return (key_event_t){ KEY_RIGHT, 0 };
case 75: return (key_event_t){ KEY_LEFT, 0 };
}
return (key_event_t){ KEY_NONE, 0 };
}
if (c == 8) return (key_event_t){ KEY_BACKSPACE, 0 };
if (c == '\r' || c == '\n') return (key_event_t){ KEY_ENTER, 0 };
if (c == 3) { ctrl_c_pressed++; return (key_event_t){ KEY_EOF, 0 }; }
if (c == 4 || c == 26) return (key_event_t){ KEY_EOF, 0 };
if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c };
return (key_event_t){ KEY_NONE, 0 };
}
#define TERM_INIT()
#define TERM_RESTORE()
#else
static struct termios saved_tio;
static key_event_t read_key(void) {
if (ctrl_c_pressed > 0) return (key_event_t){ KEY_EOF, 0 };
int c = getchar();
if (c == EOF && !feof(stdin)) { clearerr(stdin); return (key_event_t){ KEY_EOF, 0 }; }
if (c == EOF) return (key_event_t){ KEY_EOF, 0 };
if (c == 27) {
if (getchar() == '[') {
switch (getchar()) {
case 'A': return (key_event_t){ KEY_UP, 0 };
case 'B': return (key_event_t){ KEY_DOWN, 0 };
case 'C': return (key_event_t){ KEY_RIGHT, 0 };
case 'D': return (key_event_t){ KEY_LEFT, 0 };
}
}
return (key_event_t){ KEY_NONE, 0 };
}
if (c == 127 || c == 8) return (key_event_t){ KEY_BACKSPACE, 0 };
if (c == '\n' || c == '\r') return (key_event_t){ KEY_ENTER, 0 };
if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c };
return (key_event_t){ KEY_NONE, 0 };
}
#define TERM_INIT() do { \
struct termios new_tio; \
tcgetattr(STDIN_FILENO, &saved_tio); \
new_tio = saved_tio; \
new_tio.c_lflag &= ~(ICANON | ECHO); \
tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); \
} while(0)
#define TERM_RESTORE() tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio)
#endif
static void handle_up(INPUT) {
const char *h = history_prev(hist);
if (h) line_set(line, pos, len, h, prompt);
}
static void handle_down(INPUT) {
const char *h = history_next(hist);
if (h) line_set(line, pos, len, h, prompt);
}
static void handle_left(INPUT) { cursor_move(pos, *len, -1); }
static void handle_right(INPUT) { cursor_move(pos, *len, 1); }
static void handle_backspace(INPUT) { line_backspace(line, pos, len); }
static void handle_char(INPUT) { line_insert(line, pos, len, key->ch); }
static key_handler_t handlers[] = {
[KEY_UP] = handle_up, [KEY_DOWN] = handle_down,
[KEY_LEFT] = handle_left, [KEY_RIGHT] = handle_right,
[KEY_BACKSPACE] = handle_backspace, [KEY_CHAR] = handle_char,
};
static char* read_line_with_history(history_t *hist, ant_t *js, const char *prompt) {
char *line = malloc(MAX_LINE_LENGTH);
int pos = 0, len = 0; line[0] = '\0';
TERM_INIT();
do {
key_event_t key = read_key();
if (key.type == KEY_ENTER) {
printf("\n"); fflush(stdout);
TERM_RESTORE(); return line;
}
if (key.type == KEY_EOF) {
printf("\n"); fflush(stdout);
TERM_RESTORE();
free(line); return NULL;
}
if (handlers[key.type]) {
handlers[key.type](line, &pos, &len, &key, hist, prompt);
}
} while (1);
}
typedef struct {
int paren, bracket, brace;
int *templates;
int template_count, template_cap;
char string_char;
bool in_string, escaped;
} parse_state_t;
static void push_template(parse_state_t *s) {
if (s->template_count >= s->template_cap) {
s->template_cap = s->template_cap ? s->template_cap * 2 : 8;
int *new_templates = realloc(s->templates, s->template_cap * sizeof(int));
if (!new_templates) { return; } s->templates = new_templates;
}
s->templates[s->template_count++] = s->brace;
}
static bool in_template_text(parse_state_t *s) {
return s->template_count > 0 && s->brace == s->templates[s->template_count - 1];
}
static bool is_incomplete_input(const char *code, size_t len) {
parse_state_t s = {0};
for (size_t i = 0; i < len; i++) {
char c = code[i];
if (s.escaped) { s.escaped = false; continue; }
if (c == '\\' && (s.in_string || s.template_count > 0)) { s.escaped = true; continue; }
if (s.in_string) { if (c == s.string_char) s.in_string = false; continue; }
if (in_template_text(&s)) {
if (c == '`') s.template_count--;
else if (c == '$' && i + 1 < len && code[i + 1] == '{') { s.brace++; i++; }
continue;
}
if (c == '/' && i + 1 < len) {
if (code[i + 1] == '/') { while (i < len && code[i] != '\n') i++; continue; }
if (code[i + 1] == '*') {
for (i += 2; i + 1 < len && !(code[i] == '*' && code[i + 1] == '/'); i++);
if (i + 1 >= len) { free(s.templates); return true; }
i++; continue;
}
}
switch (c) {
case '"': case '\'': s.in_string = true; s.string_char = c; break;
case '`': push_template(&s); break;
case '(': s.paren++; break; case ')': s.paren--; break;
case '[': s.bracket++; break; case ']': s.bracket--; break;
case '{': s.brace++; break; case '}': s.brace--; break;
}
}
bool incomplete = s.in_string || s.template_count > 0 || s.paren > 0 || s.bracket > 0 || s.brace > 0;
free(s.templates);
return incomplete;
}
void ant_repl_run() {
ant_t *js = rt->js;
js_set_filename(js, "[repl]");
js_setup_import_meta(js, "[repl]");
printf("Welcome to Ant JavaScript v%s\n", ANT_VERSION);
printf("Type \".help\" for more information.\n");
#ifdef _WIN32
signal(SIGINT, sigint_handler);
#else
struct sigaction sa;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
#endif
history_t history;
history_init(&history);
history_load(&history);
repl_decl_registry_t decl_registry = {0};
g_repl_decl_registry = &decl_registry;
js_set(js, js_glob(js), "__dirname", js_mkstr(js, ".", 1));
js_set(js, js_glob(js), "__filename", js_mkstr(js, "[repl]", 6));
int prev_ctrl_c_count = 0;
char *multiline_buf = NULL;
size_t multiline_len = 0;
size_t multiline_cap = 0;
while (1) {
const char *prompt = multiline_buf ? "| " : "> ";
fputs(prompt, stdout);
fflush(stdout);
ctrl_c_pressed = 0;
char *line = read_line_with_history(&history, js, prompt);
if (ctrl_c_pressed > 0) {
if (multiline_buf) {
free(multiline_buf);
multiline_buf = NULL;
multiline_len = 0;
multiline_cap = 0;
prev_ctrl_c_count = 0;
if (line) free(line);
continue;
}
if (prev_ctrl_c_count > 0) {
if (line) free(line);
break;
}
printf("(To exit, press Ctrl+C again or type .exit)\n");
prev_ctrl_c_count++;
if (line) free(line);
continue;
}
if (line == NULL) {
if (multiline_buf) {
free(multiline_buf);
multiline_buf = NULL;
multiline_len = 0;
multiline_cap = 0;
continue;
}
break;
}
prev_ctrl_c_count = 0;
size_t line_len = strlen(line);
if (line_len == 0 && multiline_buf) {
if (multiline_len + 1 >= multiline_cap) {
multiline_cap = multiline_cap ? multiline_cap * 2 : 256;
multiline_buf = realloc(multiline_buf, multiline_cap);
}
multiline_buf[multiline_len++] = '\n';
multiline_buf[multiline_len] = '\0';
free(line);
continue;
}
if (line_len == 0) {
free(line);
continue;
}
if (!multiline_buf && line[0] == '.') {
cmd_result_t result = execute_command(js, &history, line);
if (result == CMD_EXIT) {
free(line);
break;
} else if (result == CMD_NOT_FOUND) {
printf("Unknown command: %s\n", line);
printf("Type \".help\" for more information.\n");
}
free(line);
continue;
}
size_t new_len = multiline_len + line_len + 1;
if (new_len >= multiline_cap || !multiline_buf) {
multiline_cap = multiline_cap ? multiline_cap * 2 : 256;
if (multiline_cap < new_len + 1) multiline_cap = new_len + 1;
multiline_buf = realloc(multiline_buf, multiline_cap);
}
if (multiline_len > 0) {
multiline_buf[multiline_len++] = '\n';
}
memcpy(multiline_buf + multiline_len, line, line_len);
multiline_len += line_len;
multiline_buf[multiline_len] = '\0';
free(line);
if (is_incomplete_input(multiline_buf, multiline_len)) continue;
history_add(&history, multiline_buf);
repl_eval_chunk(
js, &decl_registry, multiline_buf,
multiline_len, REPL_PRINT_INTERACTIVE
);
free(multiline_buf);
multiline_buf = NULL;
multiline_len = 0;
multiline_cap = 0;
}
if (multiline_buf) free(multiline_buf);
repl_decl_registry_free(&decl_registry);
g_repl_decl_registry = NULL;
history_save(&history);
history_free(&history);
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Mar 26, 4:46 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511726
Default Alt Text
repl.c (23 KB)

Event Timeline