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 syntax highlighting to REPL and error messages

+819 -87
+89
include/highlight.h
··· 1 + #ifndef HIGHLIGHT_H 2 + #define HIGHLIGHT_H 3 + 4 + #include <stddef.h> 5 + #include <stdbool.h> 6 + 7 + typedef enum { 8 + HL_STATE_NORMAL, 9 + HL_STATE_STRING_SINGLE, 10 + HL_STATE_STRING_DOUBLE, 11 + HL_STATE_TEMPLATE, 12 + HL_STATE_TEMPLATE_EXPR, 13 + HL_STATE_BLOCK_COMMENT, 14 + } hl_mode_t; 15 + 16 + typedef struct { 17 + hl_mode_t mode; 18 + int template_depth; 19 + } highlight_state; 20 + 21 + typedef enum { 22 + HL_NONE, 23 + HL_KEYWORD, 24 + HL_KEYWORD_ITALIC, 25 + HL_KEYWORD_DELETE, 26 + HL_KEYWORD_EXTENDS, 27 + HL_TYPE, 28 + HL_TYPE_STRING, 29 + HL_TYPE_BOOLEAN, 30 + HL_LITERAL_NULL, 31 + HL_STRING, 32 + HL_NUMBER, 33 + HL_COMMENT, 34 + HL_FUNCTION_NAME, 35 + HL_CLASS_NAME, 36 + HL_PARENT_CLASS, 37 + HL_FUNCTION, 38 + HL_PROPERTY, 39 + HL_OPERATOR, 40 + HL_OPERATOR_CMP, 41 + HL_OPTIONAL_CHAIN, 42 + HL_SEMICOLON, 43 + } hl_token_class; 44 + 45 + typedef struct { 46 + size_t off; 47 + size_t len; 48 + hl_token_class cls; 49 + } hl_span; 50 + 51 + typedef enum { 52 + HL_CTX_NONE, 53 + HL_CTX_AFTER_FUNCTION, 54 + HL_CTX_AFTER_CLASS, 55 + HL_CTX_AFTER_EXTENDS, 56 + } hl_context; 57 + 58 + typedef struct { 59 + const char *input; 60 + size_t input_len; 61 + size_t pos; 62 + highlight_state state; 63 + hl_context ctx; 64 + } hl_iter; 65 + 66 + static inline highlight_state hl_iter_state(const hl_iter *it) { return it->state; } 67 + 68 + void hl_iter_init(hl_iter *it, const char *input, size_t input_len, const highlight_state *state); 69 + bool hl_iter_next(hl_iter *it, hl_span *out); 70 + 71 + int ant_highlighl( 72 + const char *input, size_t input_len, 73 + char *out, size_t out_size 74 + ); 75 + 76 + int ant_highlight_stateful( 77 + const char *input, size_t input_len, 78 + char *out, size_t out_size, 79 + highlight_state *state 80 + ); 81 + 82 + int highlight_js_line_clipped( 83 + const char *line, size_t line_len, 84 + size_t max_cols, 85 + char *out, size_t out_size, 86 + highlight_state *state 87 + ); 88 + 89 + #endif
+1
include/silver/lexer.h
··· 48 48 49 49 uint8_t sv_lexer_next(sv_lexer_t *lx); 50 50 uint8_t sv_lexer_lookahead(sv_lexer_t *lx); 51 + uint8_t sv_parsekeyword(const char *buf, size_t len); 51 52 52 53 bool is_space(int c); 53 54 bool is_digit(int c);
+1
include/silver/vm.h
··· 5 5 6 6 typedef struct sv_vm sv_vm_t; 7 7 typedef struct sv_func sv_func_t; 8 + size_t os_thread_stack_size(void); 8 9 9 10 typedef enum { 10 11 SV_VM_MAIN,
+3
src/ant.c
··· 13087 13087 13088 13088 js_clear_error_site(js); 13089 13089 jsval_t result; 13090 + // TODO: this-newtarget-frame-migration 13091 + jsval_t saved_this = js->this_val; 13090 13092 13091 13093 if (sv_dump_bytecode_unlikely) sv_disasm(js, func, js->filename); 13092 13094 if (func->is_tla) result = sv_execute_entry_tla(js, func, js->this_val); 13093 13095 else result = sv_execute_entry(js->vm, func, js->this_val, NULL, 0); 13094 13096 13097 + js->this_val = saved_this; 13095 13098 return result; 13096 13099 } 13097 13100
+32 -18
src/errors.c
··· 2 2 #include "internal.h" 3 3 #include "silver/engine.h" 4 4 #include "modules/io.h" 5 + #include "highlight.h" 5 6 6 7 #include <stdlib.h> 7 8 #include <string.h> 8 9 #include <stdarg.h> 9 10 #include <limits.h> 11 + #include <crprintf.h> 10 12 11 13 #ifdef _WIN32 12 14 #define WIN32_LEAN_AND_MEAN ··· 475 477 476 478 int gutter_w = count_digits_int(error_line_no); 477 479 int src_cols_limit = error_context_src_cols_limit(gutter_w); 478 - int line_no = first_line_no; 480 + 481 + char tagged[4096]; char rendered[8192]; 482 + highlight_state hl_state = { .mode = HL_STATE_NORMAL, .template_depth = 0 }; 483 + 479 484 jsoff_t cur = ctx_start; 480 - 485 + int line_no = first_line_no; 486 + 481 487 while (cur <= err_line_end && cur < src_len) { 482 - jsoff_t line_start = cur; 483 - jsoff_t line_end = cur; 484 - 485 - while (line_end < src_len && src[line_end] != '\n' && src[line_end] != '\0') line_end++; 486 - int line_len = (int)(line_end - line_start); 487 - bool clipped = (src_cols_limit > 0 && line_len > src_cols_limit); 488 - int shown_len = clipped ? src_cols_limit : line_len; 488 + jsoff_t ls = cur, le = cur; 489 + while (le < src_len && src[le] != '\n' && src[le] != '\0') le++; 489 490 490 - *n = append_errbuf_fmt(eb, *n, "\n%*d | %.*s", gutter_w, line_no, shown_len, src + line_start); 491 - if (clipped) *n = append_errbuf_fmt(eb, *n, "..."); 491 + int line_len = (int)(le - ls); 492 + bool was_clipped = (src_cols_limit > 0 && line_len > src_cols_limit); 492 493 493 - if (line_start == err_line_start) { 494 + if (!io_no_color) { 495 + highlight_js_line_clipped( 496 + src + ls, (size_t)line_len, (size_t)src_cols_limit, 497 + tagged, sizeof(tagged), &hl_state 498 + ); 499 + crsprintf_stateful(rendered, sizeof(rendered), NULL, tagged); 500 + *n = append_errbuf_fmt(eb, *n, "\n%*d | %s", gutter_w, line_no, rendered); 501 + } else { 502 + int shown = was_clipped ? src_cols_limit : line_len; 503 + *n = append_errbuf_fmt(eb, *n, "\n%*d | %.*s", gutter_w, line_no, shown, src + ls); 504 + } 505 + 506 + if (was_clipped) *n = append_errbuf_fmt(eb, *n, "..."); 507 + if (ls == err_line_start) { 494 508 *n = append_errbuf_fmt(eb, *n, "\n%*s ", gutter_w, ""); 495 509 int caret_col = error_col; 496 510 int caret_span = error_span_cols; 497 - 498 - if (clipped) { 499 - int max_col = shown_len > 0 ? shown_len : 1; 511 + if (was_clipped) { 512 + int max_col = src_cols_limit > 0 ? src_cols_limit : 1; 500 513 if (caret_col > max_col) caret_col = max_col; 501 514 if (caret_span > max_col - caret_col + 1) caret_span = max_col - caret_col + 1; 502 515 if (caret_span < 1) caret_span = 1; 503 - } append_error_caret(eb, n, caret_col, caret_span); 516 + } 517 + append_error_caret(eb, n, caret_col, caret_span); 504 518 } 505 519 506 - if (line_end >= src_len || src[line_end] == '\0') break; 507 - cur = line_end + 1; line_no++; 520 + if (le >= src_len || src[le] == '\0') break; 521 + cur = le + 1; line_no++; 508 522 } 509 523 510 524 return true;
+567
src/highlight.c
··· 1 + #include <stdio.h> 2 + #include <string.h> 3 + #include <stdbool.h> 4 + #include <crprintf.h> 5 + 6 + #include "tokens.h" 7 + #include "highlight.h" 8 + #include "silver/lexer.h" 9 + 10 + typedef struct { const char *op; int len; hl_token_class cls; } op_entry_t; 11 + 12 + static const op_entry_t operators[] = { 13 + { "===", 3, HL_OPERATOR_CMP }, 14 + { "!==", 3, HL_OPERATOR_CMP }, 15 + { "...", 3, HL_OPERATOR }, 16 + { "=>", 2, HL_OPERATOR }, 17 + { "==", 2, HL_OPERATOR_CMP }, 18 + { "!=", 2, HL_OPERATOR_CMP }, 19 + { "<=", 2, HL_OPERATOR_CMP }, 20 + { ">=", 2, HL_OPERATOR_CMP }, 21 + { "&&", 2, HL_OPERATOR }, 22 + { "||", 2, HL_OPERATOR }, 23 + { "??", 2, HL_OPERATOR }, 24 + { "?.", 2, HL_OPTIONAL_CHAIN }, 25 + }; 26 + 27 + #define OP_COUNT (sizeof(operators) / sizeof(operators[0])) 28 + #define K(s, t) if (len == sizeof(s)-1 && !memcmp(word, s, sizeof(s)-1)) return t 29 + 30 + static hl_token_class lookup_extra_keyword(const char *word, size_t len) { 31 + switch (word[0]) { 32 + case 'a': K("abstract", HL_TYPE); break; 33 + case 'b': K("boolean", HL_TYPE_BOOLEAN); break; 34 + case 'd': K("declare", HL_TYPE); break; 35 + case 'e': K("enum", HL_TYPE); break; 36 + case 'g': K("global", HL_KEYWORD_ITALIC); break; 37 + case 'i': 38 + K("interface", HL_TYPE); 39 + K("implements", HL_TYPE); 40 + break; 41 + case 'n': 42 + K("namespace", HL_TYPE); 43 + K("never", HL_TYPE); 44 + break; 45 + case 'o': K("object", HL_TYPE); break; 46 + case 'p': 47 + K("package", HL_KEYWORD); 48 + K("private", HL_KEYWORD); 49 + K("protected", HL_KEYWORD); 50 + K("public", HL_KEYWORD); 51 + break; 52 + case 'r': K("readonly", HL_TYPE); break; 53 + case 's': 54 + K("string", HL_TYPE_STRING); 55 + K("symbol", HL_TYPE_STRING); 56 + break; 57 + case 't': K("type", HL_TYPE); break; 58 + case 'u': K("unknown", HL_TYPE); break; 59 + } return HL_NONE; 60 + } 61 + 62 + #undef K 63 + 64 + static hl_token_class tok_to_class(uint8_t tok) { 65 + static const void *dispatch[] = { 66 + [TOK_ASYNC] = &&l_kw_italic, 67 + [TOK_EXPORT] = &&l_kw_italic, 68 + [TOK_THIS] = &&l_kw_italic, 69 + [TOK_GLOBAL_THIS] = &&l_kw_italic, 70 + [TOK_WINDOW] = &&l_kw_italic, 71 + [TOK_DELETE] = &&l_kw_delete, 72 + [TOK_TYPEOF] = &&l_type, 73 + [TOK_INSTANCEOF] = &&l_type, 74 + [TOK_OF] = &&l_type, 75 + [TOK_IN] = &&l_type, 76 + [TOK_AS] = &&l_type, 77 + [TOK_TRUE] = &&l_bool, 78 + [TOK_FALSE] = &&l_bool, 79 + [TOK_NULL] = &&l_null, 80 + [TOK_UNDEF] = &&l_null, 81 + }; 82 + 83 + if (tok <= TOK_IDENTIFIER || tok >= TOK_IDENT_LIKE_END) return HL_NONE; 84 + if (tok < sizeof(dispatch) / sizeof(*dispatch) && dispatch[tok]) goto *dispatch[tok]; 85 + 86 + return HL_KEYWORD; 87 + 88 + l_kw_italic: return HL_KEYWORD_ITALIC; 89 + l_kw_delete: return HL_KEYWORD_DELETE; 90 + l_type: return HL_TYPE; 91 + l_bool: return HL_TYPE_BOOLEAN; 92 + l_null: return HL_LITERAL_NULL; 93 + } 94 + 95 + static inline bool hl_is_ident1(unsigned char c) { 96 + return 97 + (c >= 'a' && c <= 'z') 98 + || (c >= 'A' && c <= 'Z') 99 + || c == '_' 100 + || c == '$' 101 + || c >= 0x80; 102 + } 103 + 104 + // TODO: dry 105 + static inline bool hl_is_ident(unsigned char c) { 106 + return hl_is_ident1(c) || (c >= '0' && c <= '9'); 107 + } 108 + 109 + static inline bool hl_is_digit(unsigned char c) { 110 + return c >= '0' && c <= '9'; 111 + } 112 + 113 + static inline bool hl_is_xdigit(unsigned char c) { 114 + return hl_is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 115 + } 116 + 117 + static inline bool hl_is_bindigit(unsigned char c) { return c == '0' || c == '1'; } 118 + static inline bool hl_is_octdigit(unsigned char c) { return c >= '0' && c <= '7'; } 119 + 120 + void hl_iter_init(hl_iter *it, const char *input, size_t input_len, const highlight_state *state) { 121 + it->input = input; 122 + it->input_len = input_len; 123 + it->pos = 0; 124 + it->state = state ? *state : (highlight_state){ .mode = HL_STATE_NORMAL, .template_depth = 0 }; 125 + it->ctx = HL_CTX_NONE; 126 + } 127 + 128 + static hl_context keyword_sets_context(const char *word, size_t len) { 129 + if (len == 8 && memcmp(word, "function", 8) == 0) return HL_CTX_AFTER_FUNCTION; 130 + if (len == 5 && memcmp(word, "class", 5) == 0) return HL_CTX_AFTER_CLASS; 131 + if (len == 7 && memcmp(word, "extends", 7) == 0) return HL_CTX_AFTER_EXTENDS; 132 + return HL_CTX_NONE; 133 + } 134 + 135 + bool hl_iter_next(hl_iter *it, hl_span *out) { 136 + const char *input = it->input; 137 + size_t input_len = it->input_len; 138 + size_t i = it->pos; 139 + 140 + if (i >= input_len) return false; 141 + unsigned char c = (unsigned char)input[i]; 142 + 143 + if (it->state.mode == HL_STATE_BLOCK_COMMENT) { 144 + size_t start = i; 145 + while (i < input_len) { 146 + if (input[i] == '*' && i + 1 < input_len && input[i + 1] == '/') { 147 + i += 2; 148 + it->state.mode = HL_STATE_NORMAL; 149 + break; 150 + } 151 + i++; 152 + } 153 + *out = (hl_span){ start, i - start, HL_COMMENT }; 154 + it->pos = i; 155 + return true; 156 + } 157 + 158 + if (it->state.mode == HL_STATE_STRING_SINGLE || it->state.mode == HL_STATE_STRING_DOUBLE) { 159 + char quote = (it->state.mode == HL_STATE_STRING_SINGLE) ? '\'' : '"'; 160 + size_t start = i; 161 + while (i < input_len) { 162 + if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 163 + if (input[i] == quote) { 164 + i++; 165 + it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 166 + break; 167 + } 168 + i++; 169 + } 170 + *out = (hl_span){ start, i - start, HL_STRING }; 171 + it->pos = i; 172 + return true; 173 + } 174 + 175 + if (it->state.mode == HL_STATE_TEMPLATE) { 176 + size_t start = i; 177 + while (i < input_len) { 178 + if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 179 + if (input[i] == '$' && i + 1 < input_len && input[i + 1] == '{') { 180 + i += 2; 181 + it->state.mode = HL_STATE_TEMPLATE_EXPR; 182 + it->state.template_depth++; 183 + break; 184 + } 185 + if (input[i] == '`') { 186 + i++; 187 + it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 188 + break; 189 + } 190 + i++; 191 + } 192 + *out = (hl_span){ start, i - start, HL_STRING }; 193 + it->pos = i; 194 + return true; 195 + } 196 + 197 + if (it->state.mode == HL_STATE_TEMPLATE_EXPR && c == '}') { 198 + it->state.template_depth--; 199 + if (it->state.template_depth <= 0) { 200 + it->state.mode = HL_STATE_TEMPLATE; 201 + it->state.template_depth = 0; 202 + *out = (hl_span){ i, 1, HL_NONE }; 203 + it->pos = i + 1; 204 + return true; 205 + } 206 + } 207 + if (it->state.mode == HL_STATE_TEMPLATE_EXPR && c == '{') { 208 + it->state.template_depth++; 209 + *out = (hl_span){ i, 1, HL_NONE }; 210 + it->pos = i + 1; 211 + return true; 212 + } 213 + 214 + if (c == '/' && i + 1 < input_len && input[i + 1] == '/') { 215 + it->ctx = HL_CTX_NONE; 216 + *out = (hl_span){ i, input_len - i, HL_COMMENT }; 217 + it->pos = input_len; 218 + return true; 219 + } 220 + 221 + if (c == '/' && i + 1 < input_len && input[i + 1] == '*') { 222 + it->ctx = HL_CTX_NONE; 223 + size_t start = i; 224 + i += 2; 225 + while (i + 1 < input_len && !(input[i] == '*' && input[i + 1] == '/')) i++; 226 + if (i + 1 < input_len) { 227 + i += 2; 228 + } else { 229 + i = input_len; 230 + it->state.mode = HL_STATE_BLOCK_COMMENT; 231 + } 232 + *out = (hl_span){ start, i - start, HL_COMMENT }; 233 + it->pos = i; 234 + return true; 235 + } 236 + 237 + if (c == '\'' || c == '"') { 238 + it->ctx = HL_CTX_NONE; 239 + size_t start = i; 240 + it->state.mode = (c == '\'') ? HL_STATE_STRING_SINGLE : HL_STATE_STRING_DOUBLE; 241 + i++; 242 + while (i < input_len) { 243 + if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 244 + if ((unsigned char)input[i] == c) { 245 + i++; 246 + it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 247 + break; 248 + } 249 + i++; 250 + } 251 + *out = (hl_span){ start, i - start, HL_STRING }; 252 + it->pos = i; 253 + return true; 254 + } 255 + 256 + if (c == '`') { 257 + it->ctx = HL_CTX_NONE; 258 + it->state.mode = HL_STATE_TEMPLATE; 259 + *out = (hl_span){ i, 1, HL_STRING }; 260 + it->pos = i + 1; 261 + return true; 262 + } 263 + 264 + if (c == ';') { 265 + it->ctx = HL_CTX_NONE; 266 + *out = (hl_span){ i, 1, HL_SEMICOLON }; 267 + it->pos = i + 1; 268 + return true; 269 + } 270 + 271 + if (hl_is_digit(c) || (c == '.' && i + 1 < input_len && hl_is_digit((unsigned char)input[i + 1]))) { 272 + it->ctx = HL_CTX_NONE; 273 + size_t start = i; 274 + if (c == '0' && i + 1 < input_len) { 275 + unsigned char next = (unsigned char)input[i + 1]; 276 + if (next == 'x' || next == 'X') { 277 + i += 2; 278 + while (i < input_len && (hl_is_xdigit((unsigned char)input[i]) || input[i] == '_')) i++; 279 + goto num_done; 280 + } else if (next == 'b' || next == 'B') { 281 + i += 2; 282 + while (i < input_len && (hl_is_bindigit((unsigned char)input[i]) || input[i] == '_')) i++; 283 + goto num_done; 284 + } else if (next == 'o' || next == 'O') { 285 + i += 2; 286 + while (i < input_len && (hl_is_octdigit((unsigned char)input[i]) || input[i] == '_')) i++; 287 + goto num_done; 288 + } 289 + } 290 + while (i < input_len && (hl_is_digit((unsigned char)input[i]) || input[i] == '_')) i++; 291 + if (i < input_len && input[i] == '.') { 292 + i++; 293 + while (i < input_len && (hl_is_digit((unsigned char)input[i]) || input[i] == '_')) i++; 294 + } 295 + if (i < input_len && (input[i] == 'e' || input[i] == 'E')) { 296 + i++; 297 + if (i < input_len && (input[i] == '+' || input[i] == '-')) i++; 298 + while (i < input_len && (hl_is_digit((unsigned char)input[i]) || input[i] == '_')) i++; 299 + } 300 + num_done: 301 + if (i < input_len && input[i] == 'n') i++; 302 + *out = (hl_span){ start, i - start, HL_NUMBER }; 303 + it->pos = i; 304 + return true; 305 + } 306 + 307 + for (int k = 0; k < (int)OP_COUNT; k++) { 308 + int oplen = operators[k].len; 309 + if (i + (size_t)oplen <= input_len && 310 + memcmp(input + i, operators[k].op, (size_t)oplen) == 0) { 311 + it->ctx = HL_CTX_NONE; 312 + *out = (hl_span){ i, (size_t)oplen, operators[k].cls }; 313 + it->pos = i + (size_t)oplen; 314 + return true; 315 + } 316 + } 317 + 318 + if (hl_is_ident1(c)) { 319 + size_t start = i; 320 + i++; 321 + while (i < input_len && hl_is_ident((unsigned char)input[i])) i++; 322 + size_t word_len = i - start; 323 + const char *word = input + start; 324 + 325 + bool is_method = false; 326 + if (start > 0 && input[start - 1] == '.') { 327 + size_t peek = i; 328 + while (peek < input_len && input[peek] == ' ') peek++; 329 + if (peek < input_len && input[peek] == '(') is_method = true; 330 + } 331 + 332 + hl_token_class cls = HL_NONE; 333 + 334 + if (is_method) { 335 + cls = HL_FUNCTION; 336 + } else if (start > 0 && input[start - 1] == '.') { 337 + cls = HL_PROPERTY; 338 + } else if (it->ctx == HL_CTX_AFTER_FUNCTION) { 339 + cls = HL_FUNCTION_NAME; 340 + it->ctx = HL_CTX_NONE; 341 + } else if (it->ctx == HL_CTX_AFTER_CLASS) { 342 + cls = HL_CLASS_NAME; 343 + it->ctx = HL_CTX_NONE; 344 + } else if (it->ctx == HL_CTX_AFTER_EXTENDS) { 345 + cls = HL_PARENT_CLASS; 346 + it->ctx = HL_CTX_NONE; 347 + } else { 348 + cls = lookup_extra_keyword(word, word_len); 349 + 350 + if (cls == HL_NONE) { 351 + if ((word_len == 3 && memcmp(word, "NaN", 3) == 0) || 352 + (word_len == 8 && memcmp(word, "Infinity", 8) == 0)) { 353 + cls = HL_NUMBER; 354 + } 355 + else if (word_len == 7 && memcmp(word, "extends", 7) == 0) { 356 + cls = HL_KEYWORD_EXTENDS; 357 + } else { 358 + cls = tok_to_class(sv_parsekeyword(word, word_len)); 359 + } 360 + } 361 + 362 + if (cls == HL_NONE) { 363 + size_t peek = i; 364 + while (peek < input_len && input[peek] == ' ') peek++; 365 + if (peek < input_len && input[peek] == ':' && 366 + (peek + 1 >= input_len || input[peek + 1] != ':')) 367 + cls = HL_PROPERTY; 368 + } 369 + 370 + if (cls == HL_NONE && word[0] >= 'A' && word[0] <= 'Z') { 371 + cls = HL_TYPE; 372 + } 373 + 374 + hl_context next_ctx = keyword_sets_context(word, word_len); 375 + if (next_ctx != HL_CTX_NONE) it->ctx = next_ctx; 376 + } 377 + 378 + *out = (hl_span){ start, word_len, cls }; 379 + it->pos = i; 380 + return true; 381 + } 382 + 383 + if (c == '<' || c == '>' || c == '=') { 384 + it->ctx = HL_CTX_NONE; 385 + *out = (hl_span){ i, 1, HL_OPERATOR_CMP }; 386 + it->pos = i + 1; 387 + return true; 388 + } 389 + 390 + if (c == ' ' || c == '\t') { 391 + size_t start = i; 392 + while (i < input_len && (input[i] == ' ' || input[i] == '\t')) i++; 393 + *out = (hl_span){ start, i - start, HL_NONE }; 394 + it->pos = i; 395 + return true; 396 + } 397 + 398 + it->ctx = HL_CTX_NONE; 399 + *out = (hl_span){ i, 1, HL_NONE }; 400 + it->pos = i + 1; 401 + return true; 402 + } 403 + 404 + typedef struct { 405 + char *buf; 406 + size_t size; 407 + size_t pos; 408 + bool overflow; 409 + } outbuf_t; 410 + 411 + static inline void ob_putc(outbuf_t *o, char c) { 412 + if (o->pos + 1 < o->size) o->buf[o->pos++] = c; 413 + else o->overflow = true; 414 + } 415 + 416 + static inline void ob_write(outbuf_t *o, const char *s, size_t n) { 417 + if (o->pos + n < o->size) { 418 + memcpy(o->buf + o->pos, s, n); 419 + o->pos += n; 420 + } else o->overflow = true; 421 + } 422 + 423 + static inline void ob_puts(outbuf_t *o, const char *s) { 424 + ob_write(o, s, strlen(s)); 425 + } 426 + 427 + static inline void ob_put_escaped(outbuf_t *o, char c) { 428 + switch (c) { 429 + case '<': ob_write(o, "<<", 2); break; 430 + case '>': ob_write(o, ">>", 2); break; 431 + case '%': ob_write(o, "%%", 2); break; 432 + default: ob_putc(o, c); break; 433 + }} 434 + 435 + static inline void ob_write_escaped(outbuf_t *o, const char *s, size_t n) { 436 + for (size_t i = 0; i < n; i++) ob_put_escaped(o, s[i]); 437 + } 438 + 439 + static const char *class_to_crvar(hl_token_class cls) { 440 + switch (cls) { 441 + case HL_KEYWORD: return "blue"; 442 + case HL_KEYWORD_ITALIC: return "italic+blue"; 443 + case HL_KEYWORD_DELETE: return "red"; 444 + case HL_KEYWORD_EXTENDS: return "italic+cyan"; 445 + case HL_TYPE: return "cyan"; 446 + case HL_TYPE_STRING: return "green"; 447 + case HL_TYPE_BOOLEAN: return "magenta"; 448 + case HL_LITERAL_NULL: return "gray"; 449 + case HL_STRING: return "green"; 450 + case HL_NUMBER: return "yellow"; 451 + case HL_COMMENT: return "dim"; 452 + case HL_FUNCTION_NAME: return "green"; 453 + case HL_CLASS_NAME: return "bold+yellow"; 454 + case HL_PARENT_CLASS: return "bold+cyan"; 455 + case HL_FUNCTION: return "green"; 456 + case HL_PROPERTY: return "bright_magenta"; 457 + case HL_OPERATOR: return "bright_magenta"; 458 + case HL_OPERATOR_CMP: return "gray"; 459 + case HL_OPTIONAL_CHAIN: return "gray"; 460 + case HL_SEMICOLON: return "dim"; 461 + default: return NULL; 462 + }} 463 + 464 + int ant_highlight_stateful( 465 + const char *input, size_t input_len, 466 + char *out, size_t out_size, 467 + highlight_state *state 468 + ) { 469 + outbuf_t o = { .buf = out, .size = out_size, .pos = 0, .overflow = false }; 470 + 471 + hl_iter it; 472 + hl_iter_init(&it, input, input_len, state); 473 + 474 + hl_span span; 475 + while (hl_iter_next(&it, &span) && !o.overflow) { 476 + const char *var = class_to_crvar(span.cls); 477 + if (var) { 478 + ob_putc(&o, '<'); 479 + ob_puts(&o, var); 480 + ob_putc(&o, '>'); 481 + ob_write_escaped(&o, input + span.off, span.len); 482 + ob_write(&o, "</>", 3); 483 + } else { 484 + ob_write_escaped(&o, input + span.off, span.len); 485 + } 486 + } 487 + 488 + *state = hl_iter_state(&it); 489 + 490 + if (o.overflow) { 491 + o.pos = 0; 492 + o.overflow = false; 493 + ob_write_escaped(&o, input, input_len); 494 + if (o.overflow) { 495 + size_t safe = out_size > 1 ? out_size - 1 : 0; 496 + if (safe > input_len) safe = input_len; 497 + memcpy(out, input, safe); 498 + out[safe] = '\0'; 499 + return (int)safe; 500 + } 501 + } 502 + 503 + o.buf[o.pos] = '\0'; 504 + return (int)o.pos; 505 + } 506 + 507 + int ant_highlighl(const char *input, size_t input_len, char *out, size_t out_size) { 508 + highlight_state state = { .mode = HL_STATE_NORMAL, .template_depth = 0 }; 509 + return ant_highlight_stateful(input, input_len, out, out_size, &state); 510 + } 511 + 512 + int highlight_js_line_clipped( 513 + const char *line, size_t line_len, 514 + size_t max_cols, 515 + char *out, size_t out_size, 516 + highlight_state *state 517 + ) { 518 + outbuf_t o = { .buf = out, .size = out_size, .pos = 0, .overflow = false }; 519 + 520 + hl_iter it; 521 + hl_iter_init(&it, line, line_len, state); 522 + 523 + size_t vis_cols = 0; 524 + hl_span span; 525 + 526 + while (hl_iter_next(&it, &span)) { 527 + if (vis_cols >= max_cols) continue; 528 + 529 + size_t span_remaining = max_cols - vis_cols; 530 + size_t emit_len = span.len < span_remaining ? span.len : span_remaining; 531 + 532 + if (!o.overflow) { 533 + const char *var = class_to_crvar(span.cls); 534 + if (var) { 535 + ob_putc(&o, '<'); 536 + ob_puts(&o, var); 537 + ob_putc(&o, '>'); 538 + ob_write_escaped(&o, line + span.off, emit_len); 539 + ob_write(&o, "</>", 3); 540 + } else { 541 + ob_write_escaped(&o, line + span.off, emit_len); 542 + } 543 + } 544 + 545 + vis_cols += span.len; 546 + } 547 + 548 + *state = hl_iter_state(&it); 549 + 550 + if (o.overflow) { 551 + o.pos = 0; 552 + size_t emit = line_len < max_cols ? line_len : max_cols; 553 + ob_write_escaped(&o, line, emit); 554 + if (o.overflow) { 555 + size_t safe = out_size > 1 ? out_size - 1 : 0; 556 + if (safe > emit) safe = emit; 557 + memcpy(out, line, safe); 558 + out[safe] = '\0'; 559 + return (int)safe; 560 + } 561 + } 562 + 563 + o.buf[o.pos] = '\0'; 564 + return (int)o.pos; 565 + } 566 + 567 +
+2 -8
src/main.c
··· 6 6 #include <string.h> 7 7 #include <unistd.h> 8 8 #include <sys/stat.h> 9 - #include <sys/resource.h> 10 9 #include <argtable3.h> 11 10 #include <crprintf.h> 12 11 ··· 564 563 } 565 564 566 565 js_setstackbase(js, (void *)&stack_base); 567 - { 568 - struct rlimit rl; 569 - if (getrlimit(RLIMIT_STACK, &rl) == 0 && rl.rlim_cur != RLIM_INFINITY) 570 - js_setstacklimit(js, (size_t)rl.rlim_cur * 3 / 4); 571 - else 572 - js_setstacklimit(js, 512 * 1024); 573 - } 566 + js_setstacklimit(js, os_thread_stack_size() * 3 / 4); 567 + 574 568 proc_argv = build_process_argv(argc, argv, module_file, script_tail); 575 569 ant_runtime_init(js, proc_argv.argc, proc_argv.argv, localstorage_file); 576 570
+115 -55
src/repl.c
··· 28 28 #include "silver/engine.h" 29 29 #include "modules/io.h" 30 30 31 - #define MAX_HISTORY 512 32 - #define MAX_LINE_LENGTH 4096 31 + #include <crprintf.h> 32 + #include "highlight.h" 33 + 34 + #define MAX_HISTORY 512 35 + #define MAX_LINE_LENGTH 4096 33 36 #define MAX_MULTILINE_LENGTH 65536 34 - #define INPUT char *line, int *pos, int *len, key_event_t *key, history_t *hist, const char *prompt 37 + 38 + #define INPUT \ 39 + char *line, int *pos, int *len, key_event_t *key, history_t *hist, const char *prompt 35 40 36 41 static volatile sig_atomic_t ctrl_c_pressed = 0; 37 42 ··· 527 532 typedef struct { key_type_t type; int ch; } key_event_t; 528 533 typedef void (*key_handler_t)(INPUT); 529 534 535 + static crprintf_compiled *hl_prog = NULL; 536 + static highlight_state hl_line_state = { .mode = HL_STATE_NORMAL, .template_depth = 0 }; 537 + 538 + static void refresh_line(const char *line, int len, int pos, const char *prompt) { 539 + fputs("\r\033[2K", stdout); 540 + fputs(prompt, stdout); 541 + 542 + if (crprintf_get_color() && len > 0 && len <= 2048) { 543 + char tagged[8192]; 544 + char rendered[8192]; 545 + 546 + highlight_state state = hl_line_state; 547 + ant_highlight_stateful(line, (size_t)len, tagged, sizeof(tagged), &state); 548 + 549 + hl_prog = crprintf_recompile(hl_prog, tagged); 550 + crprintf_state *rs = crprintf_state_new(); 551 + 552 + crsprintf_compiled(rendered, sizeof(rendered), rs, hl_prog); 553 + crprintf_state_free(rs); 554 + 555 + fputs(rendered, stdout); 556 + } else if (len > 0) fwrite(line, 1, (size_t)len, stdout); 557 + 558 + if (pos < len) { 559 + size_t prompt_len = strlen(prompt); 560 + printf("\r\033[%zuC", prompt_len + (size_t)pos); 561 + } 562 + 563 + fflush(stdout); 564 + } 565 + 530 566 static void cursor_move(int *pos, int len, int dir) { 531 567 if (dir < 0 && *pos > 0) { printf("\033[D"); fflush(stdout); (*pos)--; } 532 568 else if (dir > 0 && *pos < len) { printf("\033[C"); fflush(stdout); (*pos)++; } 533 569 } 534 570 535 571 static void line_set(char *line, int *pos, int *len, const char *str, const char *prompt) { 536 - printf("\r\033[K%s%s", prompt, str); 537 - fflush(stdout); strcpy(line, str); 572 + strcpy(line, str); 538 573 *len = (int)strlen(line); *pos = *len; 574 + refresh_line(line, *len, *pos, prompt); 539 575 } 540 576 541 - static void line_backspace(char *line, int *pos, int *len) { 577 + static void line_backspace(char *line, int *pos, int *len, const char *prompt) { 542 578 if (*pos <= 0) return; 543 579 544 580 memmove(line + *pos - 1, line + *pos, *len - *pos + 1); 545 581 (*pos)--; (*len)--; 546 - printf("\b\033[K%s", line + *pos); 547 - for (int i = 0; i < *len - *pos; i++) printf("\033[D"); 548 - fflush(stdout); 582 + refresh_line(line, *len, *pos, prompt); 549 583 } 550 584 551 - static void line_insert(char *line, int *pos, int *len, int c) { 585 + static void line_insert(char *line, int *pos, int *len, int c, const char *prompt) { 552 586 if (*len >= MAX_LINE_LENGTH - 1) return; 553 587 554 588 memmove(line + *pos + 1, line + *pos, *len - *pos + 1); 555 589 line[*pos] = (char)c; 556 590 (*pos)++; (*len)++; 557 - printf("%c%s", c, line + *pos); 558 - for (int i = 0; i < *len - *pos; i++) printf("\033[D"); 559 - fflush(stdout); 591 + refresh_line(line, *len, *pos, prompt); 560 592 } 561 593 562 594 #ifdef _WIN32 ··· 580 612 if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c }; 581 613 return (key_event_t){ KEY_NONE, 0 }; 582 614 } 583 - #define TERM_INIT() 584 - #define TERM_RESTORE() 585 615 #else 586 616 static struct termios saved_tio; 587 617 static key_event_t read_key(void) { ··· 590 620 if (c == EOF && !feof(stdin)) { clearerr(stdin); return (key_event_t){ KEY_EOF, 0 }; } 591 621 if (c == EOF) return (key_event_t){ KEY_EOF, 0 }; 592 622 if (c == 27) { 593 - if (getchar() == '[') { 594 - switch (getchar()) { 595 - case 'A': return (key_event_t){ KEY_UP, 0 }; 596 - case 'B': return (key_event_t){ KEY_DOWN, 0 }; 597 - case 'C': return (key_event_t){ KEY_RIGHT, 0 }; 598 - case 'D': return (key_event_t){ KEY_LEFT, 0 }; 599 - } 623 + int seq1 = getchar(); 624 + if (seq1 == EOF) return (key_event_t){ KEY_NONE, 0 }; 625 + if (seq1 != '[') return (key_event_t){ KEY_NONE, 0 }; 626 + int seq2 = getchar(); 627 + if (seq2 == EOF) return (key_event_t){ KEY_NONE, 0 }; 628 + switch (seq2) { 629 + case 'A': return (key_event_t){ KEY_UP, 0 }; 630 + case 'B': return (key_event_t){ KEY_DOWN, 0 }; 631 + case 'C': return (key_event_t){ KEY_RIGHT, 0 }; 632 + case 'D': return (key_event_t){ KEY_LEFT, 0 }; 633 + } 634 + if (seq2 >= '0' && seq2 <= '9') { 635 + int seq3 = getchar(); 636 + (void)seq3; 600 637 } 601 638 return (key_event_t){ KEY_NONE, 0 }; 602 639 } ··· 605 642 if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c }; 606 643 return (key_event_t){ KEY_NONE, 0 }; 607 644 } 608 - #define TERM_INIT() do { \ 609 - struct termios new_tio; \ 610 - tcgetattr(STDIN_FILENO, &saved_tio); \ 611 - new_tio = saved_tio; \ 612 - new_tio.c_lflag &= ~(ICANON | ECHO); \ 613 - tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); \ 614 - } while(0) 615 - #define TERM_RESTORE() tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio) 616 645 #endif 617 646 618 647 static void handle_up(INPUT) { ··· 627 656 628 657 static void handle_left(INPUT) { cursor_move(pos, *len, -1); } 629 658 static void handle_right(INPUT) { cursor_move(pos, *len, 1); } 630 - static void handle_backspace(INPUT) { line_backspace(line, pos, len); } 631 - static void handle_char(INPUT) { line_insert(line, pos, len, key->ch); } 659 + 660 + static void handle_backspace(INPUT) { line_backspace(line, pos, len, prompt); } 661 + static void handle_char(INPUT) { line_insert(line, pos, len, key->ch, prompt); } 632 662 633 663 static key_handler_t handlers[] = { 634 - [KEY_UP] = handle_up, [KEY_DOWN] = handle_down, 635 - [KEY_LEFT] = handle_left, [KEY_RIGHT] = handle_right, 636 - [KEY_BACKSPACE] = handle_backspace, [KEY_CHAR] = handle_char, 664 + [KEY_UP] = handle_up, 665 + [KEY_DOWN] = handle_down, 666 + [KEY_LEFT] = handle_left, 667 + [KEY_RIGHT] = handle_right, 668 + [KEY_BACKSPACE] = handle_backspace, 669 + [KEY_CHAR] = handle_char, 637 670 }; 638 671 639 - static char* read_line_with_history(history_t *hist, ant_t *js, const char *prompt) { 672 + static inline void term_restore(void) { 673 + #ifndef _WIN32 674 + tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio); 675 + #endif 676 + } 677 + 678 + static char *read_line_with_history(history_t *hist, ant_t *js, const char *prompt) { 640 679 char *line = malloc(MAX_LINE_LENGTH); 641 680 int pos = 0, len = 0; line[0] = '\0'; 642 681 643 - TERM_INIT(); 682 + #ifndef _WIN32 683 + struct termios new_tio; 684 + tcgetattr(STDIN_FILENO, &saved_tio); 685 + new_tio = saved_tio; 686 + new_tio.c_lflag &= ~(ICANON | ECHO); 687 + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); 688 + #endif 689 + 690 + again: 691 + key_event_t key = read_key(); 692 + 693 + switch (key.type) { 694 + case KEY_ENTER: 695 + putchar('\n'); 696 + term_restore(); 697 + return line; 698 + 699 + case KEY_EOF: 700 + putchar('\n'); 701 + term_restore(); 702 + free(line); 703 + return NULL; 644 704 645 - do { 646 - key_event_t key = read_key(); 647 - 648 - if (key.type == KEY_ENTER) { 649 - printf("\n"); fflush(stdout); 650 - TERM_RESTORE(); return line; 651 - } 652 - 653 - if (key.type == KEY_EOF) { 654 - printf("\n"); fflush(stdout); 655 - TERM_RESTORE(); 656 - free(line); return NULL; 657 - } 658 - 659 - if (handlers[key.type]) { 660 - handlers[key.type](line, &pos, &len, &key, hist, prompt); 661 - } 662 - } while (1); 705 + default: 706 + if (handlers[key.type]) handlers[key.type]( 707 + line, &pos, &len, &key, hist, prompt 708 + ); 709 + break; 710 + } 711 + 712 + goto again; 663 713 } 664 714 665 715 typedef struct { ··· 758 808 759 809 while (1) { 760 810 const char *prompt = multiline_buf ? "| " : "> "; 811 + 812 + if (multiline_buf && multiline_len > 0) { 813 + char scratch[8192]; 814 + // TODO: dry 815 + hl_line_state = (highlight_state){ .mode = HL_STATE_NORMAL, .template_depth = 0 }; 816 + ant_highlight_stateful(multiline_buf, multiline_len, scratch, sizeof(scratch), &hl_line_state); 817 + } else hl_line_state = (highlight_state){ .mode = HL_STATE_NORMAL, .template_depth = 0 }; 818 + 761 819 fputs(prompt, stdout); 762 820 fflush(stdout); 763 821 ··· 857 915 } 858 916 859 917 if (multiline_buf) free(multiline_buf); 918 + if (hl_prog) { crprintf_compiled_free(hl_prog); hl_prog = NULL; } 919 + 860 920 repl_decl_registry_free(&decl_registry); 861 921 g_repl_decl_registry = NULL; 862 922
+5 -4
src/silver/lexer.c
··· 235 235 return false; 236 236 } 237 237 238 - static uint8_t parsekeyword(const char *buf, size_t len) { 238 + uint8_t sv_parsekeyword(const char *buf, size_t len) { 239 239 switch (buf[0]) { 240 240 case 'a': 241 241 K("as", TOK_AS); ··· 512 512 i++; 513 513 } 514 514 *tlen = i; 515 - return parsekeyword(buf, i); 515 + return sv_parsekeyword(buf, i); 516 516 } 517 517 518 518 if (c == '\\') { ··· 566 566 if (has_escapes) { 567 567 char decoded[256]; 568 568 size_t decoded_len = decode_ident_escapes(buf, *tlen, decoded, sizeof(decoded)); 569 - uint8_t kw = parsekeyword(decoded, decoded_len); 569 + uint8_t kw = sv_parsekeyword(decoded, decoded_len); 570 570 if (kw != TOK_IDENTIFIER) return TOK_ERR; 571 571 return TOK_IDENTIFIER; 572 572 } 573 573 574 - return parsekeyword(buf, *tlen); 574 + return sv_parsekeyword(buf, *tlen); 575 575 } 576 576 577 577 static inline jsoff_t parse_decimal(const char *buf, jsoff_t maxlen, double *out) { ··· 1158 1158 1159 1159 return tok; 1160 1160 } 1161 +
+4 -2
src/silver/limits.c
··· 17 17 18 18 int sv_user_stack_size_kb = 0; 19 19 20 - static size_t os_thread_stack_size(void) { 20 + size_t os_thread_stack_size(void) { 21 21 #ifdef _WIN32 22 - return 1024 * 1024; 22 + ULONG_PTR low, high; 23 + GetCurrentThreadStackLimits(&low, &high); 24 + return (size_t)(high - low); 23 25 #else 24 26 struct rlimit rl; 25 27 if (getrlimit(RLIMIT_STACK, &rl) == 0 && rl.rlim_cur != RLIM_INFINITY)