aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/base.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 15:54:04 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 15:54:04 +0330
commit0879d0a42d137054057cd2a1e37437e6be78ed0b (patch)
treefcb16d3185c1f80fae8d54cd9062bd68d6c0c88d /src/compiler/base.zc
parentmerge spec conformance suite (diff)
parentmerge main into work/line-tables (diff)
merge line tables
Diffstat (limited to 'src/compiler/base.zc')
-rw-r--r--src/compiler/base.zc89
1 files changed, 88 insertions, 1 deletions
diff --git a/src/compiler/base.zc b/src/compiler/base.zc
index d83dc9d..2f802b9 100644
--- a/src/compiler/base.zc
+++ b/src/compiler/base.zc
@@ -224,6 +224,16 @@ char[1100] tbuf_initial; char* tbuf = tbuf_initial; int tbufcap = 1100;
// collects them in dbuf, dlast is the line of the last one, and dline the
// line of the token that follows them. only when docs are on.
int docs_on; // docs on; keeps them: a symbol "doc name" holds a byte string
+// line tables: lines on; (the default) records, for each function compiled, its entry, its end and the
+// source line of each statement, as a line block among the code that rt.line_blocks chains (see
+// rt.zc). lt_buf holds the pairs of the functions being compiled, innermost last, each as a frame
+// [outer frame, entry, name, file, 0, offset << 32 | line ...]; lt_base is the innermost frame.
+// lt_head is the newest block of an image being built, whose own rt.line_blocks it will become.
+int lines_on = 1;
+// //:lines NAME (see mark_line): the markers the lexer has met, [lines before the file, NAME] each, and the
+// one in force, which a top-level item applies when it is reached
+word lt_marks; int lt_nmarks, lt_markcap, lt_next_mark; char* lt_mark_file; int lt_mark_base;
+word lt_buf, lt_head, lt_file_at; int lt_top, lt_cap, lt_base = 0 - 1, lt_file_img; char* lt_file;
char[2100] dbuf_initial; char* dbuf = dbuf_initial; int docbufcap = 2100; int dn, dlast, dline, docpending, dsect;
char[2100] dsnap_initial; char* dsnap = dsnap_initial; int docsnapcap = 2100; int dsn, dnm; char[300] dnmb_initial; char* dnmb = dnmb_initial; int docnamecap = 300;
int pk_tk, pk_tv, pq_n; word[4] pq_tk; word[4] pq_tv; word[4] pq_gl; word[4] pq_ln; // up to 4 tokens of lookahead
@@ -299,7 +309,7 @@ word tdstk; int ntd, tdcap;
word readonly_types;
int dty, dtag; // result of dtype()
int s_typedef;
-int s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_docs, s_small, s_std, s_icon, s_on, s_off, s_new, s_new_t;
+int s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_docs, s_lines, s_small, s_std, s_icon, s_on, s_off, s_new, s_new_t;
int s_o3, s_lfa, s_outside, s_new_raw, s_new_oz, s_oob;
int s_ld8, s_st8, s_ld16, s_st16, s_ld32, s_st32;
// ---- o3 region state ----
@@ -1222,6 +1232,83 @@ int hole() {
lit(rt.MW - 1);
return a;
}
+// ---- line tables ----
+
+// make room for n more words of pairs.
+int lt_room(int n) {
+ int cap = lt_cap;
+ if (lt_top + n <= cap) return 0;
+ if (cap < 256) cap = 256;
+ while (lt_top + n > cap) cap = cap * 2;
+ lt_buf = chk_resize(lt_buf, lt_top, cap); lt_cap = cap;
+ return 0;
+}
+// start recording the function whose code begins at entry, named name, with its header on line.
+// returns the frame to give lt_end, or -1 when no table is being made (lines off, a dry run,
+// check mode).
+int lt_begin(int entry, char* name, int line) {
+ int b;
+ if (!lines_on || dry || cmode) return 0 - 1;
+ lt_room(8);
+ b = lt_top;
+ lt_buf[b] = lt_base; lt_buf[b + 1] = entry; lt_buf[b + 2] = name; lt_buf[b + 3] = curinc; lt_buf[b + 4] = 0;
+ if (!curinc) { lt_buf[b + 3] = lt_mark_file; lt_buf[b + 4] = lt_mark_base; } // the main source may be marked as a file of its own
+ lt_top = b + 5; lt_base = b;
+ lt_buf[lt_top] = line - lt_buf[b + 4]; lt_top++; // the code before the first statement belongs to the header's line
+ return b;
+}
+// a statement begins at the current token, and its code at here.
+int lt_stmt() {
+ int off, n;
+ if (lt_base < 0 || dry) return 0;
+ off = here - lt_buf[lt_base + 1];
+ if (off < 0) return 0;
+ lt_room(2);
+ if (lt_top > lt_base + 5 && (lt_buf[lt_top - 1] >> 32) == off) lt_top--; // same place: the later statement is the one
+ n = tline - lt_buf[lt_base + 4];
+ if (lt_top > lt_base + 5 && (lt_buf[lt_top - 1] & 4294967295) == n) return 0;
+ lt_buf[lt_top] = off * 4294967296 + n; lt_top++;
+ return 0;
+}
+// the file name in a block: the include's path, or <stdin> for the main source. one copy of the
+// bytes is shared by the blocks that follow it.
+int lt_file_data(char* f) {
+ if (!f) f = "<stdin>";
+ if (lt_file_at && lt_file_img == image_active && rt.eq_w(lt_file, f)) return lt_file_at;
+ lt_file_at = bstrdata(f); lt_file_img = image_active;
+ lt_file = chk_resize(0, 0, rt.len_w(f) + 1); rt.copy_w(lt_file, f, rt.len_w(f) + 1);
+ return lt_file_at;
+}
+// finish the function begun at frame b, whose code ends at end: put its line block among the code
+// and chain it. a block is [next, entry, end, name, file, count, pairs...].
+int lt_end(int b, int end) {
+ int entry, count, i = 0, name, file, blk, need; word head; char* nm; char* fl;
+ if (b < 0) return 0;
+ entry = lt_buf[b + 1]; nm = lt_buf[b + 2]; fl = lt_buf[b + 3]; count = lt_top - (b + 5);
+ if (!nm) nm = "<fn>";
+ if (!fl) fl = "<stdin>";
+ if (end >= entry && !dry) {
+ need = 8 + count + ((rt.len_w(nm) + 7) >> 3) + 2 + ((rt.len_w(fl) + 7) >> 3) + 2;
+ reserve_code(need);
+ name = bstrdata(nm); file = lt_file_data(fl);
+ if (image_active) head = lt_head; else head = rt.line_blocks;
+ blk = here;
+ emit(head); emit(entry); emit(end); emit(name); emit(file); emit(count);
+ while (i < count) { emit(lt_buf[b + 5 + i]); i++; }
+ if (image_active) lt_head = blk; else rt.line_blocks = blk;
+ }
+ lt_top = b; lt_base = lt_buf[b];
+ return 0;
+}
+// at the top level, a marker that comes before the item takes effect: lines are on, in its file.
+int lt_apply_marks() {
+ if (incdepth || ginst) return 0;
+ while (lt_next_mark < lt_nmarks && lt_marks[lt_next_mark * 2] < tline) {
+ lt_mark_base = lt_marks[lt_next_mark * 2]; lt_mark_file = lt_marks[lt_next_mark * 2 + 1];
+ lines_on = 1; lt_next_mark++;
+ }
+ return 0;
+}
// forward references. calling a function that is not defined yet creates a
// symbol with F_DEF clear whose value is the place of its first use (for the
// "never defined" error). each call site emits a placeholder literal and adds