MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at type-hints-typescript 750 lines 22 kB view raw
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <ctype.h> 5 6#include "ant.h" 7#include "repl.h" 8#include "readline.h" 9#include "reactor.h" 10#include "runtime.h" 11#include "internal.h" 12#include "descriptors.h" 13 14#include "silver/ast.h" 15#include "silver/engine.h" 16 17#include <crprintf.h> 18#include "modules/io.h" 19#include "highlight.h" 20#include "highlight/regex.h" 21 22typedef ant_history_t history_t; 23 24typedef enum { 25 CMD_OK, 26 CMD_EXIT, 27 CMD_NOT_FOUND 28} cmd_result_t; 29 30typedef struct { 31 const char *name; 32 const char *description; 33 bool has_arg; 34 cmd_result_t (*handler)(ant_t *js, history_t *history, const char *arg); 35} repl_command_t; 36 37typedef struct { 38 char *name; 39 size_t len; 40} repl_decl_name_t; 41 42typedef struct { 43 repl_decl_name_t *items; 44 size_t count; 45 size_t cap; 46} repl_decl_registry_t; 47 48typedef struct { 49 const char **names; 50 uint32_t *lens; 51 size_t count; 52 size_t cap; 53} repl_decl_pending_t; 54 55static repl_decl_registry_t *g_repl_decl_registry = NULL; 56 57static inline void repl_clear_exception_state(ant_t *js) { 58 js->thrown_exists = false; 59 js->thrown_value = js_mkundef(); 60} 61 62static void repl_decl_registry_free(repl_decl_registry_t *reg) { 63 if (!reg) return; 64 for (size_t i = 0; i < reg->count; i++) 65 free(reg->items[i].name); 66 free(reg->items); 67 reg->items = NULL; 68 reg->count = 0; 69 reg->cap = 0; 70} 71 72static bool repl_decl_registry_contains( 73 const repl_decl_registry_t *reg, 74 const char *name, uint32_t len 75) { 76 if (!reg || !name) return false; 77 for (size_t i = 0; i < reg->count; i++) { 78 if ( 79 reg->items[i].len == (size_t)len 80 && memcmp(reg->items[i].name, name, (size_t)len) == 0 81 ) return true; 82 } 83 return false; 84} 85 86static bool repl_decl_registry_add( 87 ant_t *js, repl_decl_registry_t *reg, 88 const char *name, uint32_t len 89) { 90 if (!reg || !name) return true; 91 if (repl_decl_registry_contains(reg, name, len)) return true; 92 93 if (reg->count >= reg->cap) { 94 size_t new_cap = reg->cap ? reg->cap * 2 : 32; 95 repl_decl_name_t *ni = realloc(reg->items, new_cap * sizeof(*ni)); 96 if (!ni) { 97 js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory"); 98 return false; 99 } 100 reg->items = ni; 101 reg->cap = new_cap; 102 } 103 104 char *copy = malloc((size_t)len + 1); 105 if (!copy) { 106 js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory"); 107 return false; 108 } 109 110 memcpy(copy, name, (size_t)len); 111 copy[len] = '\0'; 112 reg->items[reg->count++] = (repl_decl_name_t){ .name = copy, .len = (size_t)len }; 113 114 return true; 115} 116 117static void repl_decl_pending_free(repl_decl_pending_t *p) { 118 if (!p) return; 119 free(p->names); 120 free(p->lens); 121 p->names = NULL; 122 p->lens = NULL; 123 p->count = 0; 124 p->cap = 0; 125} 126 127static bool repl_decl_pending_contains( 128 const repl_decl_pending_t *p, 129 const char *name, uint32_t len 130) { 131 if (!p || !name) return false; 132 for (size_t i = 0; i < p->count; i++) 133 if (p->lens[i] == len && memcmp(p->names[i], name, (size_t)len) == 0) return true; 134 return false; 135} 136 137static bool repl_decl_pending_push( 138 ant_t *js, repl_decl_pending_t *p, 139 const char *name, uint32_t len 140) { 141 if (!p || !name || len == 0) return true; 142 if (repl_decl_pending_contains(p, name, len)) return true; 143 if (p->count >= p->cap) { 144 size_t new_cap = p->cap ? p->cap * 2 : 16; 145 const char **nn = realloc(p->names, new_cap * sizeof(*nn)); 146 if (!nn) { 147 js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory"); 148 return false; 149 } 150 uint32_t *nl = realloc(p->lens, new_cap * sizeof(*nl)); 151 if (!nl) { 152 p->names = nn; 153 js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "out of memory"); 154 return false; 155 } 156 p->names = nn; 157 p->lens = nl; 158 p->cap = new_cap; 159 } 160 p->names[p->count] = name; 161 p->lens[p->count] = len; 162 p->count++; 163 return true; 164} 165 166static bool repl_collect_pattern_names(ant_t *js, sv_ast_t *pat, repl_decl_pending_t *p) { 167 if (!pat) return true; 168 switch (pat->type) { 169 case N_IDENT: 170 return repl_decl_pending_push(js, p, pat->str, pat->len); 171 case N_ASSIGN_PAT: 172 case N_ASSIGN: 173 return repl_collect_pattern_names(js, pat->left, p); 174 case N_REST: 175 case N_SPREAD: 176 return repl_collect_pattern_names(js, pat->right, p); 177 case N_ARRAY: 178 case N_ARRAY_PAT: 179 for (int i = 0; i < pat->args.count; i++) { 180 if (!repl_collect_pattern_names(js, pat->args.items[i], p)) return false; 181 } 182 return true; 183 case N_OBJECT: 184 case N_OBJECT_PAT: 185 for (int i = 0; i < pat->args.count; i++) { 186 sv_ast_t *prop = pat->args.items[i]; 187 if (!prop) continue; 188 if (prop->type == N_PROPERTY) { 189 if (!repl_collect_pattern_names(js, prop->right, p)) return false; 190 } else if (prop->type == N_REST || prop->type == N_SPREAD) { 191 if (!repl_collect_pattern_names(js, prop->right, p)) return false; 192 } 193 } 194 return true; 195 default: return true; 196 } 197} 198 199static bool repl_collect_top_level_decls(ant_t *js, sv_ast_t *stmt, repl_decl_pending_t *p) { 200 if (!stmt) return true; 201 sv_ast_t *node = (stmt->type == N_EXPORT) ? stmt->left : stmt; 202 if (!node) return true; 203 204 if (node->type == N_VAR && node->var_kind != SV_VAR_VAR) { 205 for (int i = 0; i < node->args.count; i++) { 206 sv_ast_t *decl = node->args.items[i]; 207 if (!decl || decl->type != N_VARDECL || !decl->left) continue; 208 if (!repl_collect_pattern_names(js, decl->left, p)) return false; 209 } 210 return true; 211 } 212 213 if (node->type == N_CLASS && node->str && node->len > 0) 214 return repl_decl_pending_push(js, p, node->str, node->len); 215 216 if (node->type == N_IMPORT_DECL) { 217 for (int i = 0; i < node->args.count; i++) { 218 sv_ast_t *spec = node->args.items[i]; 219 if (!spec || spec->type != N_IMPORT_SPEC || !spec->right) continue; 220 if (spec->right->type != N_IDENT) continue; 221 if (!repl_decl_pending_push(js, p, spec->right->str, spec->right->len)) return false; 222 } 223 } 224 225 return true; 226} 227 228static bool repl_precheck_and_commit_lexicals( 229 ant_t *js, repl_decl_registry_t *reg, 230 const char *code, size_t len 231) { 232 if (!js || !reg || !code || len == 0) return true; 233 234 code_arena_mark_t mark = code_arena_mark(); 235 repl_decl_pending_t pending = {0}; 236 bool ok = true; 237 238 repl_clear_exception_state(js); 239 sv_ast_t *program = sv_parse(js, code, (ant_offset_t)len, false); 240 241 if (!program || js->thrown_exists) { 242 ok = true; 243 goto done; 244 } 245 246 for (int i = 0; i < program->args.count; i++) { 247 if ( 248 !repl_collect_top_level_decls( 249 js, program->args.items[i], &pending) 250 ) { ok = false; goto done; } 251 } 252 253 for (size_t i = 0; i < pending.count; i++) { 254 if (repl_decl_registry_contains(reg, pending.names[i], pending.lens[i])) { 255 js_mkerr_typed( 256 js, JS_ERR_SYNTAX, "Identifier '%.*s' has already been declared", 257 (int)pending.lens[i], pending.names[i] 258 ); 259 ok = false; goto done; 260 } 261 } 262 263 for (size_t i = 0; i < pending.count; i++) { 264 if ( 265 !repl_decl_registry_add( 266 js, reg, pending.names[i], pending.lens[i]) 267 ) { ok = false; goto done; } 268 } 269 270done: 271 code_arena_rewind(mark); 272 repl_decl_pending_free(&pending); 273 274 if (ok && js->thrown_exists) 275 repl_clear_exception_state(js); 276 277 return ok; 278} 279 280typedef enum { 281 REPL_PRINT_INTERACTIVE, 282 REPL_PRINT_LOAD, 283} repl_print_mode_t; 284 285static void repl_eval_chunk( 286 ant_t *js, repl_decl_registry_t *decl_registry, 287 const char *code, size_t len, 288 repl_print_mode_t print_mode 289) { 290 if (!repl_precheck_and_commit_lexicals(js, decl_registry, code, len)) { 291 if (js->thrown_exists) js_set(js, js_glob(js), "_error", js->thrown_value); 292 print_uncaught_throw(js); 293 return; 294 } 295 296 repl_clear_exception_state(js); 297 ant_value_t result = js_eval_bytecode_repl(js, code, len); 298 js_run_event_loop(js); 299 300 if (js->thrown_exists) { 301 js_set(js, js_glob(js), "_error", js->thrown_value); 302 if (print_uncaught_throw(js)) return; 303 } 304 305 if (print_mode == REPL_PRINT_INTERACTIVE) { 306 js_set(js, js_glob(js), "_", result); 307 print_repl_value(js, result, stdout); 308 return; 309 } 310 311 if (vtype(result) == T_ERR) fprintf(stderr, "%s\n", js_str(js, result)); 312 else if (vtype(result) != T_UNDEF) printf("%s\n", js_str(js, result)); 313} 314 315static cmd_result_t cmd_help(ant_t *js, history_t *history, const char *arg); 316static cmd_result_t cmd_exit(ant_t *js, history_t *history, const char *arg); 317static cmd_result_t cmd_load(ant_t *js, history_t *history, const char *arg); 318static cmd_result_t cmd_save(ant_t *js, history_t *history, const char *arg); 319static cmd_result_t cmd_stats(ant_t *js, history_t *history, const char *arg); 320static cmd_result_t cmd_copy(ant_t *js, history_t *history, const char *arg); 321static cmd_result_t cmd_clear(ant_t *js, history_t *history, const char *arg); 322static cmd_result_t cmd_history(ant_t *js, history_t *history, const char *arg); 323 324static const repl_command_t commands[] = { 325 { "help", "Show this help message", false, cmd_help }, 326 { "exit", "Exit the REPL", false, cmd_exit }, 327 { "clear", "Clear the screen", false, cmd_clear }, 328 { "history", "Show command history", false, cmd_history }, 329 { "load", "Load JS from a file into the REPL session", true, cmd_load }, 330 { "save", "Save all evaluated commands in this REPL session to a file", true, cmd_save }, 331 { "stats", "Show memory statistics", false, cmd_stats }, 332 { "copy", "Evaluate expression and copy its value", true, cmd_copy }, 333 { NULL, NULL, false, NULL } 334}; 335 336static const char *repl_command_usage(const repl_command_t *cmd) { 337 if (!cmd || !cmd->name) return ""; 338 if (strcmp(cmd->name, "copy") == 0) return ".copy [expr]"; 339 if (strcmp(cmd->name, "load") == 0) return ".load <file>"; 340 if (strcmp(cmd->name, "save") == 0) return ".save <file>"; 341 if (strcmp(cmd->name, "history") == 0) return ".history"; 342 if (strcmp(cmd->name, "clear") == 0) return ".clear"; 343 if (strcmp(cmd->name, "stats") == 0) return ".stats"; 344 if (strcmp(cmd->name, "exit") == 0) return ".exit"; 345 if (strcmp(cmd->name, "help") == 0) return ".help"; 346 return cmd->name; 347} 348 349static cmd_result_t cmd_help(ant_t *js, history_t *history, const char *arg) { 350 printf("\n%sREPL Commands:%s\n", C_BOLD, C_RESET); 351 for (const repl_command_t *cmd = commands; cmd->name; cmd++) { 352 const char *usage = repl_command_usage(cmd); 353 printf(" %s%-12s%s %s\n", C_CYAN, usage, C_RESET, cmd->description); 354 } 355 printf("\n%sKeybindings:%s\n", C_BOLD, C_RESET); 356 printf(" Ctrl+C Abort current expression (press twice to exit)\n"); 357 printf(" Left/Right Move backward/forward one character\n"); 358 printf(" Home/End Jump to start/end of line\n"); 359 printf(" Up/Down Navigate history\n"); 360 printf(" Backspace Delete character backward\n"); 361 printf(" Delete Delete character under cursor\n"); 362 printf(" Enter Submit input\n"); 363 printf("\n%sSpecial Variables:%s\n", C_BOLD, C_RESET); 364 printf(" %s_%s Last expression result\n", C_CYAN, C_RESET); 365 printf(" %s_error%s Last error\n\n", C_CYAN, C_RESET); 366 return CMD_OK; 367} 368 369static cmd_result_t cmd_exit(ant_t *js, history_t *history, const char *arg) { 370 return CMD_EXIT; 371} 372 373static cmd_result_t cmd_load(ant_t *js, history_t *history, const char *arg) { 374 (void)history; 375 if (!arg || *arg == '\0') { 376 fprintf(stderr, "Usage: .load <filename>\n"); 377 return CMD_OK; 378 } 379 380 FILE *fp = fopen(arg, "r"); 381 if (fp == NULL) { 382 fprintf(stderr, "Failed to open file: %s\n", arg); 383 return CMD_OK; 384 } 385 386 fseek(fp, 0, SEEK_END); 387 long file_size = ftell(fp); 388 fseek(fp, 0, SEEK_SET); 389 390 char *file_buffer = malloc(file_size + 1); 391 if (file_buffer) { 392 size_t len = fread(file_buffer, 1, file_size, fp); 393 file_buffer[len] = '\0'; 394 repl_eval_chunk( 395 js, g_repl_decl_registry, 396 file_buffer, len, REPL_PRINT_LOAD 397 ); 398 free(file_buffer); 399 } 400 fclose(fp); 401 return CMD_OK; 402} 403 404static cmd_result_t cmd_save(ant_t *js, history_t *history, const char *arg) { 405 if (!arg || *arg == '\0') { 406 fprintf(stderr, "Usage: .save <filename>\n"); 407 return CMD_OK; 408 } 409 410 FILE *fp = fopen(arg, "w"); 411 if (fp == NULL) { 412 fprintf(stderr, "Failed to open file for writing: %s\n", arg); 413 return CMD_OK; 414 } 415 416 for (int i = 0; i < history->count; i++) { 417 fprintf(fp, "%s\n", history->lines[i]); 418 } 419 fclose(fp); 420 printf("Session saved to %s\n", arg); 421 return CMD_OK; 422} 423 424static cmd_result_t cmd_stats(ant_t *js, history_t *history, const char *arg) { 425 ant_value_t stats_fn = js_get(js, rt->ant_obj, "stats"); 426 ant_value_t result = sv_vm_call(js->vm, js, stats_fn, js_mkundef(), NULL, 0, NULL, false); 427 console_emit(js, false, NULL, &result, 1); 428 return CMD_OK; 429} 430 431static cmd_result_t cmd_clear(ant_t *js, history_t *history, const char *arg) { 432 fputs("\033[2J\033[H", stdout); 433 fflush(stdout); 434 return CMD_OK; 435} 436 437static cmd_result_t cmd_history(ant_t *js, history_t *history, const char *arg) { 438 for (int i = 0; i < history->count; i++) { 439 printf("%4d %s\n", i + 1, history->lines[i]); 440 } 441 return CMD_OK; 442} 443 444#ifdef _WIN32 445static bool repl_copy_with_command(const char *data, size_t len) { 446 FILE *pipe = _popen("clip", "wb"); 447 if (!pipe) return false; 448 449 size_t written = fwrite(data, 1, len, pipe); 450 int close_rc = _pclose(pipe); 451 return written == len && close_rc == 0; 452} 453#else 454static bool repl_copy_with_single_command(const char *cmd, const char *data, size_t len) { 455 FILE *pipe = popen(cmd, "w"); 456 if (!pipe) return false; 457 458 size_t written = fwrite(data, 1, len, pipe); 459 int close_rc = pclose(pipe); 460 return written == len && close_rc == 0; 461} 462 463static bool repl_copy_with_command(const char *data, size_t len) { 464 static const char *cmds[] = { 465 "pbcopy", 466 "wl-copy", 467 "xclip -selection clipboard", 468 "xsel --clipboard --input", 469 }; 470 for (size_t i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { 471 if (repl_copy_with_single_command(cmds[i], data, len)) return true; 472 } 473 return false; 474} 475#endif 476 477static cmd_result_t cmd_copy(ant_t *js, history_t *history, const char *arg) { 478 (void)history; 479 if (!arg || *arg == '\0') { 480 fprintf(stderr, "Usage: .copy <expression>\n"); 481 return CMD_OK; 482 } 483 484 repl_clear_exception_state(js); 485 ant_value_t result = js_eval_bytecode_repl(js, arg, strlen(arg)); 486 487 js_run_event_loop(js); 488 if (js->thrown_exists) { 489 js_set(js, js_glob(js), "_error", js->thrown_value); 490 if (print_uncaught_throw(js)) return CMD_OK; 491 } 492 493 js_set(js, js_glob(js), "_", result); 494 495 char cbuf[512]; 496 js_cstr_t cstr = js_to_cstr(js, result, cbuf, sizeof(cbuf)); 497 498 bool copied_command = repl_copy_with_command(cstr.ptr, cstr.len); 499 if (cstr.needs_free) free((void *)cstr.ptr); 500 501 if (!copied_command) { 502 fprintf(stderr, "Failed to copy to clipboard (no clipboard command available).\n"); 503 return CMD_OK; 504 } 505 506 printf("Copied to clipboard.\n"); 507 return CMD_OK; 508} 509 510static cmd_result_t execute_command(ant_t *js, history_t *history, const char *line) { 511 const char *cmd_start = line + 1; 512 513 for (const repl_command_t *cmd = commands; cmd->name; cmd++) { 514 size_t n = strlen(cmd->name); 515 if (strncmp(cmd_start, cmd->name, n) != 0) continue; 516 517 char next = cmd_start[n]; 518 if (cmd->has_arg && (next == ' ' || next == '\0')) { 519 const char *arg = cmd_start + n; 520 while (*arg == ' ') arg++; 521 return cmd->handler(js, history, arg); 522 } 523 if (!cmd->has_arg && next == '\0') return cmd->handler(js, history, NULL); 524 } 525 526 return CMD_NOT_FOUND; 527} 528 529typedef struct { 530 int paren, bracket, brace; 531 int *templates; 532 int template_count, template_cap; 533 char string_char; 534 bool in_string, escaped; 535 char last_code_char; 536} parse_state_t; 537 538static void push_template(parse_state_t *s) { 539 if (s->template_count >= s->template_cap) { 540 s->template_cap = s->template_cap ? s->template_cap * 2 : 8; 541 int *new_templates = realloc(s->templates, s->template_cap * sizeof(int)); 542 if (!new_templates) { return; } s->templates = new_templates; 543 } 544 s->templates[s->template_count++] = s->brace; 545} 546 547static bool in_template_text(parse_state_t *s) { 548 return s->template_count > 0 && s->brace == s->templates[s->template_count - 1]; 549} 550 551static bool is_incomplete_input(const char *code, size_t len) { 552 parse_state_t s = {0}; 553 554 for (size_t i = 0; i < len; i++) { 555 char c = code[i]; 556 557 if (s.escaped) { s.escaped = false; continue; } 558 if (c == '\\' && (s.in_string || s.template_count > 0)) { s.escaped = true; continue; } 559 if (s.in_string) { if (c == s.string_char) s.in_string = false; continue; } 560 561 if (in_template_text(&s)) { 562 if (c == '`') s.template_count--; 563 else if (c == '$' && i + 1 < len && code[i + 1] == '{') { s.brace++; i++; } 564 continue; 565 } 566 567 if (c == '/' && i + 1 < len) { 568 if (code[i + 1] == '/') { while (i < len && code[i] != '\n') i++; continue; } 569 if (code[i + 1] == '*') { 570 for (i += 2; i + 1 < len && !(code[i] == '*' && code[i + 1] == '/'); i++); 571 if (i + 1 >= len) { free(s.templates); return true; } 572 i++; continue; 573 } 574 if (js_regex_can_start(code, i)) { 575 size_t regex_end = 0; 576 if (!js_scan_regex_literal(code, len, i, &regex_end)) { free(s.templates); return true; } 577 i = regex_end - 1; 578 continue; 579 } 580 } 581 582 switch (c) { 583 case '"': case '\'': s.in_string = true; s.string_char = c; break; 584 case '`': push_template(&s); break; 585 case '(': s.paren++; break; case ')': s.paren--; break; 586 case '[': s.bracket++; break; case ']': s.bracket--; break; 587 case '{': s.brace++; break; case '}': s.brace--; break; 588 } 589 if (!isspace((unsigned char)c)) s.last_code_char = c; 590 } 591 592 bool incomplete = 593 s.in_string || s.template_count > 0 || 594 s.paren > 0 || s.bracket > 0 || s.brace > 0 || 595 s.last_code_char == ','; 596 597 free(s.templates); 598 return incomplete; 599} 600 601void ant_repl_run() { 602 ant_t *js = rt->js; 603 ant_readline_install_signal_handler(); 604 605 js_set_filename(js, "[repl]"); 606 js_setup_import_meta(js, "[repl]"); 607 608 crprintf( 609 "Welcome to <red+bold>Ant JavaScript</> v%s\n" 610 "Type <cyan>.copy [code]</cyan> to copy, <cyan>.help</cyan> for more information.\n\n", 611 ANT_VERSION 612 ); 613 614 history_t history; 615 ant_history_init(&history, 512); 616 ant_history_load(&history); 617 618 repl_decl_registry_t decl_registry = {0}; 619 g_repl_decl_registry = &decl_registry; 620 621 js_set(js, js_glob(js), "__dirname", js_mkstr(js, ".", 1)); 622 js_set(js, js_glob(js), "__filename", js_mkstr(js, "[repl]", 6)); 623 624 js_set(js, js_glob(js), "_", js_mkundef()); 625 js_set(js, js_glob(js), "_error", js_mkundef()); 626 627 js_set_descriptor(js, js_as_obj(js_glob(js)), "_", 1, JS_DESC_W | JS_DESC_C); 628 js_set_descriptor(js, js_as_obj(js_glob(js)), "_error", 6, JS_DESC_W | JS_DESC_C); 629 630 int prev_ctrl_c_count = 0; 631 char *multiline_buf = NULL; 632 size_t multiline_len = 0; 633 size_t multiline_cap = 0; 634 635 while (1) { 636 const char *prompt = multiline_buf ? "\x1b[2m|\x1b[0m " : "\x1b[2m❯\x1b[0m "; 637 highlight_state prefix_state = HL_STATE_INIT; 638 if (multiline_buf && multiline_len > 0) { 639 char scratch[8192]; 640 ant_highlight_stateful(multiline_buf, multiline_len, scratch, sizeof(scratch), &prefix_state); 641 } 642 643 fputs(prompt, stdout); 644 fflush(stdout); 645 646 char *line = NULL; 647 ant_readline_result_t readline_status = 648 ant_readline(&history, prompt, prefix_state, &line); 649 650 if (readline_status == ANT_READLINE_INTERRUPT) { 651 if (multiline_buf) { 652 free(multiline_buf); 653 multiline_buf = NULL; 654 multiline_len = 0; 655 multiline_cap = 0; 656 prev_ctrl_c_count = 0; 657 if (line) free(line); 658 continue; 659 } 660 if (prev_ctrl_c_count > 0) { 661 if (line) free(line); 662 break; 663 } 664 printf("(To exit, press Ctrl+C again or type .exit)\n"); 665 prev_ctrl_c_count++; 666 if (line) free(line); 667 continue; 668 } 669 670 if (readline_status == ANT_READLINE_EOF || line == NULL) { 671 if (multiline_buf) { 672 free(multiline_buf); 673 multiline_buf = NULL; 674 multiline_len = 0; 675 multiline_cap = 0; 676 continue; 677 } 678 break; 679 } 680 681 prev_ctrl_c_count = 0; 682 size_t line_len = strlen(line); 683 684 if (line_len == 0 && multiline_buf) { 685 if (multiline_len + 1 >= multiline_cap) { 686 multiline_cap = multiline_cap ? multiline_cap * 2 : 256; 687 multiline_buf = realloc(multiline_buf, multiline_cap); 688 } 689 multiline_buf[multiline_len++] = '\n'; 690 multiline_buf[multiline_len] = '\0'; 691 free(line); 692 continue; 693 } 694 695 if (line_len == 0) { 696 free(line); 697 continue; 698 } 699 700 if (!multiline_buf && line[0] == '.') { 701 cmd_result_t result = execute_command(js, &history, line); 702 if (result == CMD_EXIT) { 703 free(line); 704 break; 705 } else if (result == CMD_NOT_FOUND) { 706 printf("Unknown command: %s\n", line); 707 printf("Type \".help\" for more information.\n"); 708 } 709 free(line); 710 continue; 711 } 712 713 size_t new_len = multiline_len + line_len + 1; 714 if (new_len >= multiline_cap || !multiline_buf) { 715 multiline_cap = multiline_cap ? multiline_cap * 2 : 256; 716 if (multiline_cap < new_len + 1) multiline_cap = new_len + 1; 717 multiline_buf = realloc(multiline_buf, multiline_cap); 718 } 719 720 if (multiline_len > 0) { 721 multiline_buf[multiline_len++] = '\n'; 722 } 723 memcpy(multiline_buf + multiline_len, line, line_len); 724 multiline_len += line_len; 725 multiline_buf[multiline_len] = '\0'; 726 free(line); 727 728 if (is_incomplete_input(multiline_buf, multiline_len)) continue; 729 ant_history_add(&history, multiline_buf); 730 731 repl_eval_chunk( 732 js, &decl_registry, multiline_buf, 733 multiline_len, REPL_PRINT_INTERACTIVE 734 ); 735 736 free(multiline_buf); 737 multiline_buf = NULL; 738 multiline_len = 0; 739 multiline_cap = 0; 740 } 741 742 if (multiline_buf) free(multiline_buf); 743 ant_readline_shutdown(); 744 745 repl_decl_registry_free(&decl_registry); 746 g_repl_decl_registry = NULL; 747 748 ant_history_save(&history); 749 ant_history_free(&history); 750}