diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/zc0.c | 175 |
1 files changed, 125 insertions, 50 deletions
diff --git a/src/tools/zc0.c b/src/tools/zc0.c index b0ab29f..9faf883 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -4,8 +4,10 @@ the same image layout the resident compiler's image mode produces. the cores embed that file with #embed (src/core/genome.h). 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. */ + (.m and .d on parameters, .u on a return) are parsed and recorded in + signatures but not checked. u, m and d are ordinary names, as in the + resident compiler: a recast is the letter with the star touching it, u*x, + and it makes no code. */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> @@ -176,7 +178,10 @@ static void next(void) 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(); + while (src[pos] && src[pos] != '"') { + if (i >= 4095) { die("a byte string literal is at most 4095 bytes; embed larger data"); break; } + 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"))) { @@ -187,7 +192,14 @@ static void next(void) 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; } + if (src[pos] == '"') { + pos++; i = 0; + while (src[pos] && src[pos] != '"') { + if (i >= 4095) { die("a string literal is at most 4095 bytes"); break; } + 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; } @@ -200,9 +212,16 @@ static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, 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; } +/* u, m and d are ordinary names. u*x, m*x and d*x are recasts when the star touches + the letter, which the raw character after the name tells: u * x is a product, and + so is u*=x. the current token is the name, so pos is just past it. */ +static int isrecast(void) +{ + return tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '='; +} /* ---- symbols ---- */ -typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string */ +typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string, or a function returning one */ static Sym sy[16384]; static int nsy; typedef struct { int at, sym; } Fix; static Fix fx[65536]; static int nfx; @@ -237,7 +256,7 @@ static void fnref(const char *n) /* push a function address, fixing it up later } /* ---- locals ---- */ -typedef struct { char name[64]; int slot, arr, flt, str; } Loc; +typedef struct { char name[64]; int slot, arr, flt, str, dstr; } Loc; /* dstr: declared bstr, so always a byte string */ 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); } } @@ -349,25 +368,32 @@ static int64_t cexpr(void) for a growable handle or anything scalar. */ static int ptype0(void); static int ptype(void); +static int ptagname(void); +static int ptstr; /* the type just read is bstr itself, with no stars, brackets or parameters after it */ static int ptype1(void) { - int n, e; + int n, e, isb; + ptstr = 0; 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; } + isb = !strcmp(ts, "bstr"); ident(); if (accept("<")) { /* generic arguments: vec<node*> */ + isb = 0; do { ptype(); } while (accept(",")); if (is(">>")) ts[1] = 0; /* >> closes two lists: leave the second > */ else expect(">"); } + ptstr = isb; return 1; } static int ptype0(void) { int n = ptype1(); while (is("*") || is("[")) { + ptstr = 0; if (accept("*")) n = 1; else { int count = 0, cap = 8, *dims = malloc(cap * sizeof(int)); @@ -390,19 +416,27 @@ static int ptype(void) { int n = ptype0(); while (accept("(")) { - if (!accept(")")) { do { ptype(); if (accept(".")) ident(); } while (accept(",")); expect(")"); } - n = 1; + if (!accept(")")) { do { ptype(); if (accept(".")) ptagname(); } while (accept(",")); expect(")"); } + n = 1; ptstr = 0; } return n; } +/* the name of an ownership tag, after its dot: 1 for m, 2 for d, 3 for u. zc0 does + nothing with them but record .m and .d on parameters and .u on a return. */ +static int ptagname(void) +{ + int t = 0; + if (is("m")) t = 1; else if (is("d")) t = 2; else if (is("u")) t = 3; else die("a tag is .u, .m or .d"); + next(); + return t; +} /* 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(); + ptag = ptagname(); } /* 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 @@ -423,7 +457,7 @@ static int notatype(void) static int optype(void) { Lex L; int n; - if (notatype() || (tk != T_ID && !is("["))) return -1; + if (notatype() || isrecast() || (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); @@ -451,20 +485,26 @@ static void strlit(void) /* inline word string: jump over it, push its address * 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) +/* a byte string's words: the length, then the bytes packed 8 per word, little-endian, + and a 0 after the last byte when the length is a multiple of 8, as bstrdata does in + compiler.zc. the result is the address of the length word, which is the string. */ +static int bstrwords(const unsigned char *b, long n) { - int j = here, a, i = 0, k, n = (int)strlen(ts); - const char *b = ts; - uint64_t w; - lit(0); op(O_JMP); a = here; + int a = here; long i = 0; int k; uint64_t w; emit(n); while (i < n) { w = 0; k = 0; - while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)b[i] << (k * 8); k++; i++; } + while (k < 8 && i < n) { w |= (uint64_t)b[i] << (k * 8); k++; i++; } emit((int64_t)w); } if ((n & 7) == 0) emit(0); + return a; +} +/* b"...": the words inline, jumped over, then the address pushed */ +static void bstrlit(void) +{ + int j = here, a; + lit(0); op(O_JMP); a = bstrwords((const unsigned char *)ts, (long)strlen(ts)); setlit(j, here); lit(a); next(); } static int returns_bstr(const char *n) @@ -503,7 +543,7 @@ static void primary(void) else if (!strcmp(n, "len")) fnref("len_s"); else fnref(n); op(O_CALL); - xs = returns_bstr(n); xf = 0; + xs = returns_bstr(n) || (s && s->kind == K_FN && s->str); xf = 0; return; } fnref(n); @@ -512,7 +552,7 @@ static void incdec(int post, int d) { if (!lv) die("++/-- needs an lvalue"); lv = 0; - if (lvi >= 0) lo[lvi].str = 0; + if (lvi >= 0) lo[lvi].str = lo[lvi].dstr; 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); } @@ -552,7 +592,7 @@ static void unary(void) 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 if (isrecast()) { next(); expect("*"); unary(); rval(); lvi = -1; } /* u*x: a recast, no code, and not a place to store */ else postfix(); } static void bin(int minp) @@ -586,7 +626,7 @@ static void expr(void) 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; } + if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs || lo[dst].dstr; } } 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(); @@ -594,7 +634,7 @@ static void expr(void) 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; } + if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = lo[dst].dstr; } } } @@ -616,6 +656,7 @@ static void asmblock(void) 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_ENH) { op(22 + (int)s->val); next(); continue; } /* an enhancement is its opcode */ 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 */ @@ -631,6 +672,7 @@ static void asmblock(void) static int brk[256][64], nbrk[256], cont[256], nloop; static int epi[1024], nepi; static void stmt(void); +static void embeddecl(int local); static void block(void) { int save = nlo; @@ -652,7 +694,14 @@ static void stmt(void) if (accept(";")) return; if (is("{")) { block(); return; } { - int nw = optype(), n; + int nw = optype(), n, tstr = 0; + if (nw >= 0) tstr = ptstr; + else if (is(".")) { next(); ptagname(); nw = 1; } /* a bare tag: .m p = new(3); */ + else if (is("embed")) { /* embed "file" as [type] name; makes a local */ + Lex L; lexsave(&L); next(); + if (tk == T_STR) { embeddecl(1); return; } + lexrest(&L); + } if (nw >= 0 || accept("let") || accept("var")) { if (nw < 0) nw = 1; do { @@ -662,8 +711,8 @@ static void stmt(void) 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; } + l->flt = 0; l->dstr = tstr && !l->arr; l->str = l->dstr; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs || l->dstr; } } while (accept(",")); expect(";"); return; } @@ -684,7 +733,7 @@ static void stmt(void) 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); } + if (!is(";")) { if (is("let") || is("var") || tk == T_ID || is("[") || is(".")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } expect(";"); cond: c = here; if (!is(";")) { expr(); rval(); ftest(); } else { lit(1); xf = 0; } @@ -708,18 +757,18 @@ static void stmt(void) } /* ---- top level ---- */ -static void fndef(int ret) +static void fndef(int ret, int rstr) /* rstr: the return type is bstr */ { 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]; + char pn[16][64]; int pstr[16]; 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(")")) { do { - t = 0; ptag = 0; - if (optype() >= 0) t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; /* the type is read and dropped */ + t = 0; ptag = 0; pstr[np & 15] = 0; + if (optype() >= 0) { t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; pstr[np & 15] = ptstr; } /* 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(); @@ -729,7 +778,7 @@ static void fndef(int ret) } while (accept(",")); expect(")"); } - s->sig = ret + 2 * np + 64 * tags; + s->sig = ret + 2 * np + 64 * tags; s->str = rstr; if (accept(";")) return; /* prototype */ if (s->defd) die("function defined twice"); s->val = here; s->defd = 1; @@ -745,7 +794,7 @@ static void fndef(int ret) } #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++; } + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].dstr = lo[nlo].str = pstr[i & 15]; lo[nlo++].slot = nslot++; } /* retain the caller's pointer until the frame has its final segment. */ 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); } @@ -798,6 +847,9 @@ static void enhdef(void) } /* embed "path" as [type] name; at the top level: a global holding the file's bytes, read when the image is built. the path is relative to the working directory. + in a function it declares a local: the bytes of a bstr or char* sit in the code, + jumped over, and the local starts as their address; a number is stored in the + local. an array takes the top level. bstr (the default) packs the bytes; char* keeps one character to a word; a number takes them little-endian; T[] and T[n] have one element per file chunk of T's size. the checks are the resident compiler's (kw_embed). zc0 drops @@ -813,9 +865,14 @@ static int embed_width(const char *t, int star) if (!strcmp(t, "int") || !strcmp(t, "word") || !strcmp(t, "bool") || !strcmp(t, "float") || !strcmp(t, "f64")) return 8; return 0; } -static void embeddecl(void) +static Loc *newloc(const char *n) { - char path[1024], ty[64] = "", *n; int star = 0, arr = -2, w, i, a; long len; unsigned char *buf; FILE *f; + Loc *l = &lo[nlo++]; strcpy(l->name, n); l->slot = nslot++; l->arr = l->flt = l->str = l->dstr = 0; + return l; +} +static void embeddecl(int local) +{ + char path[1024], ty[64] = "", *n; int star = 0, arr = -2, w, i, a, j; long len; unsigned char *buf; FILE *f; Loc *l; if (tk != T_STR) die("embed takes a path in quotes: embed \"file\" as name;"); snprintf(path, sizeof path, "%s", ts); next(); expect("as"); @@ -835,13 +892,23 @@ static void embeddecl(void) if (!buf || fread(buf, 1, (size_t)len, f) != (size_t)len) die("can't read the file named by embed"); fclose(f); if (arr == -2 && (!ty[0] || (!strcmp(ty, "bstr") && !star))) { - a = here; add(n, K_GVAR, a)->str = 1; emit(0); img[a - GORG] = here; emit(len); - for (i = 0; i < len; i += 8) { uint64_t u = 0; for (w = 0; w < 8 && i + w < len; w++) u |= (uint64_t)buf[i + w] << (w * 8); emit((int64_t)u); } - if (!(len & 7)) emit(0); + if (local) { + j = here; lit(0); op(O_JMP); a = bstrwords(buf, len); setlit(j, here); + l = newloc(n); l->str = l->dstr = 1; lit(a); laddr(l->slot); op(O_ST); + } else { + a = here; add(n, K_GVAR, a)->str = 1; emit(0); w = bstrwords(buf, len); img[a - GORG] = w; + } } else if (arr == -2 && !strcmp(ty, "char") && star == 1) { - a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here; - for (i = 0; i < len; i++) emit(buf[i]); - emit(0); + if (local) { + j = here; lit(0); op(O_JMP); a = here; + for (i = 0; i < len; i++) emit(buf[i]); + emit(0); setlit(j, here); + l = newloc(n); lit(a); laddr(l->slot); op(O_ST); + } else { + a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here; + for (i = 0; i < len; i++) emit(buf[i]); + emit(0); + } } else if (arr == -2) { uint64_t u = 0; w = embed_width(ty, star); if (!w) die("embed declares a bstr, a char*, a number or an array of numbers"); @@ -851,9 +918,15 @@ static void embeddecl(void) for (i = 0; i < len; i++) u |= (uint64_t)buf[i] << (i * 8); if (ty[0] == 'i' && w < 8 && (u >> (w * 8 - 1) & 1)) u |= ~(uint64_t)0 << (w * 8); if (!strcmp(ty, "f32")) u = fop_s2f(u); - a = here; add(n, K_GVAR, a); emit((int64_t)u); + if (local) { + l = newloc(n); l->flt = !strcmp(ty, "float") || !strcmp(ty, "f64") || !strcmp(ty, "f32"); + lit((int64_t)u); laddr(l->slot); op(O_ST); + } else { + a = here; add(n, K_GVAR, a); emit((int64_t)u); + } } else { int count; w = embed_width(ty, star); + if (local) die("zc0 embeds an array at the top level, not in a function"); if (!w) die("embed declares a bstr, a char*, a number or an array of numbers"); if (len % w) die("the embedded file's size is not a whole number of elements"); count = (int)(len / w); @@ -880,18 +953,19 @@ static void top1(void) expect(";"); return; } if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; } - if (accept("embed")) { embeddecl(); return; } + if (accept("embed")) { embeddecl(0); return; } { 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("fn")) { fndef(0, 0); return; } 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; + int nw, typed, k, a, rt, gstr = 0; Sym *gs; + ptag = 0; nw = optype(); typed = nw >= 0; if (typed) gstr = ptstr; else ptag = 0; + if (!typed && is(".")) { next(); ptag = ptagname(); typed = 1; nw = 1; } /* .u name(...), or a bare tag on a global */ + rt = ptag == 3; if (typed || accept("let") || accept("var")) { - if (isfn()) { fndef(rt); return; } + if (isfn()) { if (ptag && !rt) die("the only return tag is .u"); fndef(rt, gstr); return; } do { char *n = ident(); if (dsn && !dnm[0]) strcpy(dnm, n); @@ -901,9 +975,10 @@ static void top1(void) if (k > 1) { a = here; while (k--) emit(0); add(n, K_GARR, a); } else { - a = here; add(n, K_GVAR, a); emit(0); + a = here; gs = add(n, K_GVAR, a); gs->str = gstr; 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 if (tk == T_BSTR) { int d = bstrwords((const unsigned char *)ts, (long)strlen(ts)); img[a - GORG] = d; gs->str = 1; next(); } else img[a - GORG] = cexpr(); } } |
