From ce920b6ff47f32bc06dd27124e1b882f82f3d6ae Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 10:05:40 +0000 Subject: split compiler.zc into files by phase src/compiler/compiler.zc is now twelve files under src/compiler/, listed in order in src/compiler/parts. cat of the parts in order is byte-identical to the old file, so every compiler and image except the language server's is unchanged. no function was edited. consumers read the list: the makefile, bootstrap-check, genmap (which still writes one compiler.zc into a generated map directory), tinyvm.bat, test.sh, test_win.ps1 and the lsp drivers. the language server embeds each part and serves them joined as the one document tinyvm:/compiler.zc. --- src/compiler/setup.zc | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 src/compiler/setup.zc (limited to 'src/compiler/setup.zc') diff --git a/src/compiler/setup.zc b/src/compiler/setup.zc new file mode 100644 index 0000000..4986fc3 --- /dev/null +++ b/src/compiler/setup.zc @@ -0,0 +1,191 @@ +// ---- setup ---- + +// the collector's roots for the compiler's half of the memory map: the program +// area (globals live between functions), the genome's globals, the input source +// frames, the live frame stack and the live data stack. rt.zc declares this +// and calls it from gc(). +int gc_roots() { + int end = ptop; if (end > rt.CODE_END) end = rt.CODE_END; + rt.markrange(0, end); + if (!hdelta && here > ptop && ptop < rt.CODE_END) { end = here; if (end > rt.CODE_END) end = rt.CODE_END; rt.markrange(ptop, end); } // globals of a unit still compiling + rt.markrange(rt.GORG, rt.GORG[6]); + rt.markrange(R_IH, R_POOLEND); + rt.markrange(sval, sval + nsym); + rt.markrange(R_TY, R_TYEND); + rt.markrange(R_SRC, R_SRC + R_SRC_cap); + rt.gc_frames(); + rt.markrange(rt.dsp(), rt.data_top); + return 0; +} +// start-up in two parts. zc_init clears the intern table and interns the +// spellings the parser compares against; zc_tables fills the operator and +// keyword tables. an operator is a name, a precedence and a code sequence in +// the vm's own one-letter mnemonics (see seqasm), so "<=" is "~ < 1 ^": swap, +// less-than, flip the bit. +int zc_init() { + receiver_methods = 0; closure_expected = 0; + effect_floor = 0; effect_symbols = 0; effect_edges = 0; effect_requirements = 0; effect_current = 0; + pooltop = R_POOL; + poolend = R_POOLEND; poolblocks = 0; namecount = 0; + rt.fill_w(namehash, 0, hashcap); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; ntd = 0; snaptop = 0; nloc = 0; lbase = 0; npm = 0; npe = 0; rt.fill_w(lkeys, 0, lkeycap); lkeyn = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); + s_slash = intern("/"); + s_amp = intern("&"); + s_colon = intern(":"); + s_hash = intern("#"); + s_minus = intern("-"); + s_dot = intern("."); + s_q = intern("?"); + s_case = intern("case"); + s_default = intern("default"); + s_andand = intern("&&"); + s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); s_static = intern("static"); + s_start = intern("run.start"); + s_main = intern("main"); + s_free = intern("rt.free"); + s_recycle = intern("rt.recycle"); s_copy_w = intern("rt.copy_w"); + s_do = intern("do"); + s_while = intern("while"); + s_enum = intern("enum"); + s_include = intern("include"); + s_memdebug = intern("memdebug"); s_scrub = intern("rt.scrub"); s_alloc = intern("rt.alloc_line"); + s_check = intern("rt.checkptr"); s_memset = intern("rt.mem_set"); + s_own = intern("rt.own"); s_qnew = intern("run.qnew"); s_fovf = intern("rt.frame_overflow"); + s_frame_floor = intern("rt.frame_floor"); s_frame_top = intern("rt.frame_top"); s_frame_node = intern("rt.frame_node"); + s_frame_busy = intern("rt.frame_busy"); s_frame_guard = intern("rt.frame_guard"); + s_frame_grow = intern("rt.frame_grow"); s_frame_leave = intern("rt.frame_leave"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +// (the order of the first binary operators matters: cexpr refers to * + - << >> +// & | by their index, and the comments mark which ones) +int zc_tables() { + add_unop("!", "0 $ 0 ~ - | 63 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 63 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 63 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 63 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("do", kw_do, F_STMT); + add_kw("enum", kw_enum, F_STMT | F_TOP); + add_kw("include", kw_include, F_TOP | F_STMT); + add_kw("embed", kw_embed, F_TOP | F_STMT); + add_kw("run.method", kw_method, F_TOP | F_STMT); + add_kw("run.share_value", kw_share_value, F_EXPR); + add_kw("run.syntax", kw_syntax, F_TOP | F_STMT); + add_kw("run.statement_hook", kw_statement_hook, F_TOP | F_STMT); + add_kw("memdebug", kw_memdebug, F_STMT | F_TOP); + add_kw("hints", kw_hints, F_STMT | F_TOP); + add_kw("switch", kw_switch, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + add_kw("extern", kw_extern, F_TOP); + add_kw("cfunc", kw_cfunc, F_EXPR); + add_kw("o3", kw_o3, F_STMT); + add_kw("len", kw_len, F_EXPR); + add_kw("cap", kw_cap, F_EXPR); + add_kw("push", kw_push, F_EXPR); + add_kw("pop", kw_pop, F_EXPR); + add_kw("resize", kw_resize, F_EXPR); + return 0; +} +// the default map's type records sit in the last 16384 words of the heap. +// the type list is the first words of that range, so it cannot be kept aside +// as a buddy block: types_init would overwrite the header and the allocator +// would hand the range out again. cut the heap off at R_TY until the unit +// has been compiled, then put the tail back for the program. +int heldk; +// the size class of the type area, for the memory map. +int type_class() { + int k = 0, n = R_TYEND - R_TY; + while (k < 31 && rt.pow2[k] < n) k++; + if (rt.pow2[k] != n) return 0 - 1; + return k; +} +// keep the type area out of the collector's reach while it is in use. +int shield_types() { + int k; + heldk = 0 - 1; + if (R_TY <= rt.HEAP_LO || R_TY >= rt.HEAP_HI || R_TYEND > rt.HEAP_HI) return 0; + k = type_class(); + if (k < 0) return 0; + if (R_TY + rt.pow2[k] != R_TYEND) return 0; + if (rt.heap_reset(R_TY)) heldk = k; + return 0; +} +// let the collector see the type area again. +int unshield_types() { + if (heldk >= 0) { rt.heap_uncut(R_TY, heldk); heldk = 0 - 1; } + return 0; +} +// the genome's entry point, run when the image boots: set up the heap and +// the compiler, then either run the language server (a genome that carries it) +// or compile the unit on stdin, collect the garbage that compiling made, and +// call main(). +int start() { + int s, f; + rt.heap_init(); chk_heap = 1; + shield_types(); + zc_init(); + blob_load(rt.GORG[5], 0); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = rt.CODE_END; ptop = 0; + ubase = nsym; vislo = 0; + // a genome that carries the language server runs it instead of compiling stdin + s = lookup(intern("lsp.lsp_main")); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); rt.halt(); } + unit_stdin(); + if (here > ptop) ptop = here; + rt.scrub(); // compilation's stale words would keep quarantined blocks alive + rt.gc(); + // the type area stays out of the allocator. main() may compile again + // (the self-host does) and a later new() would otherwise reuse it. + if (mdbg_flags) rt.mem_set(mdbg_flags); + // a unit that carries src/lib/std.zc has a libinit to fill argc/argv/envc/envp + // with. it runs before main, the same way lsp_main runs instead of stdin. + s = lookup(intern("std.libinit")); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + rt.halt(); + return 0; +} + -- cgit v1.2.3