From 1c53304d03414890af0da7432d1aea87e754a14a Mon Sep 17 00:00:00 2001 From: Moon Flower Date: Wed, 14 Oct 2026 08:29:46 +0330 Subject: feat: grow memory limits, add icon/actor libraries, overhaul LSP diagnostics Compiler and VM - make every memory map limit dynamic and adjustable at both build time and runtime, including compiler tables, stacks, and every execution region - reserve and grow tables, operand and return stacks, frame segments, and image-building output without relocating live code or globals - rename i/o and string helpers consistently (puts -> put_s, putn -> put_n, scat -> cat_s) across compiler, runtime, examples, tests, and docs - recast and consume values assigned to globals from inside functions - allow fixed and const together, add typed closure returns (fn:t(...)), multi-include syntax (include rt, run, core;), and recurse type inference - never infer string literals as word, anywhere, as argument, variable, or return type - draft docs/BORROW.md: a borrow checker less intrusive than Rust's but more intelligent LSP and editor integration - require explicit includes and give every toolchain area its own namespace (rt, lsp, run, core) - hover for built-in types, inferred types, values known at hover time, array and pointer lifetime/size, library names in include statements, and inferred lifetimes with explanations - honor /* */ comments as documentation for variables and arguments, and stop misattributing section headers or trailing comments as variable hints - stream diagnostics and cancel stale requests on change - jump to definition and references, including cross-file - quick fixes with automatic library import, and namespace completion Hints and diagnostics - render hints as blue squigglies by default, with hints on/small/off per file - suppress magic-number hints via a preceding /* */ comment, and say so - warn on unused arguments; allow multiple _ discards in parameters, pattern matching, and tagged enums, warning if a _ is actually used - rewrite hint and comment prose in professional compiler-doc register Libraries - add icon.zc: run-time self-modification, find/replace syntax, type and block edits, and step-through inspection of locals and globals - add actor.zc: async actors with Actor, implicit this, spawn/recv/send, injected into the compiler machinery at include time rather than built in Tooling and docs - reorganize sources, tests, and designs into subdirectories - unify windows entry points in tinyvm.bat and add a matching tinyvm.sh - add --help across the toolchain - polish the learning guide, stdlib docs, and design notes Tests - add accept, reject, and regression coverage across the compiler, LSP, - runtime, icon, actors, threads, formatting, and the toolchain contracts, plus fuzzers and property tests --- src/tools/zc0.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 17 deletions(-) (limited to 'src/tools/zc0.c') diff --git a/src/tools/zc0.c b/src/tools/zc0.c index 5ef5737..446557b 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -27,6 +27,7 @@ enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; +static char module_name[64]; static char *src; static int pos, line = 1, tstart, hdr_start; /* the signature of the function being defined: its header's source text, for the language server (ZC0DOCS) */ static char sgtext[400], sgname[80]; @@ -78,6 +79,7 @@ static void doccomment(void) /* pos is on the first slash */ while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--; bol = k < 0 || src[k] == '\n'; pos += 2; + if (src[pos] == ':') { dn = 0; dlast = cl; docpending = 0; while (src[pos] && src[pos] != '\n') pos++; return; } if (!bol) { while (src[pos] && src[pos] != '\n') pos++; return; } if (dn && dlast != cl - 1) dn = 0; if (src[pos] == ' ') pos++; @@ -176,6 +178,11 @@ static void next(void) while (src[pos] && src[pos] != '"' && i < 4095) ts[i++] = (char)esc(); ts[i] = 0; if (src[pos] == '"') pos++; tk = T_BSTR; return; } + if (src[pos] == '.' && (!strcmp(ts,"rt") || !strcmp(ts,"run") || !strcmp(ts,"core") || !strcmp(ts,"lsp") || !strcmp(ts,"std") || !strcmp(ts,"icon"))) { + ts[i++] = src[pos++]; + while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; + } tk = T_ID; return; } if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } @@ -198,10 +205,24 @@ typedef struct { char name[64]; int kind, sig, defd; int64_t val; } Sym; static Sym sy[16384]; static int nsy; typedef struct { int at, sym; } Fix; static Fix fx[65536]; static int nfx; -static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *find_raw(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static void qualify(char *dst, size_t size, const char *name) { + if (!module_name[0] || strchr(name, '.')) snprintf(dst, size, "%s", name); + else if (!strncmp(name, "doc ", 4) || !strncmp(name, "sig ", 4)) snprintf(dst, size, "%.4s%s.%s", name, module_name, name + 4); + else snprintf(dst, size, "%s.%s", module_name, name); +} +static Sym *find(const char *name) { + char full[256]; Sym *s; + qualify(full, sizeof full, name); s = find_raw(full); if (s) return s; + s = find_raw(name); if (s || strchr(name, '.')) return s; + const char *modules[] = { "rt", "run", "core", "lsp", 0 }; + for (int i = 0; modules[i]; i++) { snprintf(full,sizeof full,"%s.%s",modules[i],name); s=find_raw(full); if(s) return s; } + return 0; +} static Sym *add(const char *n, int kind, int64_t val) { - Sym *s = find(n); + char full[256]; qualify(full, sizeof full, n); n = full; + Sym *s = find_raw(n); if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; } @@ -318,11 +339,11 @@ static int64_t cexpr(void) /* ---- types ---- zc0 has no types: a type is read and thrown away. all it has to know is the - shape, so that [n]T still reserves n words and the rest reserve one. the + shape, so that T[n] still reserves n words and the rest reserve one. the grammar is the compiler's own (see ptype in compiler.zc): ptype a type, then any number of (params): int(int, int) is a function type ptype0 a type with its stars: int*, vec*, point** - ptype1 a base: [n]T, []T, fixed []T, fn, a name, or a generic instance + ptype1 a base: T[n], T[?], fixed T[?], fn, a name, or a generic instance each returns the words a variable of the type takes: n for a fixed array, 1 for a growable handle or anything scalar. */ static int ptype0(void); @@ -330,12 +351,7 @@ static int ptype(void); static int ptype1(void) { int n, e; - if (accept("[")) { - if (accept("]")) { ptype0(); return 1; } /* []T is a handle */ - n = (int)cexpr(); expect("]"); - e = ptype0(); - return n > 0 ? n * e : e; - } + if (is("[")) { die("array types are postfix: T[N], T[] or T[?]"); return 1; } if (is("fixed")) { next(); return ptype0(); } if (is("fn")) { next(); return 1; } /* fn(int): the parameters follow, in ptype */ if (tk != T_ID) { die("a type name"); return 1; } @@ -350,14 +366,30 @@ static int ptype1(void) static int ptype0(void) { int n = ptype1(); - while (accept("*")) n = 1; + while (is("*") || is("[")) { + if (accept("*")) n = 1; + else { + int count = 0, cap = 8, *dims = malloc(cap * sizeof(int)); + if (!dims) die("cannot allocate array dimensions"); + while (accept("[")) { + int length; + if (accept("?")) { expect("]"); length = -2; } + else if (accept("]")) length = -1; + else { length = (int)cexpr(); expect("]"); } + if (count == cap) { cap *= 2; int *more = realloc(dims, cap * sizeof(int)); if (!more) die("cannot grow array dimensions"); dims = more; } + dims[count++] = length; + } + while (count) { int length = dims[--count]; if (length == -2) n = 1; else n = length < 0 ? 0 : n * length; } + free(dims); + } + } return n; } static int ptype(void) { int n = ptype0(); while (accept("(")) { - if (!accept(")")) { do { ptype(); } while (accept(",")); expect(")"); } + if (!accept(")")) { do { ptype(); if (accept(".")) ident(); } while (accept(",")); expect(")"); } n = 1; } return n; @@ -435,6 +467,7 @@ static void bstrlit(void) } static int returns_bstr(const char *n) { + if (!strncmp(n, "rt.", 3)) n += 3; return !strcmp(n, "cat_s") || !strcmp(n, "slice_s") || !strcmp(n, "n_to_s") || !strcmp(n, "new_s"); } static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } @@ -457,6 +490,13 @@ static void primary(void) if (s && s->kind == K_GARR) { lit(s->val); return; } if (is("(")) { int na = args(); + if (s && s->kind == K_ENH) { + if (na != (s->sig & 15)) die("wrong number of enhancement inputs"); + op(22 + (int)s->val); + if (((s->sig >> 4) & 15) == 0) lit(0); + else if (((s->sig >> 4) & 15) != 1) die("multiple enhancement outputs need asm"); + xs = 0; xf = 0; return; + } if (!strcmp(n, "put_s") && na == 1 && xs) fnref("put_bs"); else if (!strcmp(n, "len")) fnref("len_s"); else fnref(n); @@ -671,7 +711,7 @@ static void fndef(int ret) char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node; if (dsn && !dnm[0]) strcpy(dnm, n); char pn[16][64]; - s = find(n); + char full_name[256]; qualify(full_name, sizeof full_name, n); s = find_raw(full_name); if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } expect("("); if (!accept(")")) { @@ -705,7 +745,7 @@ static void fndef(int ret) nlo = 0; nslot = 0; nepi = 0; infn = 1; for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].str = 0; lo[nlo++].slot = nslot++; } /* retain the caller's pointer until the frame has its final segment. */ - helper = !find("frame_busy") || !strcmp(s->name, "frame_grow") || !strcmp(s->name, "frame_leave"); + helper = !find("frame_busy") || !strcmp(s->name, "rt.frame_grow") || !strcmp(s->name, "rt.frame_leave"); lit(ZFP); op(O_LD); if (!helper) { lit(0); op(O_PICK); } frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); if (!helper) { @@ -756,6 +796,12 @@ static void enhdef(void) } static void top1(void) { + if (accept("namespace")) { + if (is(";")) module_name[0] = 0; + else { snprintf(module_name, sizeof module_name, "%s", ident()); } + expect(";"); return; + } + if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; } { if (accept("unchecked")) { top1(); return; } if (accept("fn")) { fndef(0); return; } @@ -790,6 +836,21 @@ static void top1(void) } } +/* section headings organize the source; they do not document a symbol. */ +static int doc_section(const char *text, int length) +{ + int start = 0, end, a, b; + while (start < length) { + end = start; while (end < length && text[end] != '\n') end++; + a = start; b = end; + while (a < b && isspace((unsigned char)text[a])) a++; + while (b > a && isspace((unsigned char)text[b - 1])) b--; + if (b - a >= 4 && text[a] == '-' && text[a + 1] == '-' && text[b - 1] == '-' && text[b - 2] == '-') return 1; + start = end + 1; + } + return 0; +} + /* one item, with the doc comment above it kept under the name it declares */ static void top(void) { @@ -801,7 +862,7 @@ static void top(void) if (dn && dline == line) { memcpy(dsnap, dbuf, dn); dsn = dn; dline = 0; } top1(); #ifdef ZC0DOCS /* enabled for images that carry editor documentation */ - if (dsn && dnm[0]) { + if (dsn && dnm[0] && !doc_section(dsnap, dsn)) { int j = here, a, i = 0, k, c; uint64_t w; char nm[80]; lit(0); op(O_JMP); a = here; emit(dsn); @@ -834,8 +895,16 @@ static void top(void) } } -int main(void) +int main(int argc, char **argv) { + if (argc == 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) { + fputs("usage: zc0 [--help|-h] < source.zc > genome.h\n" + "compiles bootstrap source from stdin into a c genome header on stdout.\n" + "source must define start(); memory layout is selected when zc0 is built.\n" + "options:\n --help, -h show this help and exit\n", stdout); + return 0; + } + if (argc != 1) { fputs("zc0: unexpected arguments; use --help for usage\n", stderr); return 2; } #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); @@ -856,7 +925,7 @@ int main(void) } buf[n] = 0; src = buf; next(); /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ - lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + lit(FTOP); lit(ZFP); op(O_ST); fnref(strstr(src, "namespace run;") ? "run.start" : "start"); op(O_JMP); emit(0); emit(0); top(); for (i = 0; i < nfx; i++) { Sym *s = &sy[fx[i].sym]; -- cgit v1.2.3