3 files changed+190−0
kiln/src/parser/C/statements/attempt_handle.cadded
+86−0@@ -0,0 +1,86 @@1
+#include "parser/H/core/parser.h"2
+#include "parser/H/core/parser_internal.h"3
+#include "parser/H/declarations/vars.h"4
+#include "codegen/H/emit/emit.h"5
+#include "codegen/H/runtime/errors.h"6
+#include "codegen/H/emit/layout.h"7
+#include "codegen/H/emit/runtime_layout.h"8
+9
+// attempt { A } handle (e) { B }10
+//11
+// A handler stack living in the fixed globals region (see12
+// elf/H/elf_writer.h) holds, per active attempt, the (rsp, rbp) to restore13
+// and the (target address, error-variable rbp-offset) to resume at --14
+// kiln's own setjmp/longjmp equivalent, hand-rolled since this is a15
+// freestanding binary with no libc to call into (see16
+// codegen/C/runtime/errors.c's raise routine for the runtime half of this).17
+//18
+// The handle variable's slot has to be reserved BEFORE compiling A: its19
+// rbp-relative offset must be embedded into the handler struct at20
+// attempt-entry, which runs before A's body -- and therefore before `e`'s21
+// real name, which only appears after A -- is even parsed. A placeholder22
+// slot is declared up front and retroactively renamed once `handle (e)`23
+// is actually seen. attempt/handle is inline (like given/during), not a24
+// new call frame, so this works the same way given/during's own bodies25
+// share the enclosing function's variable table.26
+void attempt_statement(void) {27
+ VarBlockScope saved_scope = vars_scope_begin();28
+ advance_token(); // consume 'attempt'29
+30
+ int handle_slot = declare_var("$handle", 7);31
+32
+ // --- push handler (bounds-checked -- overflowing here would33
+ // silently corrupt the code segment right after the handler table) ---34
+ emit_mov_reg_imm64(code, REG_RAX, kiln_try_depth_addr());35
+ emit_load_mem_disp32(code, REG_RAX, REG_RAX, 0);36
+ emit_cmp_reg_imm32(code, REG_RAX, MAX_KILN_TRY_DEPTH);37
+ int overflow = emit_jcc_rel32(code, COND_GE);38
+39
+ emit_mov_reg_imm64(code, REG_RCX, KILN_TRY_HANDLER_SIZE);40
+ emit_imul_reg_reg(code, REG_RAX, REG_RCX); // RAX = index * handler size41
+ emit_mov_reg_imm64(code, REG_RDI, kiln_try_handlers_addr());42
+ emit_add_reg_reg(code, REG_RDI, REG_RAX); // RDI = this handler's slot address43
+44
+ emit_store_mem_disp32(code, REG_RDI, 0, REG_RSP);45
+ emit_store_mem_disp32(code, REG_RDI, 8, REG_RBP);46
+ int patch_offset = emit_mov_reg_imm64_patchable(code, REG_RCX); // handle target, patched once known47
+ emit_store_mem_disp32(code, REG_RDI, 16, REG_RCX);48
+ emit_mov_reg_imm64(code, REG_RCX, (uint64_t)(int64_t)var_slot_tag_offset(handle_slot));49
+ emit_store_mem_disp32(code, REG_RDI, 24, REG_RCX);50
+51
+ emit_mov_reg_imm64(code, REG_RAX, kiln_try_depth_addr());52
+ emit_load_mem_disp32(code, REG_RCX, REG_RAX, 0);53
+ emit_add_reg_imm8(code, REG_RCX, 1);54
+ emit_store_mem_disp32(code, REG_RAX, 0, REG_RCX);55
+56
+ int skip_overflow_die = emit_jmp_rel32(code);57
+ emit_patch_jump(code, overflow);58
+ errors_emit_fatal(code, "runtime error: too many nested try blocks");59
+ emit_patch_jump(code, skip_overflow_die);60
+61
+ block(); // compiles A62
+63
+ // Normal completion (no error was raised): pop the handler -- past64
+ // this point, an error inside the enclosing scope should NOT be65
+ // caught by this attempt anymore.66
+ emit_mov_reg_imm64(code, REG_RAX, kiln_try_depth_addr());67
+ emit_load_mem_disp32(code, REG_RCX, REG_RAX, 0);68
+ emit_dec_reg(code, REG_RCX);69
+ emit_store_mem_disp32(code, REG_RAX, 0, REG_RCX);70
+71
+ int skip_handle = emit_jmp_rel32(code);72
+ int handle_code_offset = code->count;73
+ uint64_t handle_addr = kiln_code_base() + (uint64_t)handle_code_offset;74
+ emit_patch_imm64(code, patch_offset, handle_addr);75
+76
+ expect(TOKEN_HANDLE, "expected 'handle' after attempt block");77
+ expect(TOKEN_LPAREN, "expected '(' after 'handle'");78
+ expect(TOKEN_IDENTIFIER, "expected error variable name");79
+ rename_var(handle_slot, previous.start, previous.length);80
+ expect(TOKEN_RPAREN, "expected ')' after handle variable");81
+82
+ block(); // compiles B -- `e` already declared at handle_slot above83
+84
+ emit_patch_jump(code, skip_handle);85
+ vars_scope_end(saved_scope);86
+}kiln/src/parser/C/statements/each_loop.cadded
+84−0@@ -0,0 +1,84 @@1
+#include "parser/H/statements/each_loop.h"2
+#include "parser/H/core/parser.h"3
+#include "parser/H/core/parser_internal.h"4
+#include "parser/H/declarations/vars.h"5
+#include "parser/H/statements/loop_stack.h"6
+#include "codegen/H/expressions/expr.h"7
+#include "codegen/H/emit/emit.h"8
+9
+static int each_nesting_depth = 0;10
+11
+void each_depth_save(int *out) { *out = each_nesting_depth; }12
+void each_depth_reset(void) { each_nesting_depth = 0; }13
+void each_depth_restore(int saved) { each_nesting_depth = saved; }14
+15
+// each (x in array) { block } -- parens optional, matching16
+// given/during's own optional-paren conditions.17
+//18
+// The array expression is evaluated once, up front; x is bound to each19
+// element in turn via the same var-slot mechanism `local` uses, so it's a20
+// perfectly ordinary variable inside the body (readable, reassignable,21
+// though reassigning it doesn't affect iteration -- the next iteration22
+// overwrites it again from the array, matching a plain for-each's usual23
+// semantics). data_ptr/count/i live in this nesting level's fixed24
+// RBP-relative slot (see vars.h's each_level_offset) so they survive25
+// whatever the body's own codegen does, including calls.26
+void each_statement(void) {27
+ VarBlockScope saved_scope = vars_scope_begin();28
+ advance_token(); // consume 'each'29
+ int parenthesized = 0;30
+ if (current.type == TOKEN_LPAREN) { parenthesized = 1; advance_token(); }31
+ expect(TOKEN_IDENTIFIER, "expected loop variable name");32
+ const char *var_name = previous.start;33
+ int var_len = previous.length;34
+ expect(TOKEN_IN, "expected 'in' after loop variable");35
+ codegen_expression(); // pushes the array (tag, payload)36
+ if (parenthesized) expect(TOKEN_RPAREN, "expected ')' after loop expression");37
+38
+ if (each_nesting_depth >= MAX_KILN_EACH_DEPTH) parse_error("too many nested each loops");39
+ int32_t level = each_level_offset(each_nesting_depth);40
+ each_nesting_depth++;41
+42
+ emit_pop_reg(code, REG_RAX); // array payload (object address)43
+ emit_pop_reg(code, REG_RBX); // array tag (ignored, assumed ARRAY)44
+ emit_load_mem_disp32(code, REG_RCX, REG_RAX, 8); // count45
+ emit_load_mem_disp32(code, REG_RDX, REG_RAX, 16); // data_ptr46
+ emit_store_mem_disp32(code, REG_RBP, level + 0, REG_RDX);47
+ emit_store_mem_disp32(code, REG_RBP, level + 8, REG_RCX);48
+ emit_mov_reg_imm64(code, REG_RAX, 0);49
+ emit_store_mem_disp32(code, REG_RBP, level + 16, REG_RAX); // i = 050
+51
+ int slot = resolve_var_in_current_scope(var_name, var_len);52
+ if (slot == -1) slot = declare_var(var_name, var_len);53
+54
+ int loop_start = code->count;55
+ emit_load_mem_disp32(code, REG_RCX, REG_RBP, level + 16); // i56
+ emit_load_mem_disp32(code, REG_RDX, REG_RBP, level + 8); // count57
+ emit_cmp_reg_reg(code, REG_RCX, REG_RDX);58
+ int loop_exit = emit_jcc_rel32(code, COND_GE);59
+60
+ emit_load_mem_disp32(code, REG_RSI, REG_RBP, level + 0); // data_ptr61
+ emit_mov_reg_reg(code, REG_RAX, REG_RCX);62
+ emit_mov_reg_imm64(code, REG_RDX, 16);63
+ emit_imul_reg_reg(code, REG_RAX, REG_RDX);64
+ emit_add_reg_reg(code, REG_RSI, REG_RAX); // element address65
+66
+ emit_load_mem_disp32(code, REG_RBX, REG_RSI, 0); // element tag67
+ emit_load_mem_disp32(code, REG_RAX, REG_RSI, 8); // element payload68
+ emit_store_mem_disp32(code, REG_RBP, var_slot_tag_offset(slot), REG_RBX);69
+ emit_store_mem_disp32(code, REG_RBP, var_slot_payload_offset(slot), REG_RAX);70
+71
+ loop_push();72
+ block();73
+ loop_patch_continues(); // continue lands here, right before i++74
+75
+ emit_load_mem_disp32(code, REG_RCX, REG_RBP, level + 16);76
+ emit_add_reg_imm8(code, REG_RCX, 1);77
+ emit_store_mem_disp32(code, REG_RBP, level + 16, REG_RCX); // i++78
+ emit_jmp_back(code, loop_start);79
+ emit_patch_jump(code, loop_exit);80
+ loop_pop_and_patch_breaks(); // break lands here, after the loop81
+82
+ each_nesting_depth--;83
+ vars_scope_end(saved_scope);84
+}kiln/src/parser/H/statements/each_loop.hadded
+20−0@@ -0,0 +1,20 @@1
+#ifndef KILN_EACH_LOOP_H2
+#define KILN_EACH_LOOP_H3
+4
+// each_statement itself is declared in parser_internal.h alongside5
+// given_statement/during_statement/forge_statement/attempt_statement (the6
+// other statement-compiler cross-file links) -- this header exists only7
+// for the depth counter below, which forge_statement/lambda.c also need.8
+//9
+// The compile-time each-nesting-depth counter's save/reset/restore, for10
+// forge_statement/lambda.c to wrap around a called body's compilation11
+// (same spirit as parser/H/declarations/vars.h's12
+// vars_save/vars_clear/vars_restore): a function or lambda body gets its13
+// own RBP at runtime, so its own `each` loops can safely reuse the same14
+// offsets independently of how deep the enclosing code's nesting was --15
+// see vars.h's each_level_offset comment.16
+void each_depth_save(int *out);17
+void each_depth_reset(void);18
+void each_depth_restore(int saved);19
+20
+#endif