aboutsummaryrefslogtreecommitdiff
path: root/src/tools/zc0.c
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
commitecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 (patch)
tree70e0af76a1b13affff7c6cc8aac7b3e6ef960478 /src/tools/zc0.c
a
Diffstat (limited to 'src/tools/zc0.c')
-rw-r--r--src/tools/zc0.c880
1 files changed, 880 insertions, 0 deletions
diff --git a/src/tools/zc0.c b/src/tools/zc0.c
new file mode 100644
index 0000000..5ef5737
--- /dev/null
+++ b/src/tools/zc0.c
@@ -0,0 +1,880 @@
+/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that
+ compiler.zc is written in. it reads zc source on stdin and writes a genome
+ header (vm words placed at GORG) on stdout, in the same image layout the
+ resident compiler's image mode produces. no lifetime analysis, no quote, no
+ parse words, no block expressions: those only exist in compiler.zc. tags
+ (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures
+ but not checked. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#endif
+#include "memmap.h"
+#include "fops.h"
+
+#ifndef MAXW
+#define MAXW 0x80000
+#endif
+
+enum { T_EOF, T_NUM, T_ID, T_STR, T_OP, T_FLT, T_BSTR };
+enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH, K_TYPE, K_DOC };
+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 *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];
+static int tk, glue; static int64_t tv; static char ts[4096]; /* current token */
+static int xf, xs, lvi; /* float, byte string; the plain local it came from, or -1 */
+
+static int64_t *img; static int imgcap, here = GORG;
+static void emit(int64_t w) {
+ if (!img || here - GORG >= imgcap) {
+ int capacity; int64_t *next;
+ if (imgcap > (0x7fffffff - GORG) / 2) { fprintf(stderr, "zc0: image address overflow\n"); exit(1); }
+ capacity = imgcap ? imgcap * 2 : MAXW;
+ if (capacity <= imgcap || capacity > 0x7fffffff - GORG) { fprintf(stderr, "zc0: image address overflow\n"); exit(1); }
+ next = realloc(img, (size_t)capacity * sizeof *img);
+ if (!next) { fprintf(stderr, "zc0: cannot grow image buffer\n"); exit(1); }
+ img = next; imgcap = capacity;
+ }
+ img[here++ - GORG] = w;
+}
+static void lit(int64_t v) {
+ if ((v >> 62) != 0 && (v >> 62) != -1) {
+ emit((v >> 32) * 2 + 1); emit(0x100000000LL * 2 + 1); emit(O_MUL * 2);
+ emit((int64_t)((uint64_t)v & 0xffffffffu) * 2 + 1); emit(O_OR * 2);
+ } else emit(v * 2 + 1);
+}
+static void op(int k) { emit((int64_t)k * 2); }
+static void setlit(int at, int64_t v) { img[at - GORG] = v * 2 + 1; }
+
+static int pguess, pbad; /* inside a speculative type parse: an error is noted instead of stopping */
+static void die(const char *m) { if (pguess) { pbad = 1; return; } fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); }
+
+/* ---- lexer ---- */
+static const char *ops3[] = { "<<=", ">>=", 0 };
+static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 };
+static int esc(void)
+{
+ int c = src[pos++];
+ if (c != '\\') return c;
+ c = src[pos++];
+ switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; }
+ return c;
+}
+/* doc comments, as the self-hosted compiler reads them with docs on: the //
+ lines directly above a declaration, up to a blank line or any other text */
+static char dbuf[2100], dsnap[2100], dnm[64]; static int dn, dlast, dline, dsn, docpending;
+static void doccomment(void) /* pos is on the first slash */
+{
+ int cl = line, k = pos - 1, bol;
+ while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--;
+ bol = k < 0 || src[k] == '\n';
+ pos += 2;
+ if (!bol) { while (src[pos] && src[pos] != '\n') pos++; return; }
+ if (dn && dlast != cl - 1) dn = 0;
+ if (src[pos] == ' ') pos++;
+ while (src[pos] && src[pos] != '\n') { if (dn < 2040) dbuf[dn++] = src[pos]; pos++; }
+ while (dn && (dbuf[dn - 1] == ' ' || dbuf[dn - 1] == '\t' || dbuf[dn - 1] == '\r')) dn--;
+ dbuf[dn++] = '\n';
+ dlast = cl; docpending = 1;
+}
+/* block comments may document a declaration on the same line */
+static void docblock(void)
+{
+ int cl = line, k = pos - 1, bol, start = 1, c;
+ while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--;
+ bol = k < 0 || src[k] == '\n';
+ if (!bol || (dn && dlast != cl - 1)) dn = 0;
+ pos += 2;
+ if (src[pos] == '*') { pos++; if (src[pos] == '/') { pos++; return; } }
+ while (src[pos]) {
+ if (start) {
+ while (src[pos] == ' ' || src[pos] == '\t') pos++;
+ if (src[pos] == '*') {
+ pos++;
+ if (src[pos] == '/') { pos++; break; }
+ if (src[pos] == ' ') pos++;
+ }
+ start = 0;
+ }
+ c = src[pos++];
+ if (c == '*' && src[pos] == '/') { pos++; break; }
+ if (dn < 2040) dbuf[dn++] = (char)c;
+ if (c == '\n') { line++; start = 1; }
+ }
+ while (dn && (dbuf[dn - 1] == ' ' || dbuf[dn - 1] == '\t' || dbuf[dn - 1] == '\r' || dbuf[dn - 1] == '\n')) dn--;
+ dbuf[dn++] = '\n'; dlast = line; docpending = 1;
+}
+static void next(void)
+{
+ int ws = 0, i;
+ for (;;) {
+ while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; }
+ if (src[pos] == '/' && src[pos + 1] == '/') { doccomment(); ws = 1; continue; }
+ if (src[pos] == '/' && src[pos + 1] == '*') { docblock(); ws = 1; continue; }
+ break;
+ }
+ glue = !ws;
+ tstart = pos;
+ if (docpending) {
+ dline = 0;
+ if (dn && (line == dlast || line == dlast + 1)) dline = line;
+ docpending = 0;
+ }
+ if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, "<eof>"); return; }
+ if (isdigit((unsigned char)src[pos]) || (src[pos] == '.' && isdigit((unsigned char)src[pos + 1]))) {
+ int p = pos, fl = 0;
+ if (src[pos] == '0' && (src[pos + 1] == 'x' || src[pos + 1] == 'X')) {
+ char *e; tv = (int64_t)strtoll(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return;
+ }
+ if (src[p] == '.') {
+ int nch = (unsigned char)src[p + 1];
+ if (isdigit(nch) || nch == 'e' || nch == 'E' || !(isalpha(nch) || nch == '_')) fl = 1;
+ } else {
+ while (isdigit((unsigned char)src[p])) p++;
+ if (src[p] == '.') {
+ int nch = (unsigned char)src[p + 1];
+ /* 1.5 and 1. and 1.e2 are floats. 1.field is an integer and a field. */
+ if (isdigit(nch) || nch == 'e' || nch == 'E' || !(isalpha(nch) || nch == '_')) fl = 1;
+ }
+ }
+ if (!fl && (src[p] == 'e' || src[p] == 'E')) {
+ int q = p + 1; if (src[q] == '+' || src[q] == '-') q++; if (isdigit((unsigned char)src[q])) fl = 1;
+ }
+ if (fl) {
+ char b[80]; int n = 0, frac = 0;
+ while (src[pos] == '.' || isdigit((unsigned char)src[pos])) {
+ if (src[pos] == '.') { if (frac) break; frac = 1; }
+ if (n < 78) b[n++] = src[pos];
+ pos++;
+ }
+ if (src[pos] == 'e' || src[pos] == 'E') {
+ int q = pos + 1; if (src[q] == '+' || src[q] == '-') q++;
+ if (isdigit((unsigned char)src[q])) {
+ if (n < 78) b[n++] = src[pos++];
+ if (src[pos] == '+' || src[pos] == '-') { if (n < 78) b[n++] = src[pos++]; }
+ while (isdigit((unsigned char)src[pos]) && n < 78) b[n++] = src[pos++];
+ }
+ }
+ b[n] = 0; memcpy(ts, b, (size_t)n + 1); tv = (int64_t)fop_parse(b); tk = T_FLT; return;
+ }
+ { char *e; tv = (int64_t)strtoll(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; }
+ }
+ if (isalpha((unsigned char)src[pos]) || src[pos] == '_') {
+ i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++];
+ ts[i] = 0;
+ if (i == 1 && ts[0] == 'b' && src[pos] == '"') {
+ pos++; i = 0;
+ while (src[pos] && src[pos] != '"' && i < 4095) ts[i++] = (char)esc();
+ ts[i] = 0; if (src[pos] == '"') pos++; tk = T_BSTR; return;
+ }
+ tk = T_ID; return;
+ }
+ if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; }
+ if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; }
+ tk = T_OP;
+ for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; }
+ for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; }
+ ts[0] = src[pos++]; ts[1] = 0;
+}
+typedef struct { char *src; int pos, line, tk, glue; int64_t tv; char ts[4096]; int xf, xs; } Lex;
+static void lexsave(Lex *L) { L->src = src; L->pos = pos; L->line = line; L->tk = tk; L->glue = glue; L->tv = tv; memcpy(L->ts, ts, sizeof ts); L->xf = xf; L->xs = xs; }
+static void lexrest(Lex *L) { src = L->src; pos = L->pos; line = L->line; tk = L->tk; glue = L->glue; tv = L->tv; memcpy(ts, L->ts, sizeof ts); xf = L->xf; xs = L->xs; }
+static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); }
+static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; }
+static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } }
+static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; }
+
+/* ---- symbols ---- */
+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 *add(const char *n, int kind, int64_t val)
+{
+ Sym *s = find(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;
+}
+static void fnref(const char *n) /* push a function address, fixing it up later if needed */
+{
+ Sym *s = find(n);
+ if (!s) { s = add(n, K_FN, 0); s->defd = 0; }
+ if (s->kind != K_FN) die("not a function");
+ if (s->defd) { lit(s->val); return; }
+ fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0);
+}
+
+/* ---- locals ---- */
+typedef struct { char name[64]; int slot, arr, flt, str; } Loc;
+static Loc lo[1024]; static int nlo, nslot, infn;
+static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; }
+static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } }
+
+/* ---- operator tables ---- */
+static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(63); op(O_SHR); lit(1); op(O_ADD); }
+/* ±0 is false. a float becomes 0 or 1 before the integer test. */
+static void ftest(void) { if (!xf) return; emit(1200); emit(0); op(605); lit(1); op(18); xf = 0; }
+static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(63); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); }
+static int binprec(void)
+{
+ static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 };
+ static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 };
+ int i;
+ if (tk != T_OP) return 0;
+ for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i];
+ return 0;
+}
+static int fis(const char *o)
+{
+ return !strcmp(o, "+") || !strcmp(o, "-") || !strcmp(o, "*") || !strcmp(o, "/")
+ || !strcmp(o, "<") || !strcmp(o, ">") || !strcmp(o, "<=") || !strcmp(o, ">=")
+ || !strcmp(o, "==") || !strcmp(o, "!=");
+}
+static void fbinop(const char *o, int lf, int rf)
+{
+ if (!lf) { op(O_SWAP); op(608); op(O_SWAP); }
+ if (!rf) op(608);
+ if (!strcmp(o, "+")) { op(601); xf = 1; }
+ else if (!strcmp(o, "-")) { op(602); xf = 1; }
+ else if (!strcmp(o, "*")) { op(603); xf = 1; }
+ else if (!strcmp(o, "/")) { op(604); xf = 1; }
+ else if (!strcmp(o, "==")) { op(605); xf = 0; }
+ else if (!strcmp(o, "!=")) { op(605); lit(1); op(18); xf = 0; }
+ else if (!strcmp(o, "<")) { op(606); xf = 0; }
+ else if (!strcmp(o, ">")) { op(O_SWAP); op(606); xf = 0; }
+ else if (!strcmp(o, "<=")) { op(607); xf = 0; }
+ else if (!strcmp(o, ">=")) { op(O_SWAP); op(607); xf = 0; }
+ else xf = 0;
+}
+static int sis(const char *o)
+{
+ return !strcmp(o, "+") || !strcmp(o, "<") || !strcmp(o, ">") || !strcmp(o, "<=")
+ || !strcmp(o, ">=") || !strcmp(o, "==") || !strcmp(o, "!=");
+}
+static void sbinop(const char *o)
+{
+ if (!strcmp(o, "+")) { fnref("cat_s"); op(O_CALL); xs = 1; }
+ else if (!strcmp(o, "==")) { fnref("eq_s"); op(O_CALL); xs = 0; }
+ else if (!strcmp(o, "!=")) { fnref("eq_s"); op(O_CALL); lit(1); op(O_XOR); xs = 0; }
+ else if (!strcmp(o, "<")) { fnref("lt_s"); op(O_CALL); xs = 0; }
+ else if (!strcmp(o, ">")) { op(O_SWAP); fnref("lt_s"); op(O_CALL); xs = 0; }
+ else if (!strcmp(o, "<=")) { fnref("le_s"); op(O_CALL); xs = 0; }
+ else { op(O_SWAP); fnref("le_s"); op(O_CALL); xs = 0; }
+ xf = 0;
+}
+static void binop(const char *o)
+{
+ if (!strcmp(o, "*")) op(O_MUL);
+ else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); }
+ else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); }
+ else if (!strcmp(o, "+")) op(O_ADD);
+ else if (!strcmp(o, "-")) op(O_SUB);
+ else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); }
+ else if (!strcmp(o, ">>")) op(O_SHR);
+ else if (!strcmp(o, "<")) op(O_LT);
+ else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); }
+ else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); }
+ else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); }
+ else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); }
+ else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); }
+ else if (!strcmp(o, "&")) op(O_AND);
+ else if (!strcmp(o, "^")) op(O_XOR);
+ else if (!strcmp(o, "|")) op(O_OR);
+ else die("bad operator");
+}
+
+/* ---- constant expressions (globals, array sizes, const) ---- */
+static int64_t cexpr(void);
+static int64_t cprim(void)
+{
+ int64_t v; Sym *s;
+ if (tk == T_NUM) { v = tv; next(); return v; }
+ if (accept("-")) return -cprim();
+ if (accept("~")) return ~cprim();
+ if (accept("(")) { v = cexpr(); expect(")"); return v; }
+ if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; }
+ die("constant expected"); return 0;
+}
+static int64_t cexpr(void)
+{
+ int64_t v = cprim();
+ for (;;) {
+ if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim();
+ else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim();
+ else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim();
+ else if (accept(">>")) v >>= cprim(); else return v;
+ }
+}
+
+/* ---- 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
+ 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<T>*, point**
+ ptype1 a base: [n]T, []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);
+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("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; }
+ ident();
+ if (accept("<")) { /* generic arguments: vec<node*> */
+ do { ptype(); } while (accept(","));
+ if (is(">>")) ts[1] = 0; /* >> closes two lists: leave the second > */
+ else expect(">");
+ }
+ return 1;
+}
+static int ptype0(void)
+{
+ int n = ptype1();
+ while (accept("*")) n = 1;
+ return n;
+}
+static int ptype(void)
+{
+ int n = ptype0();
+ while (accept("(")) {
+ if (!accept(")")) { do { ptype(); } while (accept(",")); expect(")"); }
+ n = 1;
+ }
+ return n;
+}
+/* the ownership tag after a type, .u .m or .d: 0 when there is none */
+static int ptag;
+static void ptagopt(void)
+{
+ ptag = 0;
+ if (!accept(".")) return;
+ if (is("m")) ptag = 1; else if (is("d")) ptag = 2; else if (is("u")) ptag = 3; else die("a tag is .u, .m or .d");
+ next();
+}
+/* an optional type in front of a name. the words it needs, or -1 when there is
+ no type here at all, which is how an untyped name is told from a type. the
+ parse is taken back when what follows is not a name. */
+/* words that open a statement or an expression, so a type never starts with
+ one of them. they are all plain identifiers to the lexer. */
+static int notatype(void)
+{
+ static const char *w[] = { "return", "if", "else", "while", "for", "do", "break", "continue",
+ "let", "var", "fn", "const", "enhance", "struct", "enum", "asm", "switch", "case",
+ "default", "immediate", "type", "include", "as", "in", "new", "sizeof", "typeof",
+ "o3", "unchecked", 0 };
+ int i;
+ if (tk != T_ID) return 0;
+ for (i = 0; w[i]; i++) if (!strcmp(ts, w[i])) return 1;
+ return 0;
+}
+static int optype(void)
+{
+ Lex L; int n;
+ if (notatype() || (tk != T_ID && !is("["))) return -1;
+ lexsave(&L); pguess = 1; pbad = 0; n = ptype(); ptagopt(); pguess = 0;
+ if (tk == T_ID && !pbad) return n;
+ lexrest(&L);
+ return -1;
+}
+/* a name followed by ( : a function definition rather than a variable */
+static int isfn(void)
+{
+ Lex L; int r;
+ lexsave(&L); pguess = 1;
+ r = tk == T_ID;
+ if (r) { ident(); r = is("("); }
+ pguess = 0; lexrest(&L);
+ return r;
+}
+
+/* ---- expressions ---- */
+static int lv; /* the stack top is an address not yet loaded */
+static void rval(void) { if (lv) { op(O_LD); lv = 0; } }
+static void expr(void);
+static void strlit(void) /* inline word string: jump over it, push its address */
+{
+ int j = here, a, i, n = (int)strlen(ts);
+ lit(0); op(O_JMP); a = here;
+ for (i = 0; i < n; i++) emit((unsigned char)ts[i]);
+ emit(0); setlit(j, here); lit(a); next();
+}
+/* b"...": length word, then bytes packed 8 per word, little-endian, plus a 0 */
+static void bstrlit(void)
+{
+ int j = here, a, i = 0, k, n = (int)strlen(ts);
+ uint64_t w;
+ lit(0); op(O_JMP); a = here;
+ emit(n);
+ while (i < n) {
+ w = 0; k = 0;
+ while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)ts[i] << (k * 8); k++; i++; }
+ emit((int64_t)w);
+ }
+ if ((n & 7) == 0) emit(0);
+ setlit(j, here); lit(a); next();
+}
+static int returns_bstr(const char *n)
+{
+ 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; }
+static void asmblock(void);
+static void primary(void)
+{
+ char n[64]; Loc *l; Sym *s;
+ lv = 0; lvi = -1; xf = 0; xs = 0;
+ if (tk == T_FLT) { emit(1200); emit(tv); xf = 1; next(); return; }
+ if (tk == T_NUM) { lit(tv); next(); return; }
+ if (tk == T_BSTR) { bstrlit(); xs = 1; return; }
+ if (tk == T_STR) { strlit(); return; }
+ if (accept("(")) { expr(); expect(")"); return; }
+ if (is("asm")) { next(); asmblock(); return; }
+ strcpy(n, ident());
+ if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; if (!l->arr) { xf = l->flt; xs = l->str; lvi = (int)(l - lo); } return; }
+ s = find(n);
+ if (s && s->kind == K_CONST) { lit(s->val); return; }
+ if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; }
+ if (s && s->kind == K_GARR) { lit(s->val); return; }
+ if (is("(")) {
+ int na = args();
+ if (!strcmp(n, "put_s") && na == 1 && xs) fnref("put_bs");
+ else if (!strcmp(n, "len")) fnref("len_s");
+ else fnref(n);
+ op(O_CALL);
+ xs = returns_bstr(n); xf = 0;
+ return;
+ }
+ fnref(n);
+}
+static void incdec(int post, int d)
+{
+ if (!lv) die("++/-- needs an lvalue");
+ lv = 0;
+ if (lvi >= 0) lo[lvi].str = 0;
+ xs = 0;
+ if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); }
+ else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); }
+}
+static void postfix(void)
+{
+ int n, was;
+ primary();
+ for (;;) {
+ if (accept("[")) {
+ was = xs; rval(); expr(); rval();
+ if (was && accept(":")) {
+ expr(); rval(); expect("]"); fnref("slice_s"); op(O_CALL);
+ lv = 0; xs = 1; xf = 0; lvi = -1;
+ } else if (was) {
+ expect("]"); fnref("at_s"); op(O_CALL);
+ lv = 0; xs = 0; xf = 0; lvi = -1;
+ } else { expect("]"); op(O_ADD); lv = 1; xs = 0; xf = 0; lvi = -1; }
+ }
+ else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); xf = 0; xs = 0; lvi = -1; }
+ else if (accept("++")) incdec(1, 1);
+ else if (accept("--")) incdec(1, -1);
+ else if (accept("as")) ident();
+ else return;
+ }
+}
+static void unary(void)
+{
+ if (accept("-")) {
+ unary(); rval(); xs = 0;
+ if (xf) { emit(1200); emit(0); op(O_SWAP); op(602); }
+ else { lit(0); op(O_SWAP); op(O_SUB); }
+ }
+ else if (accept("!")) { unary(); rval(); ftest(); seq_isz(); xf = 0; xs = 0; }
+ else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); xf = 0; xs = 0; }
+ else if (accept("*")) { unary(); rval(); lv = 1; xs = 0; }
+ else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; xs = 0; }
+ else if (accept("++")) { unary(); incdec(0, 1); }
+ else if (accept("--")) { unary(); incdec(0, -1); }
+ else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */
+ else postfix();
+}
+static void bin(int minp)
+{
+ int p, a, b, lf, rf, ls, rs; char o[8];
+ unary();
+ while ((p = binprec()) && p >= minp) {
+ lf = xf; ls = xs; strcpy(o, ts); next(); rval();
+ if (!strcmp(o, "&&")) {
+ ftest(); a = here; lit(0); op(O_JZ); bin(p + 1); rval(); ftest(); seq_nz();
+ b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); xf = 0; xs = 0;
+ } else if (!strcmp(o, "||")) {
+ ftest(); a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP);
+ setlit(a, here); bin(p + 1); rval(); ftest(); seq_nz(); setlit(b, here); xf = 0; xs = 0;
+ } else {
+ bin(p + 1); rval(); rf = xf; rs = xs;
+ if ((ls || rs) && sis(o)) {
+ if (!(ls && rs)) die("a byte string needs a byte string on the other side");
+ sbinop(o);
+ } else if ((lf || rf) && fis(o)) { fbinop(o, lf, rf); xs = 0; }
+ else { binop(o); xf = 0; xs = 0; }
+ }
+ lv = 0; lvi = -1;
+ }
+}
+static void expr(void)
+{
+ char o[8]; int dst, lf;
+ bin(1); dst = lvi;
+ if (is("=")) {
+ if (!lv) die("assignment needs an lvalue");
+ next(); expr(); rval();
+ op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0;
+ if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs; }
+ } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) {
+ if (!lv) die("assignment needs an lvalue");
+ lf = xf; strcpy(o, ts); o[strlen(o) - 1] = 0; next();
+ lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval();
+ if ((lf || xf) && fis(o)) fbinop(o, lf, xf);
+ else { binop(o); xf = 0; }
+ op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0;
+ if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = 0; }
+ }
+}
+
+/* ---- asm blocks: raw vm words ---- */
+static void asmblock(void)
+{
+ char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c;
+ Loc *l; Sym *s;
+ expect("{");
+ while (!accept("}")) {
+ if (tk == T_EOF) die("unterminated asm");
+ if (tk == T_FLT) { emit(1200); emit(tv); next(); continue; }
+ if (tk == T_NUM) { lit(tv); next(); continue; }
+ if (tk == T_BSTR) { bstrlit(); continue; }
+ if (tk == T_STR) { strlit(); continue; }
+ if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; }
+ if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; }
+ if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; }
+ if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; }
+ if (tk != T_ID) die("bad asm token");
+ if ((l = findl(ts))) { laddr(l->slot); next(); continue; }
+ if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; }
+ if (s) { fnref(ts); next(); continue; }
+ strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */
+ }
+ for (i = 0; i < nr; i++) {
+ for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ;
+ if (k == nl) die("unknown asm label");
+ setlit(ra[i], la[k]);
+ }
+}
+
+/* ---- statements ---- */
+static int brk[256][64], nbrk[256], cont[256], nloop;
+static int epi[1024], nepi;
+static void stmt(void);
+static void block(void)
+{
+ int save = nlo;
+ expect("{");
+ while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); }
+ nlo = save;
+}
+static void jmp_to(int t) { lit(t); op(O_JMP); }
+static void loopbody(int contaddr)
+{
+ nbrk[nloop] = 0; cont[nloop++] = contaddr;
+ stmt();
+ nloop--;
+}
+static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); }
+static void stmt(void)
+{
+ int a, b, c, d;
+ if (accept(";")) return;
+ if (is("{")) { block(); return; }
+ {
+ int nw = optype(), n;
+ if (nw >= 0 || accept("let") || accept("var")) {
+ if (nw < 0) nw = 1;
+ do {
+ Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0;
+ if (nw > 1) { l->slot = nslot; nslot += nw; l->arr = 1; }
+ else {
+ if (accept("[")) { n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; }
+ else l->slot = nslot++;
+ }
+ l->flt = 0; l->str = 0;
+ if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs; }
+ } while (accept(","));
+ expect(";"); return;
+ }
+ }
+ if (accept("unchecked")) { stmt(); return; }
+ if (accept("if")) {
+ expect("("); expr(); rval(); ftest(); expect(")");
+ a = here; lit(0); op(O_JZ); stmt();
+ if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); }
+ else setlit(a, here);
+ return;
+ }
+ if (accept("while")) {
+ c = here; expect("("); expr(); rval(); ftest(); expect(")");
+ a = here; lit(0); op(O_JZ);
+ loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return;
+ }
+ if (accept("for")) {
+ int save = nlo;
+ expect("(");
+ if (!is(";")) { if (is("let") || is("var") || tk == T_ID || is("[")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); }
+ expect(";");
+ cond: c = here;
+ if (!is(";")) { expr(); rval(); ftest(); } else { lit(1); xf = 0; }
+ expect(";");
+ a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP);
+ d = here;
+ if (!is(")")) { expr(); rval(); op(O_DROP); }
+ expect(")"); jmp_to(c);
+ setlit(b, here);
+ loopbody(d); jmp_to(d); setlit(a, here); patchbreaks();
+ nlo = save; return;
+ }
+ if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; }
+ if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; }
+ if (accept("return")) {
+ if (!is(";")) { expr(); rval(); } else lit(0);
+ expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return;
+ }
+ if (is("asm")) { next(); asmblock(); return; }
+ expr(); rval(); op(O_DROP); expect(";");
+}
+
+/* ---- top level ---- */
+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);
+ if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; }
+ expect("(");
+ if (!accept(")")) {
+ do {
+ t = 0; ptag = 0;
+ if (optype() >= 0) t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; /* the type is read and dropped */
+ else if (accept(".")) { /* .m name, .d name */
+ if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d");
+ next();
+ }
+ if (np < 12) tags += t << (2 * np);
+ strcpy(pn[np++], ident());
+ } while (accept(","));
+ expect(")");
+ }
+ s->sig = ret + 2 * np + 64 * tags;
+ if (accept(";")) return; /* prototype */
+ if (s->defd) die("function defined twice");
+ s->val = here; s->defd = 1;
+#ifdef ZC0DOCS
+ { /* the header is the source from the item's first token to the { that opens the body */
+ int hl = tstart - hdr_start;
+ sgname[0] = 0;
+ if (hl > 0 && hl < (int)sizeof sgtext - 1) {
+ memcpy(sgtext, src + hdr_start, hl); sgtext[hl] = 0;
+ while (hl && isspace((unsigned char)sgtext[hl - 1])) sgtext[--hl] = 0;
+ strcpy(sgname, n);
+ }
+ }
+#endif
+ 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");
+ 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) {
+ lit(find("frame_busy")->val); op(O_LD); busy = here; lit(0); op(O_JZ);
+ done = here; lit(0); op(O_JMP); setlit(busy, here);
+ lit(ZFP); op(O_LD); lit(find("frame_floor")->val); op(O_LD);
+ lit(find("frame_guard")->val); op(O_LD); op(O_ADD); op(O_LT); check = here; lit(0); op(O_JZ);
+ lit(ZFP); op(O_LD); lit(1); op(O_PICK); lit(ZFP); op(O_ST);
+ fnref("frame_grow"); op(O_CALL); lit(ZFP); op(O_ST);
+ busy = here; lit(0); op(O_JMP);
+ setlit(check, here); setlit(done, here); op(O_DROP); setlit(busy, here);
+ }
+ for (i = np; i--;) { laddr(i); op(O_ST); }
+ block();
+ lit(0);
+ for (i = 0; i < nepi; i++) setlit(epi[i], here);
+ lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST);
+ if (!helper) {
+ lit(find("frame_node")->val); op(O_LD); node = here; lit(0); op(O_JZ);
+ lit(ZFP); op(O_LD); lit(find("frame_top")->val); op(O_LD); op(O_SUB); check = here; lit(0); op(O_JZ);
+ done = here; lit(0); op(O_JMP); setlit(check, here);
+ fnref("frame_leave"); op(O_CALL); lit(ZFP); op(O_ST); setlit(done, here); setlit(node, here);
+ }
+ op(O_RET);
+ setlit(frame, nslot);
+ infn = 0;
+}
+/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm };
+ emits the install descriptor; the booted genome installs it */
+static int nenh;
+static void enhdef(void)
+{
+ char *n, pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s;
+ if (tk == T_NUM) { nout = tv; next(); } /* enhance 2 name(...) */
+ n = ident();
+ expect("(");
+ if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); }
+ d = here; for (i = 0; i < 7; i++) emit(0);
+ na = here; for (i = 0; i < np; i++) emit(0);
+ for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); }
+ if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); }
+ else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; }
+ else die("expected micro or vm");
+ img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout;
+ img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na;
+ s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256;
+ expect(";");
+}
+static void top1(void)
+{
+ {
+ if (accept("unchecked")) { top1(); return; }
+ if (accept("fn")) { fndef(0); return; }
+ if (accept(".")) { if (strcmp(ident(), "u")) die("the only return tag is .u"); fndef(1); return; } /* .u name(...) */
+ if (accept("const")) { optype(); do { char *n = ident(); if (dsn && !dnm[0]) strcpy(dnm, n); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); return; }
+ if (accept("enhance")) { enhdef(); return; }
+ {
+ int nw, typed, k, a, rt;
+ ptag = 0; nw = optype(); typed = nw >= 0; rt = ptag == 3;
+ if (typed || accept("let") || accept("var")) {
+ if (isfn()) { fndef(rt); return; }
+ do {
+ char *n = ident();
+ if (dsn && !dnm[0]) strcpy(dnm, n);
+ k = 0;
+ if (typed) k = nw > 1 ? nw : 0;
+ else if (accept("[")) { k = (int)cexpr(); expect("]"); }
+ if (k > 1) {
+ a = here; while (k--) emit(0); add(n, K_GARR, a);
+ } else {
+ a = here; add(n, K_GVAR, a); emit(0);
+ if (accept("=")) {
+ if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); }
+ else img[a - GORG] = cexpr();
+ }
+ }
+ } while (accept(","));
+ expect(";"); return;
+ }
+ }
+ die("expected fn, var or const");
+ }
+}
+
+/* one item, with the doc comment above it kept under the name it declares */
+static void top(void)
+{
+ while (tk != T_EOF) {
+ dsn = 0; dnm[0] = 0;
+#ifdef ZC0DOCS
+ sgname[0] = 0; hdr_start = tstart;
+#endif
+ 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]) {
+ int j = here, a, i = 0, k, c; uint64_t w; char nm[80];
+ lit(0); op(O_JMP); a = here;
+ emit(dsn);
+ while (i < dsn) {
+ w = 0; k = 0;
+ while (k < 8 && i < dsn) { c = (unsigned char)dsnap[i]; if (c > 255) c = 63; w |= (uint64_t)c << (k * 8); k++; i++; }
+ emit((int64_t)w);
+ }
+ if (!(dsn & 7)) emit(0);
+ setlit(j, here);
+ sprintf(nm, "doc %s", dnm);
+ add(nm, K_DOC, a);
+ }
+ if (sgname[0]) { /* the signature, as the header's own text, under "sig name" */
+ int j = here, a, i = 0, k, c, sl = (int)strlen(sgtext); uint64_t w; char nm[96];
+ lit(0); op(O_JMP); a = here;
+ emit(sl);
+ while (i < sl) {
+ w = 0; k = 0;
+ while (k < 8 && i < sl) { c = (unsigned char)sgtext[i]; w |= (uint64_t)c << (k * 8); k++; i++; }
+ emit((int64_t)w);
+ }
+ if (!(sl & 7)) emit(0);
+ setlit(j, here);
+ sprintf(nm, "sig %s", sgname);
+ add(nm, K_DOC, a);
+ }
+#endif
+ dsn = 0; dnm[0] = 0;
+ }
+}
+
+int main(void)
+{
+#ifdef _WIN32
+ _setmode(_fileno(stdin), _O_BINARY);
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+ char *buf = malloc(65536); int capacity = 65536, n = 0, i, j; Sym *st;
+ if (!buf) { fprintf(stderr, "zc0: cannot allocate source buffer\n"); return 1; }
+ for (;;) {
+ size_t got;
+ if (n + 1 == capacity) {
+ char *next;
+ if (capacity > 0x7fffffff / 2) { fprintf(stderr, "zc0: source is too large\n"); return 1; }
+ capacity *= 2; next = realloc(buf, (size_t)capacity);
+ if (!next) { fprintf(stderr, "zc0: cannot grow source buffer\n"); return 1; }
+ buf = next;
+ }
+ got = fread(buf + n, 1, (size_t)(capacity - n - 1), stdin); n += (int)got;
+ if (!got) { if (ferror(stdin)) { fprintf(stderr, "zc0: source read failed\n"); return 1; } break; }
+ }
+ 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);
+ top();
+ for (i = 0; i < nfx; i++) {
+ Sym *s = &sy[fx[i].sym];
+ if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; }
+ setlit(fx[i].at, s->val);
+ }
+ if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; }
+ /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */
+ img[5] = here;
+ for (i = 0; i < nsy; i++) {
+ emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig);
+ for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]);
+ emit(0);
+ }
+ emit(0);
+ img[6] = here;
+ printf("#define GORG %d\n#define GLEN %d\nstatic const int64_t genome[] = {", GORG, here - GORG);
+ for (i = 0; i < here - GORG; i++) printf("%s%lld", !i ? "\n" : i % 12 ? "," : ",\n", (long long)img[i]);
+ printf("\n};\n");
+ return 0;
+}