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.

labeled loops

+373 -20
+1 -1
meson.build
··· 75 75 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 76 76 77 77 version_conf = configuration_data() 78 - version_conf.set('ANT_VERSION', '0.1.3.12') 78 + version_conf.set('ANT_VERSION', '0.1.3.13') 79 79 version_conf.set('ANT_GIT_HASH', git_hash) 80 80 version_conf.set('ANT_BUILD_DATE', build_date) 81 81
+372 -19
src/ant.c
··· 122 122 static call_stack_t global_call_stack = {NULL, 0, 0}; 123 123 static coroutine_queue_t pending_coroutines = {NULL, NULL}; 124 124 125 + typedef struct { 126 + const char *name; 127 + jsoff_t name_len; 128 + bool is_loop; 129 + bool is_block; 130 + } label_entry_t; 131 + 132 + static const UT_icd label_entry_icd = { 133 + .sz = sizeof(label_entry_t), 134 + .init = NULL, 135 + .copy = NULL, 136 + .dtor = NULL, 137 + }; 138 + 139 + static UT_array *label_stack = NULL; 140 + 141 + static const char *break_target_label = NULL; 142 + static jsoff_t break_target_label_len = 0; 143 + static const char *continue_target_label = NULL; 144 + static jsoff_t continue_target_label_len = 0; 145 + 146 + #define F_BREAK_LABEL 1U 147 + #define F_CONTINUE_LABEL 2U 148 + static uint8_t label_flags = 0; 149 + 125 150 typedef struct esm_module { 126 151 char *path; 127 152 char *resolved_path; ··· 406 431 static size_t tostr(struct js *js, jsval_t value, char *buf, size_t len); 407 432 static size_t strpromise(struct js *js, jsval_t value, char *buf, size_t len); 408 433 static size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size); 434 + static jsval_t js_stmt_impl(struct js *js); 435 + static bool continue_targets_innermost_loop(void); 436 + static bool is_continue_target(const char *name, jsoff_t len); 437 + static bool is_this_loop_continue_target(int depth_at_entry); 438 + static void clear_continue_label(void); 439 + static void clear_break_label(void); 409 440 410 441 static inline jsoff_t esize(jsoff_t w); 411 442 static jsval_t js_expr(struct js *js); ··· 8430 8461 jsval_t v = for_iter_exec_body(js, ctx); 8431 8462 if (is_err(v)) return v; 8432 8463 if (js->flags & F_BREAK) break; 8464 + if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 8433 8465 if (js->flags & F_RETURN) return v; 8434 8466 } 8435 8467 ··· 8479 8511 jsval_t v = for_iter_exec_body(js, ctx); 8480 8512 if (is_err(v)) return v; 8481 8513 if (js->flags & F_BREAK) break; 8514 + if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 8482 8515 if (js->flags & F_RETURN) return v; 8483 8516 } 8484 8517 ··· 8498 8531 jsval_t v = for_iter_exec_body(js, ctx); 8499 8532 if (is_err(v)) return v; 8500 8533 if (js->flags & F_BREAK) break; 8534 + if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 8501 8535 if (js->flags & F_RETURN) return v; 8502 8536 } 8503 8537 ··· 8551 8585 jsval_t v = for_iter_exec_body(js, ctx); 8552 8586 if (is_err(v)) return v; 8553 8587 if (js->flags & F_BREAK) break; 8588 + if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 8554 8589 if (js->flags & F_RETURN) return v; 8555 8590 } 8556 8591 ··· 8561 8596 uint8_t flags = js->flags, exe = !(flags & F_NOEXEC); 8562 8597 jsval_t v, res = js_mkundef(); 8563 8598 jsoff_t pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; 8599 + 8600 + if (!label_stack) { 8601 + utarray_new(label_stack, &label_entry_icd); 8602 + } 8603 + label_entry_t marker = { .name = NULL, .name_len = 0, .is_loop = true, .is_block = false }; 8604 + utarray_push_back(label_stack, &marker); 8605 + int marker_index = utarray_len(label_stack) - 1; 8606 + 8564 8607 if (exe) mkscope(js); 8565 8608 if (!expect(js, TOK_FOR, &res)) goto done; 8566 8609 if (!expect(js, TOK_LPAREN, &res)) goto done; ··· 8713 8756 goto done; 8714 8757 } 8715 8758 8759 + if (label_flags & F_CONTINUE_LABEL) { 8760 + if (is_this_loop_continue_target(marker_index)) { 8761 + clear_continue_label(); 8762 + js->flags &= ~(F_BREAK | F_NOEXEC); 8763 + } else { 8764 + js->flags |= F_BREAK; 8765 + break; 8766 + } 8767 + } 8768 + 8716 8769 if (js->flags & F_BREAK) break; 8770 + 8717 8771 if (js->flags & F_RETURN) { 8718 8772 res = v; 8719 8773 goto done; ··· 8819 8873 } 8820 8874 } 8821 8875 8822 - if (js->flags & F_BREAK) break; 8876 + if (label_flags & F_CONTINUE_LABEL) { 8877 + if (is_this_loop_continue_target(marker_index)) { 8878 + clear_continue_label(); 8879 + js->flags &= ~(F_BREAK | F_NOEXEC); 8880 + js->flags = flags; 8881 + js->pos = pos2, js->consumed = 1; 8882 + if (next(js) != TOK_RPAREN) { 8883 + v = js_expr(js); 8884 + if (is_err2(&v, &res)) goto done; 8885 + } 8886 + continue; 8887 + } 8888 + } 8889 + 8890 + if (js->flags & F_BREAK) { 8891 + break; 8892 + } 8893 + 8823 8894 if (js->flags & F_RETURN) { 8824 8895 res = v; 8825 8896 break; ··· 8832 8903 } 8833 8904 js->pos = pos4, js->tok = TOK_SEMICOLON, js->consumed = 0; 8834 8905 done: 8906 + if (label_stack && utarray_len(label_stack) > 0) { 8907 + utarray_pop_back(label_stack); 8908 + } 8909 + 8835 8910 if (exe) delscope(js); 8836 8911 uint8_t preserve = 0; 8837 8912 if (js->flags & F_RETURN) { 8838 8913 preserve = js->flags & (F_RETURN | F_NOEXEC); 8839 8914 } 8915 + if ((js->flags & F_BREAK) && (label_flags & F_BREAK_LABEL)) { 8916 + preserve |= (js->flags & (F_BREAK | F_NOEXEC)); 8917 + } 8918 + if (label_flags & F_CONTINUE_LABEL) { 8919 + preserve |= F_BREAK | F_NOEXEC; 8920 + } 8840 8921 js->flags = flags | preserve; 8841 8922 return res; 8842 8923 } ··· 8845 8926 uint8_t flags = js->flags, exe = !(flags & F_NOEXEC); 8846 8927 jsval_t res = js_mkundef(), v; 8847 8928 8929 + if (!label_stack) { 8930 + utarray_new(label_stack, &label_entry_icd); 8931 + } 8932 + label_entry_t marker = { .name = NULL, .name_len = 0, .is_loop = true, .is_block = false }; 8933 + utarray_push_back(label_stack, &marker); 8934 + int marker_index = utarray_len(label_stack) - 1; 8935 + 8848 8936 js->consumed = 1; 8849 - if (!expect(js, TOK_LPAREN, &res)) return res; 8937 + if (!expect(js, TOK_LPAREN, &res)) goto done; 8850 8938 8851 8939 jsoff_t cond_start = js->pos; 8852 8940 js->flags |= F_NOEXEC; 8853 8941 v = js_expr(js); 8854 - if (is_err(v)) return v; 8942 + if (is_err(v)) { res = v; goto done; } 8855 8943 8856 - if (!expect(js, TOK_RPAREN, &res)) return res; 8944 + if (!expect(js, TOK_RPAREN, &res)) goto done; 8857 8945 8858 8946 jsoff_t body_start = js->pos; 8859 8947 v = js_block_or_stmt(js); 8860 - if (is_err(v)) return v; 8948 + if (is_err(v)) { res = v; goto done; } 8861 8949 jsoff_t body_end = js->pos; 8862 8950 8863 8951 if (exe) { ··· 8884 8972 break; 8885 8973 } 8886 8974 8975 + if (label_flags & F_CONTINUE_LABEL) { 8976 + if (is_this_loop_continue_target(marker_index)) { 8977 + clear_continue_label(); 8978 + js->flags &= ~(F_BREAK | F_NOEXEC); 8979 + continue; 8980 + } 8981 + } 8982 + 8887 8983 if (js->flags & F_BREAK) { 8888 8984 break; 8889 8985 } ··· 8899 8995 js->tok = TOK_SEMICOLON; 8900 8996 js->consumed = 0; 8901 8997 8998 + done: 8999 + if (label_stack && utarray_len(label_stack) > 0) { 9000 + utarray_pop_back(label_stack); 9001 + } 9002 + 8902 9003 uint8_t preserve = 0; 8903 9004 if (js->flags & F_RETURN) { 8904 9005 preserve = js->flags & (F_RETURN | F_NOEXEC); 8905 9006 } 9007 + if ((js->flags & F_BREAK) && (label_flags & F_BREAK_LABEL)) { 9008 + preserve |= (js->flags & (F_BREAK | F_NOEXEC)); 9009 + } 9010 + if (label_flags & F_CONTINUE_LABEL) { 9011 + preserve |= F_BREAK | F_NOEXEC; 9012 + } 8906 9013 js->flags = flags | preserve; 8907 9014 8908 9015 return res; ··· 8912 9019 uint8_t flags = js->flags, exe = !(flags & F_NOEXEC); 8913 9020 jsval_t res = js_mkundef(), v; 8914 9021 9022 + if (!label_stack) { 9023 + utarray_new(label_stack, &label_entry_icd); 9024 + } 9025 + label_entry_t marker = { .name = NULL, .name_len = 0, .is_loop = true, .is_block = false }; 9026 + utarray_push_back(label_stack, &marker); 9027 + int marker_index = utarray_len(label_stack) - 1; 9028 + 8915 9029 js->consumed = 1; 8916 9030 8917 9031 jsoff_t body_start = js->pos; 8918 9032 bool is_block = (next(js) == TOK_LBRACE); 8919 9033 js->flags |= F_NOEXEC; 8920 9034 v = js_block_or_stmt(js); 8921 - if (is_err(v)) return v; 9035 + if (is_err(v)) { res = v; goto done; } 8922 9036 8923 9037 if (is_block && next(js) == TOK_RBRACE) { 8924 9038 js->consumed = 1; 8925 9039 } 8926 9040 (void) js->pos; 8927 9041 8928 - if (!expect(js, TOK_WHILE, &res)) return res; 8929 - if (!expect(js, TOK_LPAREN, &res)) return res; 9042 + if (!expect(js, TOK_WHILE, &res)) goto done; 9043 + if (!expect(js, TOK_LPAREN, &res)) goto done; 8930 9044 8931 9045 jsoff_t cond_start = js->pos; 8932 9046 v = js_expr(js); 8933 - if (is_err(v)) return v; 9047 + if (is_err(v)) { res = v; goto done; } 8934 9048 8935 - if (!expect(js, TOK_RPAREN, &res)) return res; 9049 + if (!expect(js, TOK_RPAREN, &res)) goto done; 8936 9050 jsoff_t cond_end = js->pos; 8937 9051 8938 9052 if (exe) { ··· 8947 9061 break; 8948 9062 } 8949 9063 9064 + if (label_flags & F_CONTINUE_LABEL) { 9065 + if (is_this_loop_continue_target(marker_index)) { 9066 + clear_continue_label(); 9067 + js->flags &= ~(F_BREAK | F_NOEXEC); 9068 + } else { break; } 9069 + } 9070 + 8950 9071 if (js->flags & F_BREAK) { 8951 9072 break; 8952 9073 } ··· 8971 9092 js->pos = cond_end; 8972 9093 js->consumed = 1; 8973 9094 9095 + done: 9096 + if (label_stack && utarray_len(label_stack) > 0) { 9097 + utarray_pop_back(label_stack); 9098 + } 9099 + 8974 9100 uint8_t preserve = 0; 8975 9101 if (js->flags & F_RETURN) { 8976 9102 preserve = js->flags & (F_RETURN | F_NOEXEC); 9103 + } 9104 + if ((js->flags & F_BREAK) && (label_flags & F_BREAK_LABEL)) { 9105 + preserve |= (js->flags & (F_BREAK | F_NOEXEC)); 9106 + } 9107 + if (label_flags & F_CONTINUE_LABEL) { 9108 + preserve |= F_BREAK | F_NOEXEC; 8977 9109 } 8978 9110 js->flags = flags | preserve; 8979 9111 ··· 9211 9343 return res; 9212 9344 } 9213 9345 9346 + static bool label_exists(const char *name, jsoff_t len, bool check_loop) { 9347 + if (!label_stack) return false; 9348 + int depth = utarray_len(label_stack); 9349 + for (int i = depth - 1; i >= 0; i--) { 9350 + label_entry_t *entry = (label_entry_t *)utarray_eltptr(label_stack, i); 9351 + if (entry && entry->name_len == len && 9352 + memcmp(entry->name, name, len) == 0) { 9353 + if (check_loop && !entry->is_loop) { 9354 + return false; 9355 + } 9356 + return true; 9357 + } 9358 + } 9359 + return false; 9360 + } 9361 + 9362 + static bool continue_targets_innermost_loop(void) { 9363 + if (!(label_flags & F_CONTINUE_LABEL)) return false; 9364 + if (!label_stack || !continue_target_label) return false; 9365 + 9366 + int depth = utarray_len(label_stack); 9367 + for (int i = depth - 1; i >= 0; i--) { 9368 + label_entry_t *entry = (label_entry_t *)utarray_eltptr(label_stack, i); 9369 + if (entry && entry->is_loop) { 9370 + if (entry->name_len == continue_target_label_len && 9371 + memcmp(entry->name, continue_target_label, continue_target_label_len) == 0) { 9372 + return true; 9373 + } 9374 + return false; 9375 + } 9376 + } 9377 + return false; 9378 + } 9379 + 9380 + static bool is_this_loop_continue_target(int marker_index) { 9381 + if (!(label_flags & F_CONTINUE_LABEL)) return false; 9382 + if (!label_stack || !continue_target_label) return false; 9383 + if (marker_index <= 0) return false; 9384 + 9385 + label_entry_t *entry = (label_entry_t *)utarray_eltptr(label_stack, marker_index - 1); 9386 + if (!entry) return false; 9387 + if (entry->name == NULL) return false; 9388 + if (!entry->is_loop) return false; 9389 + 9390 + if (entry->name_len == continue_target_label_len && 9391 + memcmp(entry->name, continue_target_label, continue_target_label_len) == 0) { 9392 + return true; 9393 + } 9394 + return false; 9395 + } 9396 + 9214 9397 static jsval_t js_break(struct js *js) { 9215 - if (js->flags & F_NOEXEC) { 9216 - } else { 9217 - if (!(js->flags & (F_LOOP | F_SWITCH))) return js_mkerr(js, "not in loop or switch"); 9398 + js->consumed = 1; 9399 + 9400 + uint8_t nxt = next(js); 9401 + if (nxt == TOK_IDENTIFIER && !js->had_newline) { 9402 + const char *label = &js->code[js->toff]; 9403 + jsoff_t label_len = js->tlen; 9404 + js->consumed = 1; 9405 + 9406 + if (js->flags & F_NOEXEC) { 9407 + return js_mkundef(); 9408 + } 9409 + 9410 + if (!label_exists(label, label_len, false)) { 9411 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "undefined label '%.*s'", (int)label_len, label); 9412 + } 9413 + 9414 + break_target_label = label; 9415 + break_target_label_len = label_len; 9416 + label_flags |= F_BREAK_LABEL; 9218 9417 js->flags |= F_BREAK | F_NOEXEC; 9418 + return js_mkundef(); 9419 + } 9420 + 9421 + if (js->flags & F_NOEXEC) { 9422 + return js_mkundef(); 9219 9423 } 9220 - js->consumed = 1; 9424 + 9425 + if (!(js->flags & (F_LOOP | F_SWITCH))) { 9426 + bool in_labeled_block = false; 9427 + if (label_stack) { 9428 + int depth = utarray_len(label_stack); 9429 + for (int i = depth - 1; i >= 0; i--) { 9430 + label_entry_t *entry = (label_entry_t *)utarray_eltptr(label_stack, i); 9431 + if (entry && entry->is_block) { 9432 + in_labeled_block = true; 9433 + break; 9434 + } 9435 + } 9436 + } 9437 + if (!in_labeled_block) { 9438 + return js_mkerr(js, "not in loop or switch"); 9439 + } 9440 + } 9441 + 9442 + js->flags |= F_BREAK | F_NOEXEC; 9221 9443 return js_mkundef(); 9222 9444 } 9223 9445 9224 9446 static jsval_t js_continue(struct js *js) { 9447 + js->consumed = 1; 9448 + 9449 + uint8_t nxt = next(js); 9450 + if (nxt == TOK_IDENTIFIER && !js->had_newline) { 9451 + const char *label = &js->code[js->toff]; 9452 + jsoff_t label_len = js->tlen; 9453 + js->consumed = 1; 9454 + 9455 + if (js->flags & F_NOEXEC) { 9456 + return js_mkundef(); 9457 + } 9458 + 9459 + if (!label_exists(label, label_len, true)) { 9460 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "undefined label '%.*s' or not a loop", (int)label_len, label); 9461 + } 9462 + 9463 + continue_target_label = label; 9464 + continue_target_label_len = label_len; 9465 + label_flags |= F_CONTINUE_LABEL; 9466 + js->flags |= F_BREAK | F_NOEXEC; 9467 + return js_mkundef(); 9468 + } 9469 + 9225 9470 if (js->flags & F_NOEXEC) { 9226 - } else { 9227 - if (!(js->flags & F_LOOP)) return js_mkerr(js, "not in loop"); 9228 - js->flags |= F_NOEXEC; 9471 + return js_mkundef(); 9472 + } 9473 + 9474 + if (!(js->flags & F_LOOP)) { 9475 + return js_mkerr(js, "not in loop"); 9229 9476 } 9230 - js->consumed = 1; 9477 + 9478 + js->flags |= F_NOEXEC; 9231 9479 return js_mkundef(); 9232 9480 } 9233 9481 ··· 10012 10260 *res = js_mkerr_typed(js, JS_ERR_SYNTAX, "async must be followed by function"); 10013 10261 } 10014 10262 10015 - static jsval_t js_stmt(struct js *js) { 10263 + static jsval_t js_stmt(struct js *js); 10264 + 10265 + static bool is_break_target(const char *name, jsoff_t len) { 10266 + if (!(label_flags & F_BREAK_LABEL)) return false; 10267 + if (break_target_label_len != len) return false; 10268 + return memcmp(break_target_label, name, len) == 0; 10269 + } 10270 + 10271 + static bool is_continue_target(const char *name, jsoff_t len) { 10272 + if (!(label_flags & F_CONTINUE_LABEL)) return false; 10273 + if (continue_target_label_len != len) return false; 10274 + return memcmp(continue_target_label, name, len) == 0; 10275 + } 10276 + 10277 + static void clear_break_label(void) { 10278 + break_target_label = NULL; 10279 + break_target_label_len = 0; 10280 + label_flags &= ~F_BREAK_LABEL; 10281 + } 10282 + 10283 + static void clear_continue_label(void) { 10284 + continue_target_label = NULL; 10285 + continue_target_label_len = 0; 10286 + label_flags &= ~F_CONTINUE_LABEL; 10287 + } 10288 + 10289 + static jsval_t js_labeled_stmt(struct js *js, const char *label, jsoff_t label_len) { 10290 + uint8_t flags = js->flags; 10291 + jsval_t res = js_mkundef(); 10292 + 10293 + if (!label_stack) { 10294 + utarray_new(label_stack, &label_entry_icd); 10295 + } 10296 + 10297 + uint8_t next_tok = next(js); 10298 + bool is_loop = (next_tok == TOK_WHILE || next_tok == TOK_DO || next_tok == TOK_FOR); 10299 + bool is_block = (next_tok == TOK_LBRACE); 10300 + 10301 + label_entry_t entry = { 10302 + .name = label, 10303 + .name_len = label_len, 10304 + .is_loop = is_loop, 10305 + .is_block = is_block || !is_loop 10306 + }; 10307 + utarray_push_back(label_stack, &entry); 10308 + 10309 + if (is_loop && !(flags & F_NOEXEC)) { 10310 + res = js_stmt_impl(js); 10311 + 10312 + if ((js->flags & F_BREAK) && is_break_target(label, label_len)) { 10313 + js->flags &= ~(F_BREAK | F_NOEXEC); 10314 + clear_break_label(); 10315 + res = js_mkundef(); 10316 + } 10317 + 10318 + if ((label_flags & F_CONTINUE_LABEL) && is_continue_target(label, label_len)) { 10319 + clear_continue_label(); 10320 + } 10321 + } else if (is_loop && (flags & F_NOEXEC)) { 10322 + res = js_stmt_impl(js); 10323 + } else { 10324 + res = js_stmt_impl(js); 10325 + 10326 + if ((js->flags & F_BREAK) && is_break_target(label, label_len)) { 10327 + js->flags &= ~(F_BREAK | F_NOEXEC); 10328 + js->flags |= (flags & F_NOEXEC); 10329 + clear_break_label(); 10330 + res = js_mkundef(); 10331 + } 10332 + } 10333 + 10334 + if (label_stack && utarray_len(label_stack) > 0) { 10335 + utarray_pop_back(label_stack); 10336 + } 10337 + 10338 + return res; 10339 + } 10340 + 10341 + static jsval_t js_stmt_impl(struct js *js) { 10016 10342 jsval_t res; 10017 10343 if (js->brk > js->gct) js_gc(js); 10018 10344 uint8_t stmt_tok = next(js); ··· 10078 10404 } 10079 10405 10080 10406 return res; 10407 + } 10408 + 10409 + static jsval_t js_stmt(struct js *js) { 10410 + uint8_t tok = next(js); 10411 + 10412 + if (tok == TOK_IDENTIFIER) { 10413 + js_parse_state_t saved_state; 10414 + JS_SAVE_STATE(js, saved_state); 10415 + jsoff_t saved_toff = js->toff; 10416 + jsoff_t saved_tlen = js->tlen; 10417 + 10418 + const char *potential_label = &js->code[js->toff]; 10419 + jsoff_t potential_label_len = js->tlen; 10420 + 10421 + js->consumed = 1; 10422 + 10423 + if (next(js) == TOK_COLON) { 10424 + js->consumed = 1; 10425 + return js_labeled_stmt(js, potential_label, potential_label_len); 10426 + } 10427 + 10428 + JS_RESTORE_STATE(js, saved_state); 10429 + js->toff = saved_toff; 10430 + js->tlen = saved_tlen; 10431 + } 10432 + 10433 + return js_stmt_impl(js); 10081 10434 } 10082 10435 10083 10436 static jsval_t builtin_String(struct js *js, jsval_t *args, int nargs) {