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.

add repl

+357 -21
+6
include/repl.h
··· 1 + #ifndef REPL_H 2 + #define REPL_H 3 + 4 + void ant_repl_run(void); 5 + 6 + #endif
+2 -1
meson.build
··· 13 13 sources = files( 14 14 'src/main.c', 15 15 'src/ant.c', 16 + 'src/repl.c', 16 17 'src/runtime.c', 17 18 ) + files(module_files) 18 19 ··· 42 43 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 43 44 44 45 version_conf = configuration_data() 45 - version_conf.set('ANT_VERSION', '0.0.6.11') 46 + version_conf.set('ANT_VERSION', '0.0.6.12') 46 47 version_conf.set('ANT_GIT_HASH', git_hash) 47 48 version_conf.set('ANT_BUILD_DATE', build_date) 48 49
+17 -20
src/main.c
··· 9 9 #include "ant.h" 10 10 #include "config.h" 11 11 #include "runtime.h" 12 + #include "repl.h" 12 13 13 14 #include "modules/builtin.h" 14 15 #include "modules/io.h" ··· 17 18 #include "modules/timer.h" 18 19 #include "modules/json.h" 19 20 #include "modules/fetch.h" 21 + 22 + int js_result = EXIT_SUCCESS; 20 23 21 24 static int execute_module(struct js *js, const char *filename) { 22 25 char *filename_copy = strdup(filename); ··· 77 80 78 81 if (help->count > 0) { 79 82 printf("Ant sized JavaScript\n\n"); 80 - printf("Usage: ant"); 81 - arg_print_syntax(stdout, argtable, "\n\n"); 83 + printf("Usage: ant [options] [module.js]\n\n"); 84 + printf("If no module file is specified, ant starts in REPL mode.\n\n"); 85 + printf("Options:\n"); 82 86 arg_print_glossary(stdout, argtable, " %-25s %s\n"); 83 87 arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); 84 88 return EXIT_SUCCESS; ··· 97 101 return EXIT_FAILURE; 98 102 } 99 103 100 - if (file->count == 0) { 101 - fprintf(stderr, "Error: No input file specified\n"); 102 - printf("Try 'ant --help' for more information.\n"); 103 - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); 104 - return EXIT_FAILURE; 105 - } 106 - 107 - const char *module_file = file->filename[0]; 104 + bool repl_mode = (file->count == 0); 105 + const char *module_file = repl_mode ? NULL : file->filename[0]; 108 106 dump = debug->count; 109 107 110 108 size_t initial_size = 4 * 1024 * 1024; ··· 121 119 return EXIT_FAILURE; 122 120 } 123 121 124 - if (gct->count > 0) { 125 - js_setgct(js, gct->ival[0]); 126 - } 127 - 128 - struct ant_runtime *rt = ant_runtime_init(js); 129 - 122 + if (gct->count > 0) js_setgct(js, gct->ival[0]); 123 + ant_runtime_init(js); 124 + 130 125 init_builtin_module(); 131 126 init_crypto_module(); 132 127 init_fetch_module(); ··· 134 129 init_json_module(); 135 130 init_server_module(); 136 131 init_timer_module(); 137 - 138 - int result = execute_module(js, module_file); 132 + 133 + if (repl_mode) ant_repl_run(); else { 134 + js_result = execute_module(js, module_file); 135 + js_run_event_loop(js); 136 + } 139 137 140 - js_run_event_loop(js); 141 138 if (dump) js_dump(js); 142 139 143 140 js_destroy(js); 144 141 arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); 145 142 146 - return result; 143 + return js_result; 147 144 }
+332
src/repl.c
··· 1 + #include <stdio.h> 2 + #include <stdlib.h> 3 + #include <string.h> 4 + #include <termios.h> 5 + #include <ctype.h> 6 + #include <signal.h> 7 + #include <unistd.h> 8 + 9 + #include "ant.h" 10 + #include "repl.h" 11 + #include "config.h" 12 + #include "runtime.h" 13 + 14 + #define MAX_HISTORY 100 15 + #define MAX_LINE_LENGTH 4096 16 + 17 + static volatile sig_atomic_t ctrl_c_pressed = 0; 18 + 19 + typedef struct { 20 + char **lines; 21 + int count; 22 + int capacity; 23 + int current; 24 + } history_t; 25 + 26 + static void sigint_handler(int sig) { 27 + (void)sig; 28 + ctrl_c_pressed++; 29 + } 30 + 31 + static void history_init(history_t *hist) { 32 + hist->capacity = MAX_HISTORY; 33 + hist->lines = malloc(sizeof(char*) * hist->capacity); 34 + hist->count = 0; 35 + hist->current = -1; 36 + } 37 + 38 + static void history_add(history_t *hist, const char *line) { 39 + if (strlen(line) == 0) return; 40 + if (hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) return; 41 + 42 + if (hist->count >= hist->capacity) { 43 + free(hist->lines[0]); 44 + memmove(hist->lines, hist->lines + 1, sizeof(char*) * (hist->capacity - 1)); 45 + hist->count--; 46 + } 47 + 48 + hist->lines[hist->count++] = strdup(line); 49 + hist->current = hist->count; 50 + } 51 + 52 + static const char* history_prev(history_t *hist) { 53 + if (hist->count == 0) return NULL; 54 + if (hist->current > 0) hist->current--; 55 + return hist->lines[hist->current]; 56 + } 57 + 58 + static const char* history_next(history_t *hist) { 59 + if (hist->count == 0) return NULL; 60 + if (hist->current < hist->count - 1) { 61 + hist->current++; 62 + return hist->lines[hist->current]; 63 + } 64 + hist->current = hist->count; 65 + return ""; 66 + } 67 + 68 + static void history_free(history_t *hist) { 69 + for (int i = 0; i < hist->count; i++) free(hist->lines[i]); 70 + free(hist->lines); 71 + } 72 + 73 + static char* read_line_with_history(history_t *hist, struct js *js) { 74 + static struct termios old_tio, new_tio; 75 + char *line = malloc(MAX_LINE_LENGTH); 76 + 77 + int pos = 0; 78 + int len = 0; 79 + line[0] = '\0'; 80 + 81 + tcgetattr(STDIN_FILENO, &old_tio); 82 + new_tio = old_tio; 83 + new_tio.c_lflag &= ~(ICANON | ECHO); 84 + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); 85 + 86 + while (1) { 87 + if (ctrl_c_pressed > 0) { 88 + printf("\n"); 89 + fflush(stdout); 90 + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 91 + free(line); 92 + return NULL; 93 + } 94 + 95 + int c = getchar(); 96 + 97 + if (c == EOF) { 98 + if (ctrl_c_pressed > 0) { 99 + printf("\n"); 100 + fflush(stdout); 101 + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 102 + free(line); 103 + return NULL; 104 + } 105 + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 106 + free(line); 107 + return NULL; 108 + } 109 + 110 + if (c == 27) { 111 + int next1 = getchar(); 112 + if (next1 == '[') { 113 + int next2 = getchar(); 114 + 115 + if (next2 == 'A') { 116 + const char *prev = history_prev(hist); 117 + if (prev) { 118 + printf("\r\033[K> "); 119 + fflush(stdout); 120 + 121 + strcpy(line, prev); 122 + len = strlen(line); 123 + pos = len; 124 + printf("%s", line); 125 + fflush(stdout); 126 + } 127 + continue; 128 + } else if (next2 == 'B') { 129 + const char *next = history_next(hist); 130 + if (next) { 131 + printf("\r\033[K> "); 132 + fflush(stdout); 133 + 134 + strcpy(line, next); 135 + len = strlen(line); 136 + pos = len; 137 + printf("%s", line); 138 + fflush(stdout); 139 + } 140 + continue; 141 + } else if (next2 == 'C') { 142 + if (pos < len) { 143 + printf("\033[C"); 144 + fflush(stdout); 145 + pos++; 146 + } 147 + continue; 148 + } else if (next2 == 'D') { 149 + if (pos > 0) { 150 + printf("\033[D"); 151 + fflush(stdout); 152 + pos--; 153 + } 154 + continue; 155 + } 156 + } 157 + continue; 158 + } 159 + 160 + if (c == 127 || c == 8) { 161 + if (pos > 0) { 162 + memmove(line + pos - 1, line + pos, len - pos + 1); 163 + pos--; 164 + len--; 165 + printf("\b\033[K%s", line + pos); 166 + for (int i = 0; i < len - pos; i++) printf("\033[D"); 167 + fflush(stdout); 168 + } 169 + continue; 170 + } 171 + 172 + if (c == '\n' || c == '\r') { 173 + printf("\n"); 174 + fflush(stdout); 175 + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 176 + line[len] = '\0'; 177 + return line; 178 + } 179 + 180 + if (isprint(c) && len < MAX_LINE_LENGTH - 1) { 181 + memmove(line + pos + 1, line + pos, len - pos + 1); 182 + line[pos] = c; 183 + pos++; 184 + len++; 185 + printf("%c%s", c, line + pos); 186 + for (int i = 0; i < len - pos; i++) printf("\033[D"); 187 + fflush(stdout); 188 + } 189 + } 190 + } 191 + 192 + void ant_repl_run() { 193 + struct js *js = rt->js; 194 + 195 + printf("Welcome to Ant JavaScript v%s\n", ANT_VERSION); 196 + printf("Type \".help\" for more information.\n"); 197 + 198 + struct sigaction sa; 199 + sa.sa_handler = sigint_handler; 200 + sigemptyset(&sa.sa_mask); 201 + sa.sa_flags = 0; 202 + sigaction(SIGINT, &sa, NULL); 203 + 204 + history_t history; 205 + history_init(&history); 206 + 207 + js_set(js, js_glob(js), "__dirname", js_mkstr(js, ".", 1)); 208 + int prev_ctrl_c_count = 0; 209 + 210 + while (1) { 211 + printf("> "); 212 + fflush(stdout); 213 + 214 + ctrl_c_pressed = 0; 215 + char *line = read_line_with_history(&history, js); 216 + 217 + if (ctrl_c_pressed > 0) { 218 + if (prev_ctrl_c_count > 0) { 219 + printf("\n"); 220 + if (line) free(line); 221 + break; 222 + } 223 + printf("(To exit, press Ctrl+C again or type .exit)\n"); 224 + prev_ctrl_c_count++; 225 + if (line) free(line); 226 + continue; 227 + } 228 + 229 + if (line == NULL) break; 230 + prev_ctrl_c_count = 0; 231 + size_t line_len = strlen(line); 232 + 233 + if (line_len == 0) { 234 + free(line); 235 + continue; 236 + } 237 + 238 + history_add(&history, line); 239 + 240 + if (line[0] == '.') { 241 + if (strcmp(line, ".help") == 0) { 242 + printf(" .help - Show this help message\n"); 243 + printf(" .exit - Exit the REPL\n"); 244 + printf(" .clear - Clear the current context\n"); 245 + printf(" .load - Load JS from a file into the REPL session\n"); 246 + printf(" .save - Save all evaluated commands in this REPL session to a file\n"); 247 + printf(" .gc - Run garbage collector\n"); 248 + printf(" .stats - Show memory statistics\n"); 249 + printf("\nPress Ctrl+C to abort current expression.\n"); 250 + } else if (strcmp(line, ".exit") == 0) { 251 + free(line); 252 + break; 253 + } else if (strcmp(line, ".clear") == 0) { 254 + printf("Clearing context...\n"); 255 + } else if (strncmp(line, ".load", 5) == 0) { 256 + const char *filename = line + 5; 257 + while (*filename == ' ') filename++; 258 + 259 + if (*filename == '\0') { 260 + fprintf(stderr, "Usage: .load <filename>\n"); 261 + } else { 262 + FILE *fp = fopen(filename, "r"); 263 + if (fp == NULL) { 264 + fprintf(stderr, "Failed to open file: %s\n", filename); 265 + } else { 266 + fseek(fp, 0, SEEK_END); 267 + long file_size = ftell(fp); 268 + fseek(fp, 0, SEEK_SET); 269 + 270 + char *file_buffer = malloc(file_size + 1); 271 + if (file_buffer) { 272 + size_t len = fread(file_buffer, 1, file_size, fp); 273 + file_buffer[len] = '\0'; 274 + 275 + jsval_t result = js_eval(js, file_buffer, len); 276 + if (js_type(result) == JS_ERR) { 277 + fprintf(stderr, "%s\n", js_str(js, result)); 278 + } else if (js_type(result) != JS_UNDEF) { 279 + printf("%s\n", js_str(js, result)); 280 + } 281 + 282 + free(file_buffer); 283 + } 284 + fclose(fp); 285 + } 286 + } 287 + } else if (strncmp(line, ".save", 5) == 0) { 288 + const char *filename = line + 5; 289 + while (*filename == ' ') filename++; 290 + 291 + if (*filename == '\0') { 292 + fprintf(stderr, "Usage: .save <filename>\n"); 293 + } else { 294 + FILE *fp = fopen(filename, "w"); 295 + if (fp == NULL) { 296 + fprintf(stderr, "Failed to open file for writing: %s\n", filename); 297 + } else { 298 + for (int i = 0; i < history.count; i++) fprintf(fp, "%s\n", history.lines[i]); 299 + fclose(fp); 300 + printf("Session saved to %s\n", filename); 301 + } 302 + } 303 + } else if (strcmp(line, ".gc") == 0) { 304 + js_gc(js); 305 + printf("Garbage collection complete\n"); 306 + } else if (strcmp(line, ".stats") == 0) { 307 + size_t total, min, cstack; 308 + js_stats(js, &total, &min, &cstack); 309 + printf("Memory stats:\n"); 310 + printf(" Total: %zu bytes\n", total); 311 + printf(" Free: %zu bytes\n", min); 312 + printf(" C Stack: %zu bytes\n", cstack); 313 + } else { 314 + printf("Unknown command: %s\n", line); 315 + printf("Type \".help\" for more information.\n"); 316 + } 317 + free(line); 318 + continue; 319 + } 320 + 321 + jsval_t eval_result = js_eval(js, line, line_len); 322 + 323 + if (js_type(eval_result) == JS_ERR) fprintf(stderr, "%s\n", js_str(js, eval_result)); else { 324 + const char *str = js_str(js, eval_result); 325 + printf("%s\n", str); 326 + } 327 + 328 + free(line); 329 + } 330 + 331 + history_free(&history); 332 + }