aboutsummaryrefslogtreecommitdiff
path: root/bootstrap
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-03 20:24:47 +0200
committerlemon <lsof@mailbox.org>2022-08-03 20:24:47 +0200
commit1625c50f0c0e4b1c7ba01a5df5713efaf6dce606 (patch)
treebc5f24811413749b776964c1bbdec13a46dd9768 /bootstrap
initial
Diffstat (limited to 'bootstrap')
-rw-r--r--bootstrap/.gitignore4
-rw-r--r--bootstrap/all.h415
-rwxr-xr-xbootstrap/build.sh10
-rw-r--r--bootstrap/cgen.c417
-rw-r--r--bootstrap/dump.c384
-rw-r--r--bootstrap/env.c48
-rw-r--r--bootstrap/main.c14
-rw-r--r--bootstrap/parse.c1733
-rw-r--r--bootstrap/test.cff41
-rw-r--r--bootstrap/types.c208
-rw-r--r--bootstrap/util.c105
-rw-r--r--bootstrap/vec.c114
-rw-r--r--bootstrap/vec.h182
13 files changed, 3675 insertions, 0 deletions
diff --git a/bootstrap/.gitignore b/bootstrap/.gitignore
new file mode 100644
index 0000000..b5e3e58
--- /dev/null
+++ b/bootstrap/.gitignore
@@ -0,0 +1,4 @@
+compile_commands.json
+a.out
+cff0
+.cache/
diff --git a/bootstrap/all.h b/bootstrap/all.h
new file mode 100644
index 0000000..7f26372
--- /dev/null
+++ b/bootstrap/all.h
@@ -0,0 +1,415 @@
+#pragma once
+
+#include "vec.h"
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/***********/
+/** Types **/
+/***********/
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t i64;
+#define bool _Bool
+#define noreturn _Noreturn
+#define slice_t(T) struct { T *d; unsigned n; }
+
+struct span {
+ short fileid;
+ int idx, col, line;
+};
+
+/* must be alpha sorted */
+#define LIST_KEYWORDS(_) \
+ _(and) \
+ _(break) \
+ _(case) \
+ _(const) \
+ _(defmacro) \
+ _(do) \
+ _(else) \
+ _(extern) \
+ _(fn) \
+ _(for) \
+ _(if) \
+ _(let) \
+ _(not) \
+ _(or) \
+ _(return) \
+ _(switch) \
+ _(typedef) \
+ _(typeof) \
+ _(while) \
+
+enum toktype {
+#define KWTK(kw) TKkw_##kw,
+ LIST_KEYWORDS(KWTK)
+#undef KWTK
+ TKintlit,
+ TKflolit,
+ TKboolit,
+ TKstrlit,
+ TKchrlit,
+ TKnullit,
+ TKident,
+ TKgensym,
+ TKeof,
+ NUM_LEXTOKENS
+};
+#define NUM_KEYWORDS TKintlit
+
+struct tok {
+ int t;
+ struct span span;
+ union {
+ struct {
+ const struct type *ty;
+ u64 i;
+ } ilit;
+ struct {
+ const struct type *ty;
+ double f;
+ } flit;
+ bool boolit;
+ struct {
+ const char *str;
+ int strlen;
+ };
+ };
+};
+
+struct toktree {
+ slice_t(struct tok);
+};
+
+struct expanarg {
+ const char *name;
+ struct toktree toks;
+};
+
+struct parser {
+ FILE *fp;
+ const char *curfile;
+ struct span tokspan;
+ struct span curspan;
+ bool eof;
+ int peekchr;
+ bool have_peektok;
+ struct tok peektok;
+ struct env *primenv;
+ struct env *tlenv;
+ struct env *curenv;
+ struct fn *curfn;
+ struct expan {
+ struct expan *prev;
+ slice_t(struct expanarg) args;
+ struct toktree toks;
+ const char *name;
+ struct span spp,span;
+ int idx;
+ } *curexpan; // macro expansions
+ int expanno;
+};
+
+enum typetype {
+ TYvoid,
+ TYbool,
+ TYint,
+ TYfloat,
+ TYptr,
+ TYarr,
+ TYslice,
+ TYfn,
+};
+
+struct type {
+ enum typetype t;
+ size_t size, align;
+ bool konst;
+ union {
+ bool int_signed;
+ struct {
+ const struct type *child;
+ i64 length;
+ };
+ struct {
+ slice_t(const struct type *) params;
+ const struct type *retty;
+ bool variadic;
+ } fn;
+ };
+ // cgen.c (mutated later, not hashed or involved in equality)
+ const char *_cname;
+};
+
+struct fnparam {
+ const struct type *ty;
+ const char *name;
+};
+
+struct fn {
+ const char *name, *asmname;
+ slice_t(struct fnparam) params;
+ const struct type *selfty;
+ const struct type *retty;
+ bool variadic;
+ int id;
+ struct stmt *body;
+};
+
+struct macrocase {
+ bool variadic;
+ slice_t(const char *) params;
+ struct toktree body;
+};
+
+struct macro {
+ const char *name;
+ slice_t(struct macrocase) cs;
+};
+
+enum decltype {
+ Dtype,
+ Dfn,
+ Dvar,
+ Dmacro,
+};
+
+struct decl {
+ enum decltype t;
+ const char *name;
+ char **_cname;
+ bool externp;
+ struct span span;
+ union {
+ const struct type *ty;
+ struct fn fn;
+ struct {
+ const struct type *ty;
+ struct expr *ini;
+ int fnid;
+ } var;
+ struct macro macro;
+ };
+};
+
+struct env {
+ struct env *parent;
+ struct decls {
+ struct decls *next;
+ struct decl decl;
+ } *decls;
+};
+
+enum exprtype {
+ Eintlit,
+ Eflolit,
+ Estrlit,
+ Eboolit,
+ Enullit,
+ Ename,
+ Eprefix, Epostfix, // (unops)
+ Ebinop,
+ Econd,
+ Ecall,
+ Eindex,
+};
+
+struct expr {
+ enum exprtype t;
+ struct span span;
+ const struct type *ty;
+ union {
+ u64 i; // also for bool lit
+ double f;
+ struct {
+ const char *d;
+ u64 n;
+ } strlit;
+ const struct decl *ref;
+ struct {
+ int op; // operator token
+ struct expr *child;
+ } unop;
+ struct {
+ int op; // operator token
+ struct expr *lhs, *rhs;
+ } binop;
+ struct {
+ struct expr *test, *t, *f;
+ } cond;
+ struct {
+ struct expr *callee;
+ slice_t(struct expr) args;
+ } call;
+ struct {
+ struct expr *lhs, *rhs;
+ } index;
+ };
+};
+
+enum stmttype {
+ Sblock,
+ Sexpr,
+ Sdecl,
+ Sifelse,
+ Swhile,
+ Sfor,
+ Siswitch,
+ Sreturn,
+};
+
+struct blockstmt {
+ struct env env;
+ slice_t(struct stmt) stmts;
+};
+
+struct iswitchcase {
+ slice_t(struct expr) es;
+ struct blockstmt t;
+};
+
+struct stmt {
+ enum stmttype t;
+ union {
+ struct blockstmt block;
+ struct expr expr;
+ struct decl decl;
+ struct {
+ struct expr test;
+ struct blockstmt t;
+ struct stmt *f;
+ } ifelse;
+ struct {
+ struct stmt *ini;
+ struct expr test;
+ struct expr *next;
+ struct blockstmt body;
+ } loop;
+ struct {
+ struct expr test;
+ slice_t(struct iswitchcase) cs;
+ struct blockstmt *f;
+ } iswitch;
+ struct expr *retex;
+ };
+};
+
+struct transunit {
+ slice_t(struct decl) decls;
+};
+
+/************/
+/** Target **/
+/************/
+
+#define alignof __alignof__
+
+static const struct targ {
+ size_t ptrsize,
+ shortsize,
+ intsize,
+ longsize,
+ llongsize,
+ sizesize,
+ f32align,
+ f64align;
+ bool charsigned;
+} g_targ = {
+ .ptrsize = sizeof(void *),
+ .shortsize = sizeof(short),
+ .intsize = sizeof(int),
+ .longsize = sizeof(long),
+ .llongsize = sizeof(long long),
+ .sizesize = sizeof(size_t),
+ .f32align = alignof(float),
+ .f64align = alignof(double),
+ .charsigned = CHAR_MIN < 0,
+};
+
+/*********************************/
+/** Macros and inline functions **/
+/*********************************/
+
+#define ARRAY_LENGTH(a) (sizeof a / sizeof *a)
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define WITH_TMPCHANGE(T,var,new) \
+ for (T __old = (var), *__dummy = ((var) = (new), NULL); \
+ !__dummy; \
+ __dummy++, (var) = __old)
+
+static inline u32
+bswap32(u32 x) {
+ return (x >> 24)
+ | (x >> 8 & 0xFF00)
+ | (x << 8 & 0xFF0000)
+ | (x << 24 & 0xFF000000u);
+}
+
+static inline u64
+bswap64(u64 x) {
+ return ((u64)bswap32(x) << 32) | bswap32(x >> 32) ;
+}
+
+#define vec_slice_cpy(slice, v) \
+ ((slice)->d = vec_compact(v), \
+ (slice)->n = (v)->length)
+
+#define jkhashv(h, v) jkhash(h, (void *)&(v), sizeof((v)))
+
+/////////////////////////////////
+
+/** util.c **/
+u32 jkhash(u32 hash, const u8 *data, size_t length);
+int addfilepath(const char *);
+const char *fileid2path(int);
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+void *xrealloc(void *, size_t);
+char *xstrdup(const char *);
+char *xasprintf(const char *fmt, ...);
+void noreturn fatal(struct parser *, struct span, const char *fmt, ...);
+
+/** parse.c **/
+extern const char *keyword2str[];
+void parse(struct transunit *, struct parser *);
+void initparser(struct parser *, const char *fname);
+
+/** types.c **/
+extern const struct type
+ *ty_void, *ty_bool, *ty_f32, *ty_f64,
+ *ty_i8, *ty_u8, *ty_i16, *ty_u16,
+ *ty_i32, *ty_u32, *ty_i64, *ty_u64,
+ *ty_int, *ty_uint, *ty_isize, *ty_usize,
+ *ty_iptrint, *ty_uptrint, *ty_c_int, *ty_c_uint,
+ *ty_c_char, *ty_c_schar, *ty_c_uchar, *ty_c_short,
+ *ty_c_ushort, *ty_c_long, *ty_c_ulong, *ty_c_llong,
+ *ty_c_ullong;
+void inittypes(void);
+const struct type *interntype(struct type);
+bool typeeql(const struct type *lhs, const struct type *rhs);
+bool completetype(const struct type *);
+void putprimtypes(struct env *);
+void visittypes(void (*visitor)(const struct type *, void *), void *arg);
+
+/** env.c **/
+struct decl *envfind(const struct env *, const char *name);
+bool envput(struct env *, const struct decl *decl);
+struct env *mkenv(const struct env *parent);
+
+/** dump.c **/
+void dumptransunit(const struct transunit *);
+const char *tokt2str(int);
+const char *tok2str(struct tok);
+void pritoktree(struct toktree);
+
+/** cgen.c **/
+void cgen(FILE *, const struct transunit *);
diff --git a/bootstrap/build.sh b/bootstrap/build.sh
new file mode 100755
index 0000000..741f3aa
--- /dev/null
+++ b/bootstrap/build.sh
@@ -0,0 +1,10 @@
+#!/bin/env sh
+
+if [ x"$CC" == x ]; then
+ export CC=cc
+fi
+export CFLAGS="-Wall -Wno-multichar -g"
+
+set -x
+
+$CC $CFLAGS -o cff0 *.c
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
new file mode 100644
index 0000000..6445bf8
--- /dev/null
+++ b/bootstrap/cgen.c
@@ -0,0 +1,417 @@
+#include "all.h"
+
+static FILE *outfp;
+
+static void pri(const char *fmt, ...);
+
+static void
+gentype(const struct type *ty) {
+ assert(ty);
+ switch (ty->t) {
+ case TYvoid:
+ pri("void");
+ break;
+ case TYbool:
+ pri("bool");
+ break;
+ case TYint:
+ pri("%sint%z_t", ty->int_signed ? "" : "u",
+ 8 * ty->size);
+ break;
+ case TYfloat:
+ pri("%s", ty->size == 4 ? "float" : "double");
+ break;
+ case TYptr:
+ pri("%t *", ty->child);
+ break;
+ case TYarr:
+ assert(ty->length >= 0);
+ case TYslice:
+ assert(ty->_cname);
+ pri("%s", ty->_cname);
+ return;
+ case TYfn:
+ assert(ty->_cname);
+ pri("%s", ty->_cname);
+ return;
+ }
+ if (ty->konst)
+ pri(" const");
+}
+
+static void
+pristring(const char *s, u64 n) {
+ extern int isprint(int);
+ pri("\"");
+ for (int i = 0; i < n; ++i) {
+ if (isprint(s[i]))
+ pri("%c", s[i]);
+ else
+ fprintf(outfp, "\\%.3o", s[i]);
+ }
+ pri("\"");
+}
+
+static void genexpr(struct expr *expr);
+
+void
+pri(const char *fmt, ...) {
+ va_list ap;
+ unsigned ch;
+ const char *S;
+ int ws;
+
+ va_start(ap, fmt);
+ for (char c; (c = *fmt++);) {
+ if (c != '%') {
+ fputc(c, outfp);
+ continue;
+ }
+ switch ((c = *fmt++)) {
+ case '%':
+ fputc(c, outfp);
+ break;
+ case 'c':
+ ch = bswap32(va_arg(ap, int));
+ while (ch) {
+ if (ch & 0xFF)
+ fputc(ch, outfp);
+ ch >>= 8;
+ }
+ break;
+ case 'd':
+ fprintf(outfp, "%d", va_arg(ap, int));
+ break;
+ case 'U':
+ fprintf(outfp, "%llu", (unsigned long long)va_arg(ap, u64));
+ break;
+ case 'z':
+ fprintf(outfp, "%zu", va_arg(ap, size_t));
+ break;
+ case 'f':
+ fprintf(outfp, "%.20f", va_arg(ap, double));
+ break;
+ case 's':
+ fprintf(outfp, "%s", va_arg(ap, const char *));
+ break;
+ case 'w':
+ ws = va_arg(ap, int);
+ for (int i = 0; i < ws; ++i)
+ fprintf(outfp, " ");
+ break;
+ case 'S':
+ S = va_arg(ap, const char *);
+ pristring(S, va_arg(ap, u64));
+ break;
+ case 't':
+ gentype(va_arg(ap, const struct type *));
+ break;
+ case 'e':
+ genexpr(va_arg(ap, struct expr *));
+ break;
+ default:
+ fprintf(outfp, "pri(): bad fmt '%%%c'\n", c);
+ abort();
+ break;
+ }
+ }
+ va_end(ap);
+}
+
+static void genblock(struct blockstmt block);
+
+static void
+genexpr(struct expr *ex) {
+ const struct type *ty = ex->ty;
+ switch (ex->t) {
+ case Eintlit:
+ if (ty == ty_int)
+ pri("%U", ex->i);
+ else
+ pri("((%t)%U)", ty, ex->i);
+ break;
+ case Eflolit:
+ pri("%f%s", ex->f, ty->size == 4 ? "f" : "");
+ break;
+ case Estrlit:
+ pri("%S", ex->strlit.d, ex->strlit.n);
+ break;
+ case Eboolit:
+ pri("((_Bool)%U)", ex->i);
+ break;
+ case Enullit:
+ pri("NULL");
+ break;
+ case Ename:
+ if (ex->ref->t == Dfn && ex->ref->_cname && *ex->ref->_cname)
+ pri("%s", *ex->ref->_cname);
+ else
+ pri("%s", ex->ref->name);
+ break;
+ case Eprefix:
+ pri("%c(%e)", ex->unop.op, ex->unop.child);
+ break;
+ case Epostfix:
+ pri("(%e)%c", ex->unop.op, ex->unop.child);
+ break;
+ case Ebinop:
+ pri("(%e %c %e)", ex->binop.lhs, ex->binop.op, ex->binop.rhs);
+ break;
+ case Econd:
+ pri("(%e) ? (%e) : (%e)", ex->cond.test, ex->cond.t, ex->cond.f);
+ break;
+ case Ecall:
+ pri("%e(", ex->call.callee);
+ for (int i = 0; i < ex->call.args.n; ++i) {
+ pri("%e", &ex->call.args.d[i]);
+ if (i < ex->call.args.n - 1)
+ pri(", ");
+ }
+ pri(")");
+ break;
+ case Eindex:
+ pri("%e[%e]", ex->index.lhs, ex->index.rhs);
+ break;
+
+ }
+}
+
+static void genfn(const char *cname, struct fn *fn);
+
+static void
+genstmt(struct stmt *stmt) {
+ switch (stmt->t) {
+ case Sblock:
+ genblock(stmt->block);
+ break;
+ case Sexpr:
+ genexpr(&stmt->expr);
+ pri(";\n");
+ break;
+ case Sdecl:
+ ; struct decl decl = stmt->decl;
+ switch (decl.t) {
+ case Dvar:
+ pri("%t %s", decl.var.ty, decl.name);
+ if (decl.var.ini)
+ pri(" = %e", decl.var.ini);
+ pri(";\n");
+ break;
+ case Dfn:
+ if (decl.externp)
+ genfn(decl.fn.name, &decl.fn);
+ break;
+ case Dtype: case Dmacro:
+ break;
+ }
+ break;
+ case Sifelse:
+ pri("if (%e) ", &stmt->ifelse.test);
+ genblock(stmt->ifelse.t);
+ if (stmt->ifelse.f) {
+ pri("else ");
+ genstmt(stmt->ifelse.f);
+ }
+ break;
+ case Swhile:
+ pri("while (%e) ", &stmt->loop.test);
+ genblock(stmt->loop.body);
+ break;
+ case Sfor:
+ pri("for (");
+ if (stmt->loop.ini)
+ genstmt(stmt->loop.ini);
+ pri("%e;", &stmt->loop.test);
+ if (stmt->loop.next)
+ pri(" %e", &stmt->loop.next);
+ pri(")");
+ genblock(stmt->loop.body);
+ case Siswitch:
+ break;
+ case Sreturn:
+ pri("return");
+ if (stmt->retex)
+ pri(" %e", stmt->retex);
+ pri(";\n");
+ break;
+ }
+}
+
+static void
+genblock(struct blockstmt block) {
+ pri("{\n");
+ for (int i = 0; i < block.stmts.n; ++i)
+ genstmt(&block.stmts.d[i]);
+ pri("}\n");
+}
+
+static void
+liftnestedex(struct expr *expr) {
+ // TODO implement when statement expressions exist
+ //switch (expr->t) {
+ //}
+}
+
+#define blocktostmt(Block) \
+ &(struct stmt) {Sblock, .block = Block}
+
+static void // lift nested fns and static vars
+liftnested(struct stmt *stmt) {
+ static int id;
+ if (!stmt)
+ return;
+ switch (stmt->t) {
+ case Sblock:
+ for (int i = 0; i < stmt->block.stmts.n; ++i)
+ liftnested(&stmt->block.stmts.d[i]);
+ break;
+ case Sexpr:
+ liftnestedex(&stmt->expr);
+ break;
+ case Sdecl:
+ switch (stmt->decl.t) {
+ case Dfn:
+ if (stmt->decl.fn.body) {
+ *stmt->decl._cname = xasprintf("__f%s_%d", stmt->decl.fn.name, id++);
+ genfn(*stmt->decl._cname, &stmt->decl.fn);
+ }
+ break;
+ case Dvar:
+ liftnestedex(stmt->decl.var.ini);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Sifelse:
+ liftnested(stmt->ifelse.f);
+ liftnested(blocktostmt(stmt->ifelse.t));
+ break;
+ case Swhile:
+ liftnested(blocktostmt(stmt->loop.body));
+ break;
+ case Sfor:
+ liftnested(stmt->loop.ini);
+ liftnestedex(&stmt->loop.test);
+ liftnestedex(stmt->loop.next);
+ liftnested(blocktostmt(stmt->loop.body));
+ break;
+ case Siswitch:
+ liftnestedex(&stmt->iswitch.test);
+ for (int i = 0; i < stmt->iswitch.cs.n; ++i) {
+ // not visiting constant exprs
+ liftnested(blocktostmt(stmt->iswitch.cs.d[i].t));
+ }
+ if (stmt->iswitch.f)
+ liftnested(blocktostmt(*stmt->iswitch.f));
+ break;
+ case Sreturn:
+ liftnestedex(stmt->retex);
+ break;
+ }
+}
+
+static void
+genfn(const char *cname, struct fn *fn) {
+ liftnested(fn->body);
+ if (fn->body)
+ pri("\n");
+ pri("%t %s(", fn->retty, cname);
+ for (int i = 0; i < fn->params.n; ++i) {
+ pri("%t %s", fn->params.d[i].ty, fn->params.d[i].name);
+ if (i < fn->params.n - 1 || fn->variadic)
+ pri(", ");
+ }
+ if (fn->variadic)
+ pri("...");
+ pri(")");
+ if (!fn->body) {
+ pri(";\n");
+ } else {
+ genblock(fn->body->block);
+ }
+
+}
+
+static void
+gendecl(struct decl *decl, bool toplevel) {
+ switch (decl->t) {
+ case Dfn:
+ if (!decl->externp)
+ pri("static ");
+ if (decl->fn.body)
+ assert(toplevel);
+ if (!*decl->_cname)
+ *decl->_cname = (char *)decl->fn.name;
+ genfn(*decl->_cname, &decl->fn);
+ break;
+ case Dvar:
+ assert(!toplevel);
+ break;
+ case Dtype:
+ case Dmacro:
+ break;
+ }
+}
+
+static void
+defctype(const struct type *ty, void *_) {
+ static int id;
+ char **cname = (char **)&ty->_cname;
+
+ if (!ty->_cname)
+ switch (ty->t) {
+ case TYvoid: case TYbool:
+ case TYint: case TYfloat:
+ case TYptr:
+ return;
+ case TYarr:
+ assert(ty->length >= 0);
+ defctype(ty->child, NULL);
+ *cname = xasprintf("__ty%d", id++);
+ pri("typedef %t %s[%U];\n", ty->child, *cname, ty->length);
+ break;
+ case TYslice:
+ defctype(ty->child, NULL);
+ *cname = xasprintf("__ty%d", id++);
+ pri("typedef struct { %t *ptr; size_t len; } %s;\n",
+ ty->child, *cname);
+ break;
+ case TYfn:
+ *cname = xasprintf("__ty%d", id++);
+ defctype(ty->fn.retty, NULL);
+ for (int i = 0; i < ty->fn.params.n; ++i)
+ defctype(ty->fn.params.d[i], NULL);
+ pri("typedef %t %s(", ty->fn.retty, *cname);
+ for (int i = 0; i < ty->fn.params.n; ++i) {
+ pri("%t", ty->fn.params.d[i]);
+ if (i < ty->fn.params.n - 1 || ty->fn.variadic)
+ pri(", ");
+ }
+ if (ty->fn.variadic)
+ pri("...");
+ pri(");\n");
+
+
+ break;
+ }
+
+}
+
+static void
+prelude() {
+ pri("#include <stdint.h>\n"
+ "#include <stddef.h>\n");
+}
+
+void
+cgen(FILE *fp, const struct transunit *tu) {
+ outfp = fp;
+
+ prelude();
+ visittypes(defctype, NULL);
+
+ for (int i = 0; i < tu->decls.n; ++i) {
+ gendecl(&tu->decls.d[i], 1);
+ }
+}
diff --git a/bootstrap/dump.c b/bootstrap/dump.c
new file mode 100644
index 0000000..e4f35c9
--- /dev/null
+++ b/bootstrap/dump.c
@@ -0,0 +1,384 @@
+#include "all.h"
+#include <stdarg.h>
+
+static void pri(const char *fmt, ...);
+
+static void
+pritype(const struct type *ty) {
+ assert(ty);
+ if (ty->konst)
+ pri("const ");
+ switch (ty->t) {
+ case TYvoid:
+ pri("void");
+ break;
+ case TYbool:
+ pri("bool");
+ break;
+ case TYint:
+ pri("%c%z", ty->int_signed ? 'i' : 'u',
+ 8 * ty->size);
+ break;
+ case TYfloat:
+ pri("f%z", 8 * ty->size);
+ break;
+ case TYptr:
+ pri("*%t", ty->child);
+ break;
+ case TYarr:
+ assert(ty->length >= 0);
+ pri("[%U]%t", ty->length, ty->child);
+ break;
+ case TYslice:
+ pri("[#]%t", ty->child);
+ break;
+ case TYfn:
+ pri("fn(");
+ for (int i = 0; i < ty->fn.params.n; ++i) {
+ pri("_ %t", ty->fn.params.d[i]);
+ pri(", ");
+ }
+ if (ty->fn.variadic)
+ pri("...");
+ pri(") %t", ty->fn.retty);
+ }
+}
+
+static void dumpexpr(const struct expr *expr);
+
+static void
+pristring(const char *s, u64 n) {
+ extern int isprint(int);
+ pri("\"");
+ for (int i = 0; i < n; ++i) {
+ if (isprint(s[i]))
+ pri("%c", s[i]);
+ else
+ fprintf(stderr, "\\%.3o", s[i]);
+ }
+ pri("\"");
+}
+
+
+const char *
+tokt2str(int t) {
+ static char buf[128];
+
+ if (t == TKintlit) {
+ return "integer literal";
+ } else if (t == TKident) {
+ return "identifier";
+ } else if (t == TKstrlit) {
+ return "string literal";
+ } else if (t == TKchrlit) {
+ return "character literal";
+ } else if (t == TKeof) {
+ return "end of file";
+ } else if (t < NUM_KEYWORDS) {
+ snprintf(buf, sizeof buf - 1, "`%s'", keyword2str[t]);
+ } else if (t > 0xFF) {
+ unsigned m = bswap32(t);
+ int i = 0;
+ strcpy(buf, "`");
+ while (m) {
+ if (m & 0xFF)
+ buf[i++] = m;
+ m >>= 8;
+ }
+ strcpy(buf, "'");
+ } else {
+ snprintf(buf, sizeof buf - 1, "`%c'", t);
+ }
+ return buf;
+}
+
+const char *
+tok2str(struct tok tok) {
+ static char buf[512];
+
+ if (tok.t == TKintlit) {
+ snprintf(buf, sizeof buf - 1, "`%llu'", (unsigned long long)tok.ilit.i);
+ } else if (tok.t == TKident) {
+ snprintf(buf, sizeof buf - 1, "`%s'", tok.str);
+ } else if (tok.t == TKgensym) {
+ snprintf(buf, sizeof buf - 1, "`$%s'", tok.str);
+ } else if (tok.t == TKstrlit) {
+ snprintf(buf, sizeof buf - 1, "\"%s\"", tok.str);
+ } else if (tok.t == TKchrlit) {
+ u64 t = bswap64(tok.ilit.i);
+ int i = 1;
+ strcpy(buf, "'");
+ while (t) {
+ if (t & 0xFF)
+ buf[i++] = t;
+ t >>= 8;
+ }
+ buf[i] = '\0';
+ strcat(buf, "'");
+ } else if (tok.t == TKeof) {
+ return "<eof>";
+ } else if (tok.t < NUM_KEYWORDS) {
+ snprintf(buf, sizeof buf - 1, "`%s'", keyword2str[tok.t]);
+ } else if (tok.t > 0xFF) {
+ unsigned t = bswap32(tok.t);
+ int i = 1;
+ strcpy(buf, "`");
+ while (t) {
+ if (t & 0xFF)
+ buf[i++] = t;
+ t >>= 8;
+ }
+ buf[i] = '\0';
+ strcat(buf, "'");
+ } else {
+ snprintf(buf, sizeof buf - 1, "`%c'", tok.t);
+ }
+ return buf;
+}
+
+void
+pritoktree(struct toktree toks) {
+ fprintf(stderr, "[ ");
+ for (int i = 0; i < toks.n; ++i) {
+ fprintf(stderr, "%s ", tok2str(toks.d[i]));
+ }
+ fprintf(stderr, "]");
+}
+
+
+void
+pri(const char *fmt, ...) {
+ va_list ap;
+ unsigned ch;
+ const char *S;
+ int ws;
+
+ va_start(ap, fmt);
+ for (char c; (c = *fmt++);) {
+ if (c != '%') {
+ fputc(c, stderr);
+ continue;
+ }
+ switch ((c = *fmt++)) {
+ case '%':
+ fputc(c, stderr);
+ break;
+ case 'c':
+ ch = bswap32(va_arg(ap, int));
+ while (ch) {
+ fputc(ch, stderr);
+ ch >>= 8;
+ }
+ break;
+ case 'd':
+ fprintf(stderr, "%d", va_arg(ap, int));
+ break;
+ case 'U':
+ fprintf(stderr, "%llu", (unsigned long long)va_arg(ap, u64));
+ break;
+ case 'z':
+ fprintf(stderr, "%zu", va_arg(ap, size_t));
+ break;
+ case 'f':
+ fprintf(stderr, "%f", va_arg(ap, double));
+ break;
+ case 's':
+ fprintf(stderr, "%s", va_arg(ap, const char *));
+ break;
+ case 'w':
+ ws = va_arg(ap, int);
+ for (int i = 0; i < ws; ++i)
+ fprintf(stderr, " ");
+ break;
+ case 'S':
+ S = va_arg(ap, const char *);
+ pristring(S, va_arg(ap, u64));
+ break;
+ case 't':
+ pritype(va_arg(ap, const struct type *));
+ break;
+ case 'e':
+ dumpexpr(va_arg(ap, const struct expr *));
+ break;
+ default:
+ fprintf(stderr, "pri(): bad fmt '%%%c'\n", c);
+ abort();
+ break;
+ }
+ }
+ va_end(ap);
+}
+
+static void dumpexpr(const struct expr *expr) {
+ switch (expr->t) {
+ case Eintlit:
+ pri("%U_%t", expr->i, expr->ty);
+ break;
+ case Eflolit:
+ pri("%f%s", expr->f, expr->ty->size == 4 ? "f" : "");
+ break;
+ case Estrlit:
+ pri("%S", expr->strlit.d, expr->strlit.n);
+ break;
+ case Eboolit:
+ pri("#%c", expr->i ? 't' : 'f');
+ break;
+ case Enullit:
+ pri("#null");
+ break;
+ case Ename:
+ pri("%s", expr->ref->name);
+ break;
+ case Ebinop:
+ pri("(%e %c %e)", expr->binop.lhs, expr->binop.op, expr->binop.rhs);
+ break;
+ case Eprefix:
+ pri("%c(%e)", expr->unop.op, expr->unop.child);
+ break;
+ case Epostfix:
+ pri("(%e)%c", expr->unop.child, expr->unop.op);
+ break;
+ case Econd:
+ pri("(%e) ? (%e) : (%e)", expr->cond.test, expr->cond.t, expr->cond.f);
+ break;
+ case Ecall:
+ pri("%e(", expr->call.callee);
+ for (int i = 0; i < expr->call.args.n; ++i) {
+ pri("%e", &expr->call.args.d[i]);
+ if (i < expr->call.args.n - 1)
+ pri(", ");
+ }
+ pri(")");
+ break;
+ case Eindex:
+ pri("%e[%e]", expr->index.lhs, expr->index.rhs);
+ break;
+ default:
+ assert(0 && "exprbad");
+ }
+}
+
+static void dumpdecl(const struct decl *decl, int ws);
+static void dumpstmt(const struct stmt *stmt, int ws);
+
+static void
+dumpblock(const struct blockstmt *block, int ws) {
+ pri("%w{\n", ws -1);
+ for (int i = 0; i < block->stmts.n; ++i)
+ dumpstmt(&block->stmts.d[i], ws + 1);
+ pri("%w}\n", ws - 1);
+}
+
+static void
+dumpstmt(const struct stmt *stmt, int ws) {
+ switch (stmt->t) {
+ case Sblock:
+ dumpblock(&stmt->block, ws);
+ break;
+ case Sdecl:
+ dumpdecl(&stmt->decl, ws);
+ break;
+ case Sexpr:
+ pri("%w%e\n", ws, &stmt->expr);
+ break;
+ case Sifelse:
+ pri("%wif (%e)\n", ws, &stmt->ifelse.test);
+ dumpblock(&stmt->ifelse.t, ws + 1);
+ if (stmt->ifelse.f) {
+ pri("%welse\n", ws);
+ dumpstmt(stmt->ifelse.f, ws + 1);
+ }
+ break;
+ case Swhile:
+ pri("%wwhile %e\n", ws, &stmt->loop.test);
+ dumpblock(&stmt->loop.body, ws + 1);
+ break;
+ case Sfor:
+ pri("%wfor \n", ws);
+ if (stmt->loop.ini)
+ dumpstmt(stmt->loop.ini, ws + 1);
+ pri("%w; %e; ", ws + 1, &stmt->loop.test);
+ if (stmt->loop.next)
+ pri("%e", stmt->loop.next);
+ pri("\n");
+ dumpblock(&stmt->loop.body, ws + 1);
+ break;
+ case Siswitch:
+ pri("%wswitch %e {\n", ws, &stmt->iswitch.test);
+ for (int i = 0; i < stmt->iswitch.cs.n; ++i) {
+ struct iswitchcase *c = &stmt->iswitch.cs.d[i];
+ pri("%wcase ", ws);
+ for (int j = 0; j < c->es.n; ++j)
+ pri("%e, ", &c->es.d[j]);
+ pri("do\n");
+ dumpblock(&c->t, ws + 1);
+ }
+ if (stmt->iswitch.f) {
+ pri("%wcase else\n", ws);
+ dumpblock(stmt->iswitch.f, ws + 1);
+ }
+ pri("%w}\n", ws);
+ break;
+ case Sreturn:
+ if (stmt->retex)
+ pri("%wreturn %e;\n", ws, stmt->retex);
+ else
+ pri("%wreturn;\n", ws);
+ break;
+ }
+}
+
+static void
+dumpdecl(const struct decl *decl, int ws) {
+ pri("%w", ws);
+ pri("decl %s ", decl->name);
+ switch (decl->t) {
+ case Dtype:
+ pri("<type> %t\n", decl->ty);
+ break;
+ case Dvar:
+ pri("<var> %t", decl->var.ty);
+ if (decl->var.ini)
+ pri(" = %e", decl->var.ini);
+ pri("\n");
+ break;
+ case Dfn:
+ pri("<%sfn> (", decl->externp ? "extern " : "");
+ for (int i = 0; i < decl->fn.params.n; ++i)
+ pri("%s %t, ", decl->fn.params.d[i].name, decl->fn.params.d[i].ty);
+ if (decl->fn.variadic)
+ pri("...");
+ pri(") %t", decl->fn.retty);
+ if (decl->fn.body) {
+ pri("\n");
+ dumpstmt(decl->fn.body, ws + 1);
+ } else {
+ pri(" <...>\n");
+ }
+ break;
+ case Dmacro:
+ pri("<macro> {\n");
+ for (int i = 0; i < decl->macro.cs.n; ++i) {
+ struct macrocase c = decl->macro.cs.d[i];
+ pri("(");
+ for (int j = 0; j < c.params.n; ++j) {
+ if (j == c.params.n - 1 && c.variadic)
+ pri("...");
+ pri("%s", c.params.d[j]);
+ if (j < c.params.n - 1)
+ pri(", ");
+ }
+ pri(") ");
+ pritoktree(c.body);
+ pri("\n");
+ }
+ pri("}\n");
+ break;
+ }
+}
+
+void
+dumptransunit(const struct transunit *tu) {
+ for (int i = 0; i < tu->decls.n; ++i)
+ dumpdecl(&tu->decls.d[i], 0);
+}
diff --git a/bootstrap/env.c b/bootstrap/env.c
new file mode 100644
index 0000000..513b4d9
--- /dev/null
+++ b/bootstrap/env.c
@@ -0,0 +1,48 @@
+#include "all.h"
+
+
+struct decl *
+envfind(const struct env *env, const char *name) {
+ if (!env)
+ return NULL;
+ for (struct decls *decls = env->decls; decls; decls = decls->next) {
+ if (!strcmp(decls->decl.name, name))
+ return &decls->decl;
+ }
+ return envfind(env->parent, name);
+}
+
+static bool
+declsshadowable(const struct decl *a, const struct decl *b) {
+ return b->t == Dvar;
+}
+
+static bool
+declscompatible(const struct decl *a, const struct decl *b) {
+ if (a->t != b->t)
+ return 0;
+ if (a->t == Dfn && a->fn.selfty == b->fn.selfty && !a->fn.body)
+ return 1;
+ if (a->t == Dtype && a->ty == b->ty)
+ return 1;
+ return 0;
+}
+
+bool
+envput(struct env *env, const struct decl *decl) {
+ struct decls *decls;
+ struct env env_noparent = {
+ .decls = env->decls
+ };
+ struct decl *decl0;
+ if ((decl0 = envfind(&env_noparent, decl->name))) {
+ if (!declsshadowable(decl0, decl) && !declscompatible(decl0, decl))
+ return 0;
+ }
+
+ decls = xcalloc(1, sizeof *decls);
+ decls->next = env->decls;
+ decls->decl = *decl;
+ env->decls = decls;
+ return 1;
+}
diff --git a/bootstrap/main.c b/bootstrap/main.c
new file mode 100644
index 0000000..4acf913
--- /dev/null
+++ b/bootstrap/main.c
@@ -0,0 +1,14 @@
+#include "all.h"
+
+int
+main(int argc, char **argv) {
+ struct parser P;
+ struct transunit tu = {0};
+
+ assert(argc > 1);
+ inittypes();
+ initparser(&P, argv[1]);
+ parse(&tu, &P);
+ // dumptransunit(&tu);
+ cgen(stdout, &tu);
+}
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
new file mode 100644
index 0000000..96a185c
--- /dev/null
+++ b/bootstrap/parse.c
@@ -0,0 +1,1733 @@
+#include "all.h"
+
+/***********/
+/** Lexer **/
+/***********/
+
+static u8
+chr(struct parser *P) {
+ int c;
+ if (P->peekchr >= 0) {
+ c = P->peekchr;
+ P->peekchr = -1;
+ } else {
+ c = fgetc(P->fp);
+ }
+ P->curspan.idx++;
+ P->curspan.col++;
+ if (c == '\n') {
+ P->curspan.col = 1;
+ P->curspan.line++;
+ }
+ if (c == EOF || !c) {
+ P->eof = 1;
+ return 0;
+ }
+ return c;
+}
+
+static u8
+chrpeek(struct parser *P) {
+ int c;
+ if (P->peekchr >= 0)
+ return P->peekchr;
+ c = fgetc(P->fp);
+ return P->peekchr = c == EOF ? 0 : (u8)c;
+}
+
+static bool
+chrmatch(struct parser *P, u8 c) {
+ if (chrpeek(P) == c) {
+ chr(P);
+ return 1;
+ }
+ return 0;
+}
+
+static bool
+aisspace(char c) {
+ switch (c)
+ case ' ': case '\t': case '\n':
+ case '\r': case '\v': case '\f':
+ return 1;
+ return 0;
+}
+
+static bool
+aisdigit(char c) {
+ return c >= '0' && c <= '9';
+}
+
+static bool
+aisxdigit(char c) {
+ return (c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || (c >= 'a' && c <= 'f') ;
+}
+
+static bool
+aisalpha(char c) {
+ return (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z');
+}
+
+static bool
+aissep(char c) {
+ if (aisspace(c))
+ return 1;
+ switch (c)
+ case '(': case ')': case '[': case ']':
+ case '{': case '}': case '.': case ',':
+ case ';': case '?': case '+': case '-':
+ case '*': case '/': case '&': case '|':
+ case '^': case '~': case '=': case '\'':
+ case '"': case '<': case '>':
+ return 1;
+ return 0;
+}
+
+static void
+eatspaces(struct parser *P) {
+ for (;; chr(P)) {
+ if (aisspace(chrpeek(P)))
+ continue;
+ else if (chrpeek(P) == '!') {
+ while (chrpeek(P) != '\n' && chrpeek(P) != 0)
+ chr(P);
+ } else {
+ break;
+ }
+ }
+}
+
+static int
+readtilsep(struct parser *P, char *buf, int n, bool dot) {
+ int i = 0;
+ u8 c;
+ while (!aissep((c = chrpeek(P))) || (dot && c == '.')) {
+ chr(P);
+ if (i >= n - 1)
+ return -1;
+ buf[i++] = c;
+ }
+ buf[i++] = 0;
+ return i;
+}
+
+const char *keyword2str[] = {
+#define KWSTR(kw) #kw,
+ LIST_KEYWORDS(KWSTR)
+#undef KWSTR
+};
+// #define NUM_KEYWORDS (sizeof keyword2str / sizeof *keyword2str)
+
+static int
+str2keyword(const char *s) {
+ int i = 0, j = NUM_KEYWORDS - 1;
+ while (i <= j) {
+ int k = (j + i) / 2;
+ int cmp = strcmp(keyword2str[k], s);
+ if (!cmp) {
+ return k;
+ } else if (cmp < 0) {
+ i = k + 1;
+ } else {
+ j = k - 1;
+ }
+ }
+ return -1;
+}
+
+static bool
+str2number(struct tok *res, const char *s) {
+ char c;
+ u64 acc = 0;
+ double accf = 0.0;
+ double fmul = 0.1;
+ int base = 10;
+ bool flt = 0;
+ int nused = 0;
+
+ for (int i = 0; (c = s[i]); ++i) {
+ extern int tolower(int);
+
+ if (i == 0 && c == '0')
+ --nused;
+
+ if (i == 1 && tolower(c) == 'x') {
+ base = 16;
+ continue;
+ }
+ if (!flt && c == '.' && base == 10) {
+ flt = 1;
+ accf = acc;
+ continue;
+ }
+
+ if (nused > 0 && c == '_')
+ continue;
+
+ if (base == 16 && !aisxdigit(c))
+ return 0;
+ else if (base != 16 && (c < '0' || c > '0' + base - 1))
+ return 0;
+
+ ++nused;
+ if (flt) {
+ accf = accf + (c - '0') * fmul;
+ fmul *= 0.1;
+ } else {
+ c = tolower(c);
+ acc = acc * base + (c <= '9' ? c - '0' : c - 'a' + 10);
+ }
+ }
+ memset(res, 0, sizeof *res);
+ if (flt) {
+ res->t = TKflolit;
+ res->flit.f = accf;
+ } else {
+ res->t = TKintlit;
+ res->ilit.i = acc;
+ }
+ return 1;
+}
+
+static size_t
+readstrlit(struct parser *P, u8 delim, char *s, size_t n) {
+ u8 c;
+ size_t i = 0;
+ while ((c = chr(P)) != delim) {
+ if (!c || c == '\n')
+ fatal(P, P->tokspan, "unterminated %s literal",
+ delim == '"' ? "string" : "character");
+ if (i == n - 1)
+ assert(0 && "too long string/charliteral");
+
+ if (c != '\\')
+ s[i++] = c;
+ else switch ((c = chr(P))) {
+ case 0: case '\n':
+ fatal(P, P->tokspan, "unterminated %s literal",
+ delim == '"' ? "string" : "character");
+ case '\'': s[i++] = '\''; break;
+ case '"': s[i++] = '"'; break;
+ case 'n': s[i++] = '\n'; break;
+ case 'r': s[i++] = '\r'; break;
+ case 't': s[i++] = '\t'; break;
+ case 'v': s[i++] = '\v'; break;
+ case 'f': s[i++] = '\f'; break;
+ case '0': s[i++] = '\0'; break;
+ default:
+ fatal(P, P->tokspan, "unknown escape sequence '\\%c'", c);
+ }
+ }
+ s[i++] = 0;
+ return i - 1;
+}
+
+#define MAX_MACROEXPAND_ITER 100
+
+// guard used at macro arg expansion to avoid it expanding into itself, e.g.:
+// defmacro foo(x,y) [(x+y)]
+// let x, y ...;
+// foo(x,y)
+// would cause infinite recursion otherwise
+static bool
+ismacrodupe(struct parser *P, struct toktree toks) {
+ for (struct expan *ep = P->curexpan; ep; ep = ep->prev) {
+ if (ep->toks.d == toks.d)
+ return 1;
+ }
+ return 0;
+}
+
+static struct tok
+lex(struct parser *P) {
+ u8 c;
+ struct tok tok;
+
+ if (P->have_peektok) {
+ tok = P->peektok;
+ P->have_peektok = 0;
+ return tok;
+ }
+
+ if (P->expanno > MAX_MACROEXPAND_ITER) {
+ fatal(P, P->curspan,
+ "maximum number of nested macro expansions reached (infinite recursion?)");
+ }
+
+ if (P->curexpan) {
+ struct expan *ep = P->curexpan;
+ if (ep->idx == ep->toks.n) {
+ // end of macro expansion
+ --P->expanno;
+ free(ep->args.d);
+ P->curexpan = ep->prev;
+ free(ep);
+ return lex(P);
+ }
+ tok = ep->toks.d[ep->idx++];
+ // expand macro arg?
+ if (tok.t == TKident) {
+ for (struct expan *ep = P->curexpan; ep; ep = ep->prev) {
+ for (int i = 0; i < ep->args.n; ++i) {
+ if (!strcmp(tok.str, ep->args.d[i].name)) {
+ if (!ismacrodupe(P, ep->args.d[i].toks)) {
+ ++P->expanno;
+ P->curexpan = memcpy(xmalloc(sizeof *ep), &(struct expan) {
+ P->curexpan, {0}, ep->args.d[i].toks,
+ }, sizeof *ep);
+ return lex(P);
+ } else
+ return tok;
+ }
+ }
+ }
+ }
+ return tok;
+ }
+
+ eatspaces(P);
+ tok.span = P->tokspan = P->curspan;
+ if (aisdigit((c = chrpeek(P)))) {
+ char s[80];
+
+ if (readtilsep(P, s, sizeof s, 1) < 0)
+ fatal(P, P->tokspan, "number literal too long");
+ if (!str2number(&tok, s))
+ fatal(P, P->tokspan, "invalid number literal");
+ tok.span = P->tokspan;
+ return tok;
+ } else if (aisalpha(c) || c == '_' || c == '#' || c == '$') {
+ char s[100];
+ int t;
+
+ if (readtilsep(P, s, sizeof s, 0) < 0)
+ fatal(P, P->tokspan, "identifier too long");
+ if ((t = str2keyword(s)) >= 0) {
+ tok.t = t;
+ } else if (!strcmp(s, "#")) {
+ tok.t = '#';
+ } else if (!strcmp(s, "#t") || !strcmp(s, "#f")) {
+ tok.t = TKboolit;
+ tok.boolit = s[1] == 't';
+ } else if (!strcmp(s, "#null")) {
+ tok.t = TKnullit;
+ } else if (c == '#') {
+ fatal(P, P->tokspan, "identifier cannot start with '#'");
+ } else if (c == '$') {
+ tok.t = TKgensym;
+ tok.str = xstrdup(s + 1);
+ } else {
+ tok.t = TKident;
+ tok.str = xstrdup(s);
+ }
+ return tok;
+ } else if (c == '\'' || c == '"') {
+ u8 delim = c;
+ char s[1000];
+ size_t n;
+ chr(P);
+ tok.span = P->tokspan = P->curspan;
+ n = readstrlit(P, delim, s, sizeof s);
+ if (delim == '"') {
+ tok.t = TKstrlit;
+ tok.str = strcpy(xmalloc(n + 1), s);
+ tok.strlen = n;
+ } else {
+ if (n == 0)
+ fatal(P, P->tokspan, "empty char literal");
+ if (n > 8)
+ fatal(P, P->tokspan, "too long multichar literal '%s'", s);
+ tok.t = TKchrlit;
+ tok.ilit.i = 0;
+ tok.ilit.ty = NULL;
+ for (int j = 0; j < n; ++j)
+ tok.ilit.i = (tok.ilit.i << 8) | (u8)s[j];
+ }
+ return tok;
+ }
+ switch ((c = chr(P))) {
+ case '(': case ')': case '[': case ']':
+ case '{': case '}': case ',':
+ case ';': case '?': case '~':
+ tok.t = c;
+ return tok;
+ case '.':
+ if (chrmatch(P, '.')) {
+ if (chr(P) != '.')
+ fatal(P, P->tokspan, "invalid token '..'");
+ tok.t = '...';
+ } else {
+ tok.t = c;
+ }
+ return tok;
+ case '*':
+ if (chrmatch(P, '=')) tok.t = '*=';
+ else tok.t = '*';
+ return tok;
+ case '/':
+ if (chrmatch(P, '=')) tok.t = '/=';
+ else tok.t = '/';
+ return tok;
+ case '%':
+ if (chrmatch(P, '=')) tok.t = '%=';
+ else tok.t = '%';
+ return tok;
+ case '+':
+ if (chrmatch(P, '=')) tok.t = '+=';
+ else if (chrmatch(P, '+')) tok.t = '++';
+ else tok.t = '+';
+ return tok;
+ case '-':
+ if (chrmatch(P, '=')) tok.t = '-=';
+ else if (chrmatch(P, '-')) tok.t = '--';
+ else tok.t = '-';
+ return tok;
+ case '&':
+ if (chrmatch(P, '=')) tok.t = '&=';
+ else tok.t = '&';
+ return tok;
+ case '|':
+ if (chrmatch(P, '=')) tok.t = '|=';
+ else tok.t = '|';
+ return tok;
+ case '^':
+ if (chrmatch(P, '=')) tok.t = '^=';
+ else tok.t = '^';
+ return tok;
+ case ':':
+ if (chrmatch(P, '=')) tok.t = ':=';
+ else tok.t = ':';
+ return tok;
+ case '=':
+ if (chrmatch(P, '=')) tok.t = '==';
+ else tok.t = '=';
+ return tok;
+ case '!':
+ if (chrmatch(P, '=')) tok.t = '!=';
+ else tok.t = '!';
+ return tok;
+ case '<':
+ if (chrmatch(P, '=')) tok.t = '<=';
+ else if (chrmatch(P, '<')) {
+ if (chrmatch(P, '=')) tok.t = '<<=';
+ else tok.t = '<<';
+ } else tok.t = '<';
+ return tok;
+ case '>':
+ if (chrmatch(P, '=')) tok.t = '>=';
+ else if (chrmatch(P, '>')) {
+ if (chrmatch(P, '=')) tok.t = '>>=';
+ else tok.t = '>>';
+ } else tok.t = '>';
+ return tok;
+ case 0:
+ tok.t = TKeof;
+ return tok;
+ }
+ fatal(P, P->tokspan, "unexpected character '%c'", c);
+}
+
+static struct tok
+lexpeek(struct parser *P) {
+ struct tok tok;
+
+ if (P->have_peektok)
+ return P->peektok;
+ tok = lex(P);
+ P->peektok = tok;
+ P->have_peektok = 1;
+ return tok;
+}
+
+static bool
+lexmatch(struct parser *P, struct tok *tokp, int t) {
+ struct tok tok = lexpeek(P);
+
+ if (tokp)
+ *tokp = tok;
+ if (tok.t == t) {
+ lex(P);
+ return 1;
+ }
+ return 0;
+}
+
+static struct tok
+lexexpects(struct parser *P, int t, const char *what) {
+ struct tok tok;
+ if (!lexmatch(P, &tok, t))
+ fatal(P, tok.span, "expected %s (near %s)",
+ what ? what : tokt2str(t), tok2str(tok));
+ return tok;
+}
+#define lexexpect(P, t) lexexpects(P, t, NULL)
+
+/************/
+/** Parser **/
+/************/
+
+static void
+pushenv(struct parser *P, struct env *env) {
+ assert(P->curenv == env->parent);
+ P->curenv = env;
+}
+
+static void
+popenv(struct parser *P) {
+ P->curenv = P->curenv->parent;
+ assert(P->curenv);
+}
+
+static void
+putdecl(struct parser *P, struct span espan, const struct decl *decl) {
+ if (envfind(P->primenv, decl->name))
+ fatal(P, espan, "cannot shadow primitive `%s'", decl->name);
+ if (!envput(P->curenv, decl))
+ fatal(P, decl->span,
+ "cannot shadow earlier incompatible declaration of `%s'", decl->name);
+}
+
+static const struct decl *
+finddecl(struct parser *P, const char *name) {
+ const struct decl *decl;
+ if ((decl = envfind(P->primenv, name)))
+ return decl;
+ return envfind(P->curenv, name);
+}
+
+static struct expr parseexpr(struct parser *P);
+
+static const struct type *
+constify(const struct type *ty) {
+ struct type ty2 = *ty;
+ if (ty->konst)
+ return ty;
+ if (ty2.t != TYfn)
+ ty2.konst = 1;
+ return interntype(ty2);
+}
+
+static const struct type *
+unconstify(const struct type *ty) {
+ struct type ty2 = *ty;
+ if (!ty->konst)
+ return ty;
+ ty2.konst = 0;
+ return interntype(ty2);
+}
+
+static const struct type *
+constifychild(const struct type *ty) {
+ struct type ty2 = *ty;
+ const struct type *child = constify(ty->child);
+ if (child == ty->child)
+ return ty;
+ ty2.child = child;
+ return interntype(ty2);
+}
+
+
+static const struct type * typeof2(const struct type *, const struct type *);
+
+static const struct type *
+parsetype(struct parser *P) {
+ struct tok tok;
+ if (lexmatch(P, &tok, '*')) {
+ return interntype((struct type) {
+ TYptr,
+ g_targ.ptrsize,
+ .child = parsetype(P)
+ });
+ } else if (lexmatch(P, &tok, '[')) {
+ i64 length = -1;
+ if (!lexmatch(P, &tok, ']')) {
+ length = lexexpect(P, TKintlit).ilit.i;
+ assert(length >= 0);
+ lexexpect(P, ']');
+ }
+ return interntype((struct type) {
+ TYarr,
+ g_targ.ptrsize,
+ .child = parsetype(P),
+ .length = length
+ });
+ } else if (lexmatch(P, &tok, TKkw_const)) {
+ return constify(parsetype(P));
+ } else if (lexmatch(P, &tok, TKkw_typeof)) {
+ const struct type *ty = NULL, *ty2;
+ lexexpect(P, '(');
+ do {
+ ty2 = parseexpr(P).ty;
+ ty = ty ? typeof2(ty, ty2) : ty2;
+ if (!ty)
+ fatal(P, tok.span, "incompatible types in typeof(...)");
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, ')');
+ break;
+ }
+ } while (!lexmatch(P, &tok, ')'));
+ return ty;
+ } else if (lexmatch(P, &tok, TKident)) {
+ const struct decl *decl = finddecl(P, tok.str);
+ if (!decl)
+ fatal(P, P->tokspan, "%s is not defined", tok2str(tok));
+ if (decl->t != Dtype)
+ fatal(P, P->tokspan, "%s is not a type", tok2str(tok));
+ return decl->ty;
+ }
+ fatal(P, P->tokspan, "expected type (near %s)", tok2str(tok));
+}
+
+static const struct type *
+ilittype(struct parser *P, u64 i) {
+ if (i <= INT_MAX)
+ return ty_int;
+ if (i <= INT32_MAX)
+ return ty_i32;
+ if (i <= INT64_MAX)
+ return ty_i64;
+ return ty_u64;
+}
+
+static int
+numtype2rank(const struct type *a) {
+ if (a->t == TYint) {
+ if (a->size < g_targ.intsize || a == ty_int)
+ return 0;
+ if (a == ty_uint) return 1;
+ if (a == ty_i32) return 2;
+ if (a == ty_u32) return 3;
+ if (a == ty_i64) return 4;
+ if (a == ty_u64) return 5;
+ }
+ if (a == ty_f32) return 6;
+ if (a == ty_f64) return 7;
+ return -1;
+}
+
+static const struct type *
+rank2numtype(int r) {
+ static const struct type **types[] = {
+ &ty_int, &ty_uint, &ty_i32, &ty_u32,
+ &ty_i64, &ty_u64, &ty_f32, &ty_f64,
+ };
+ assert(r >= 0 && r < ARRAY_LENGTH(types));
+ return *types[r];
+}
+
+static const struct type *
+numpromote(const struct type *ty) {
+ int r = numtype2rank(ty);
+ if (r < 0)
+ return NULL;
+ return rank2numtype(r);
+}
+
+static bool
+isnumtype(const struct type *a) {
+ return a->t == TYint || a->t == TYfloat;
+}
+
+static const struct type *
+arraydecay(const struct type *ty) {
+ struct type ty2 = *ty;
+
+ if (ty->t != TYarr)
+ return ty;
+
+ ty2.t = TYptr;
+ return interntype(ty2);
+}
+
+// peer type resolution
+static const struct type *
+typeof2(const struct type *a, const struct type *b) {
+ if (isnumtype(a) && isnumtype(b)) {
+ int ra = numtype2rank(a), rb = numtype2rank(b);
+ return rank2numtype(MAX(ra, rb));
+ }
+ if (a == b)
+ return a;
+ if (a->t == TYptr && b->t == TYarr)
+ b = arraydecay(b);
+ if (a->t == TYarr && b->t == TYptr)
+ a = arraydecay(a);
+ if (a->t == TYptr && b->t == TYptr) {
+ bool akonst = a->child->konst,
+ bkonst = b->child->konst;
+ const struct type *uac = unconstify(a->child),
+ *ubc = unconstify(b->child);
+ if (uac == ubc)
+ return akonst ? a : b;
+ if (uac == ty_void) {
+ if (bkonst && !akonst)
+ return constifychild(a);
+ return a;
+ }
+ if (ubc == ty_void) {
+ if (akonst && !bkonst)
+ return constifychild(b);
+ return b;
+ }
+ if (uac == ty_u8) {
+ if (bkonst && !akonst)
+ return constifychild(a);
+ return a;
+ }
+ if (ubc == ty_u8) {
+ if (akonst && !bkonst)
+ return constifychild(b);
+ return b;
+ }
+
+ }
+ return NULL;
+}
+
+static bool
+islvalue(const struct expr *ex) {
+ if (ex->t == Ename)
+ return 1;
+ if (ex->t == Eprefix && ex->unop.op == '*')
+ return 1;
+ return 0;
+}
+
+/** Expression parsing **/
+
+static struct expr *
+exprdup(struct expr ex) {
+ return memcpy(xmalloc(sizeof ex), &ex, sizeof ex);
+}
+
+
+static struct blockstmt parseblock0(struct parser *P);
+static struct stmt parseblock(struct parser *P);
+static void parseexpandmacro(struct parser *P, const struct macro *macro);
+
+static struct expr
+pexprimary(struct parser *P) {
+ struct expr ex = {0};
+ struct tok tok;
+
+ if (lexmatch(P, &tok, TKintlit) || lexmatch(P, &tok, TKchrlit)) {
+ ex.t = Eintlit;
+ ex.span = tok.span;
+ ex.i = tok.ilit.i;
+ ex.ty = tok.ilit.ty ? tok.ilit.ty : ilittype(P, ex.i);
+ } else if (lexmatch(P, &tok, TKflolit)) {
+ ex.t = Eflolit;
+ ex.span = tok.span;
+ ex.f = tok.flit.f;
+ ex.ty = tok.flit.ty ? tok.flit.ty : ty_f64;
+ } else if (lexmatch(P, &tok, TKstrlit)) {
+ ex.t = Estrlit;
+ ex.span = tok.span;
+ ex.strlit.d = tok.str;
+ ex.strlit.n = tok.strlen;
+ ex.ty = interntype((struct type) {
+ TYarr, tok.strlen + 1, 1, 0,
+ .child = constify(ty_u8), .length = tok.strlen + 1
+ });
+ } else if (lexmatch(P, &tok, TKboolit)) {
+ ex.t = Eboolit;
+ ex.span = tok.span;
+ ex.i = tok.boolit;
+ ex.ty = ty_bool;
+ } else if (lexmatch(P, &tok, TKnullit)) {
+ ex.t = Enullit;
+ ex.span = tok.span;
+ ex.i = tok.boolit;
+ ex.ty = interntype((struct type) {
+ TYptr, g_targ.ptrsize, .child = ty_void
+ });
+ } else if (lexmatch(P, &tok, TKident)) {
+ const struct decl *decl;
+
+ decl = finddecl(P, tok.str);
+ if (!decl)
+ fatal(P, tok.span, "%s is not defined", tok2str(tok));
+ if (decl->t == Dtype) {
+ assert(0);
+ } else if (decl->t == Dmacro) {
+ parseexpandmacro(P, &decl->macro);
+ return parseexpr(P);
+ } else {
+ ex.t = Ename;
+ ex.span = tok.span;
+ ex.ref = decl;
+ if (decl->t == Dvar) {
+ if (decl->var.fnid != P->curfn->id)
+ fatal(P, tok.span,
+ "cannot access local variable `%s' belonging to outer function",
+ tok.str);
+ ex.ty = decl->var.ty;
+ } else if (decl->t == Dfn) {
+ ex.ty = decl->fn.selfty;
+ } else assert(0);
+ }
+ } else if (lexmatch(P, &tok, '(')) {
+ ex = parseexpr(P);
+ lexexpect(P, ')');
+ } else {
+ fatal(P, tok.span, "expected expression (near %s)", tok2str(tok));
+ }
+ return ex;
+}
+
+static struct expr
+pexpostfix(struct parser *P) {
+ struct expr ex = pexprimary(P);
+ struct tok tok;
+
+ for (;;) if (lexmatch(P, &tok, '(')) {
+ vec_t(struct expr) args = {0};
+ int i = 0, n = ex.ty->fn.params.n;
+
+ if (ex.ty->t != TYfn)
+ fatal(P, ex.span, "callee is not a function");
+ while (!lexmatch(P, NULL, ')')) {
+ struct expr arg = parseexpr(P);
+
+ if (i == n && ! ex.ty->fn.variadic)
+ fatal(P, arg.span, "too many args for call");
+ if (i < n && !typeof2(arg.ty, ex.ty->fn.params.d[i++]))
+ fatal(P, arg.span, "call argument #%d type mismatch", i);
+
+ vec_push(&args, arg);
+ if (!lexmatch(P, NULL, ',')) {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ ex.call.callee = exprdup(ex);
+ ex.t = Ecall;
+ ex.span = tok.span;
+ ex.ty = ex.call.callee->ty->fn.retty;
+ vec_slice_cpy(&ex.call.args, &args);
+ } else if (lexmatch(P, &tok, '[')) {
+ struct expr lhs = ex,
+ rhs = parseexpr(P);
+ lexexpect(P, ']');
+ if (lhs.ty->t != TYarr && lhs.ty->t != TYptr)
+ fatal(P, lhs.span, "indexee is not array or pointer type");
+ if (rhs.ty->t != TYint)
+ fatal(P, lhs.span, "index expression type is not integral");
+ ex.t = Eindex;
+ ex.span = tok.span;
+ ex.ty = lhs.ty->child;
+ ex.index.lhs = exprdup(lhs);
+ ex.index.rhs = exprdup(rhs);
+ } else if (lexmatch(P, &tok, '++') || lexmatch(P, &tok, '--')) {
+ if (!isnumtype(ex.ty))
+ fatal(P, ex.span, "invalid operand to unary operator %s: not numeric",
+ tok2str(tok));
+ if (!islvalue(&ex))
+ fatal(P, ex.span, "left operand to postfix %s operator is not lvalue",
+ tok2str(tok));
+ if (ex.ty->konst)
+ fatal(P, ex.span, "left operand to postfix %s operator is const",
+ tok2str(tok));
+ ex = (struct expr) {
+ Epostfix, tok.span, ex.ty, .unop = {
+ tok.t, exprdup(ex)
+ }
+ };
+ } else {
+ break;
+ }
+
+ return ex;
+}
+
+static struct expr
+pexprefix(struct parser *P) {
+ struct tok tok;
+
+ if (lexmatch(P, &tok, '-') || lexmatch(P, &tok, '~')) {
+ struct expr ex = pexprefix(P);
+ const struct type *ty = numpromote(ex.ty);
+
+ if (!ty)
+ fatal(P, ex.span, "invalid operand to unary operator %s: not numeric",
+ tok2str(tok));
+ if (ty->t != TYint && tok.t == '~')
+ fatal(P, ex.span, "invalid operand to unary operator %s: not integral",
+ tok2str(tok));
+ return (struct expr) {
+ Eprefix, tok.span, ty, .unop = {
+ tok.t, exprdup(ex)
+ }
+ };
+ } else if (lexmatch(P, &tok, '++') || lexmatch(P, &tok, '--')) {
+ struct expr ex = pexprefix(P);
+
+ if (!isnumtype(ex.ty))
+ fatal(P, ex.span, "invalid operand to unary operator %s: not numeric",
+ tok2str(tok));
+ if (!islvalue(&ex))
+ fatal(P, ex.span, "left operand to prefix %s operator is not lvalue",
+ tok2str(tok));
+ if (ex.ty->konst)
+ fatal(P, ex.span, "left operand to prefix %s operator is const",
+ tok2str(tok));
+ return (struct expr) {
+ Eprefix, tok.span, ex.ty, .unop = {
+ tok.t, exprdup(ex)
+ }
+ };
+ } else if (lexmatch(P, &tok, TKkw_not)) {
+ struct expr ex = pexprefix(P);
+
+ if (ex.ty->t != TYbool)
+ fatal(P, ex.span, "invalid operand to unary operator %s: not boolean",
+ tok2str(tok));
+ return (struct expr) {
+ Eprefix, tok.span, ty_bool, .unop = {
+ 'not', exprdup(ex)
+ }
+ };
+ } else if (lexmatch(P, &tok, '*')) {
+ struct expr ex = pexprefix(P);
+ if (ex.ty->t != TYptr)
+ fatal(P, ex.span, "invalid operand type to dereference, not pointer");
+ if (!completetype(ex.ty->child))
+ fatal(P, ex.span, "invalid operand type to dereference, incomplete");
+ return (struct expr) {
+ Eprefix, tok.span, ex.ty->child, .unop = {
+ '*', exprdup(ex)
+ }
+ };
+ } else if (lexmatch(P, &tok, '&')) {
+ struct expr ex = pexprefix(P);
+ struct type ty2 = { TYptr, g_targ.ptrsize, .child = ex.ty};
+ if (!islvalue(&ex))
+ fatal(P, ex.span, "invalid operand type to address-of, not an lvalue");
+ return (struct expr) {
+ Eprefix, tok.span, interntype(ty2), .unop = {
+ '&', exprdup(ex)
+ }
+ };
+ }
+
+ return pexpostfix(P);
+}
+
+static bool
+peeksbitarithop(struct parser *P, struct tok* tokp) {
+ struct tok tok = lexpeek(P);
+
+ if (tokp)
+ *tokp = tok;
+ switch (tok.t) {
+ case '+': case '-': case '*': case '/': case '%':
+ return 1;
+ case '&': case '|': case '^': case '<<': case '>>':
+ return 2;
+ }
+ return 0;
+}
+
+static struct expr
+pexbitarith(struct parser *P) {
+ struct expr ex;
+ struct tok tok;
+ int tokt;
+ int oret;
+
+ ex = pexprefix(P);
+ if (!(oret = peeksbitarithop(P, &tok)))
+ return ex;
+ tokt = tok.t;
+
+ while (lexmatch(P, &tok, tokt)) {
+ struct expr rhs = pexprefix(P);
+ const struct type *ty = typeof2(ex.ty, rhs.ty);
+ if (!ty) {
+ if ((tokt == '+' || tokt == '-')
+ && ((ex.ty->t == TYptr && rhs.ty->t == TYint)
+ || (ex.ty->t == TYint && rhs.ty->t == TYptr)))
+ {
+ ty = ex.ty->t == TYptr ? ex.ty : rhs.ty;
+ } else {
+ goto err;
+ }
+ } else if (tokt == '-' && ex.ty->t == TYptr && rhs.ty->t == TYptr) {
+ ty = ty_isize;
+ } else if (!isnumtype(ty)) {
+ err:
+ fatal(P, tok.span, "invalid operands to binary operator %s",
+ tokt2str(tokt));
+ }
+ if (oret == 2 && ty->t == TYfloat)
+ fatal(P, tok.span, "invalid operands to bitwise operator: not integral",
+ tokt2str(tokt));
+ ex = (struct expr) {
+ Ebinop, tok.span, ty, .binop = {
+ tokt, exprdup(ex), exprdup(rhs)
+ }
+ };
+
+ }
+
+ return ex;
+}
+
+static bool
+matchcmpop(struct parser *P, struct tok *tokp) {
+ struct tok tok = lexpeek(P);
+
+ if (tokp)
+ *tokp = tok;
+ switch (tok.t) {
+ case '==': case '<=': case '>=':
+ case '<': case '>':
+ lex(P);
+ return 1;
+ }
+ return 0;
+}
+
+static struct expr
+pexcmp(struct parser *P) {
+ struct expr ex;
+ struct tok tok;
+
+ ex = pexbitarith(P);
+ if (matchcmpop(P, &tok)) {
+ struct expr rhs = pexbitarith(P);
+ if (!typeof2(ex.ty, rhs.ty))
+ fatal(P, tok.span, "incompatible operands to binary operator %s",
+ tok2str(tok));
+ if (tok.t != '==' && !isnumtype(typeof2(ex.ty, rhs.ty)))
+ fatal(P, tok.span, "invalid operands to relational operator %s: not numeric",
+ tok2str(tok));
+ ex = (struct expr) {
+ Ebinop, tok.span, ty_bool, .binop = {
+ tok.t, exprdup(ex), exprdup(rhs)
+ }
+ };
+ }
+ return ex;
+}
+
+static struct expr
+pexlog(struct parser *P) {
+ struct expr ex;
+ struct tok tok;
+ int tokt;
+
+ ex = pexcmp(P);
+ tok = lexpeek(P);
+ tokt = tok.t;
+ if (tokt != TKkw_and && tokt != TKkw_or)
+ return ex;
+
+ while (lexmatch(P, &tok, tokt)) {
+ struct expr rhs = pexcmp(P);
+ if (ex.ty->t != TYbool || rhs.ty->t != TYbool)
+ fatal(P, tok.span, "invalid operands to binary operator %s: not boolean",
+ tokt2str(tok.t));
+ ex = (struct expr) {
+ Ebinop, tok.span, ty_bool, .binop = {
+ tokt == TKkw_and ? 'and' : 'or', exprdup(ex), exprdup(rhs)
+ }
+ };
+
+ }
+
+ return ex;
+}
+
+static struct expr
+pexcond(struct parser *P) {
+ struct expr ex;
+ struct tok tok;
+
+ ex = pexlog(P);
+ if (lexmatch(P, &tok, '?')) {
+ struct expr ex2 = parseexpr(P);
+ struct expr ex3;
+ const struct type *ty;
+
+ lexexpect(P, ':');
+ ex3 = pexcond(P);
+ if (!(ty = typeof2(ex2.ty, ex3.ty)))
+ fatal(P, tok.span, "conditional operator branches have incompatible types");
+ ex = (struct expr) {
+ Econd, tok.span, ty, .cond = {
+ exprdup(ex), exprdup(ex2), exprdup(ex3)
+ }
+ };
+ }
+ return ex;
+}
+
+static bool
+matchassignop(struct parser *P, struct tok *tokp) {
+ struct tok tok = lexpeek(P);
+ if (tokp)
+ *tokp = tok;
+ switch (tok.t) {
+ case '=':
+ lex(P);
+ return 1;
+ case '+=': case '-=': case '*=': case '/=': case '%=':
+ lex(P);
+ return 2;
+ case '&=': case '|=': case '^=': case '<<=': case '>>=':
+ lex(P);
+ return 3;
+ }
+ return 0;
+}
+
+static struct expr
+pexassign(struct parser *P) {
+ struct expr ex;
+ struct tok tok;
+ int oret;
+
+ ex = pexcond(P);
+ if ((oret = matchassignop(P, &tok))) {
+ struct expr rhs = pexcond(P);
+ if (!islvalue(&ex))
+ fatal(P, ex.span,
+ "left operand to assignment operator %s is not an lvalue",
+ tok2str(tok));
+ if (!typeof2(ex.ty, rhs.ty))
+ fatal(P, ex.span,
+ "operands to assignment operator %s have incompatible types",
+ tok2str(tok));
+ if (ex.ty->konst)
+ fatal(P, ex.span,
+ "left operand to assignment operator %s is const",
+ tok2str(tok));
+ ex = (struct expr) {
+ Ebinop, tok.span, ex.ty, .binop = {
+ tok.t, exprdup(ex), exprdup(rhs)
+ }
+ };
+ }
+
+ return ex;
+}
+
+static struct expr
+parseexpr(struct parser *P) {
+ return pexassign(P);
+}
+
+/** Statements and declarations parsing **/
+
+static struct stmt *
+stmtdup(struct stmt st) {
+ return memcpy(xmalloc(sizeof st), &st, sizeof st);
+}
+
+static struct stmt
+pstlet(struct parser *P) {
+ struct stmt st = {0};
+ struct tok tok;
+ const char *name;
+ const struct type *ty = NULL;
+ struct expr *ini = NULL;
+ bool konst = 0;
+
+ if (lexmatch(P, NULL, TKkw_const))
+ konst = 1;
+
+ tok = lexexpect(P, TKident);
+ name = tok.str;
+
+ if (lexmatch(P, NULL, '=')) {
+ ini = exprdup(parseexpr(P));
+ ty = ini->ty;
+ } else {
+ ty = parsetype(P);
+ lexexpect(P, '=');
+ ini = exprdup(parseexpr(P));
+ }
+ if (!typeof2(ty, ini->ty))
+ fatal(P, tok.span, "incompatible initializer type");
+
+ if (!completetype(ty))
+ fatal(P, tok.span, "let `%s': variable type is incomplete", name);
+
+ if (konst)
+ ty = constify(ty);
+
+ assert(ty);
+ st.t = Sdecl;
+ st.decl = (struct decl) {
+ Dvar, name, .var = {
+ .ty = ty,
+ .ini = ini,
+ .fnid = P->curfn->id,
+ }
+ };
+ putdecl(P, tok.span, &st.decl);
+ return st;
+}
+
+static struct stmt
+pstifelse(struct parser *P) {
+ struct stmt st = {Sifelse};
+
+ st.ifelse.test = parseexpr(P);
+ if (st.ifelse.test.ty->t != TYbool && st.ifelse.test.ty->t != TYptr)
+ fatal(P, st.ifelse.test.span,
+ "if statement condition must be bool or pointer");
+ lexexpect(P, '{');
+ st.ifelse.t = parseblock(P).block;
+ if (lexmatch(P, NULL, TKkw_else)) {
+ if (lexmatch(P, NULL, TKkw_if))
+ st.ifelse.f = stmtdup(pstifelse(P));
+ else {
+ lexexpect(P, '{');
+ st.ifelse.f = stmtdup(parseblock(P));
+ }
+ }
+
+ return st;
+}
+
+static struct stmt
+pstfor(struct parser *P) {
+ struct stmt st = {Sfor};
+ struct tok tok;
+
+ if (lexmatch(P, NULL, TKkw_let)) {
+ st.loop.ini = stmtdup(pstlet(P));
+ lexexpect(P, ';');
+ } else if (lexmatch(P, NULL, ';')) {
+ // pass
+ } else {
+ st.loop.ini = stmtdup((struct stmt) {
+ Sexpr, .expr = parseexpr(P)
+ });
+ lexexpect(P, ';');
+ }
+
+ if (lexmatch(P, &tok, ';')) {
+ // for (...; ; ...;) -> for (...; #t; ...;)
+ st.loop.test = (struct expr) { Eboolit, tok.span, ty_bool, .i = 1 };
+ } else {
+ st.loop.test = parseexpr(P);
+ lexexpect(P, ';');
+ }
+
+ if (st.loop.test.ty->t != TYbool && st.loop.test.ty->t != TYptr)
+ fatal(P, st.loop.test.span,
+ "for statement condition must be bool or pointer");
+
+ if (!lexmatch(P, &tok, '{')) {
+ st.loop.next = exprdup(parseexpr(P));
+ lexexpect(P, '{');
+ }
+ st.loop.body = parseblock(P).block;
+ return st;
+}
+
+static struct stmt parsestmt(struct parser *P);
+
+static struct stmt
+pstiswitch(struct parser *P, const struct expr *test) {
+ struct stmt st = {Siswitch};
+ struct tok tok;
+ vec_t(struct iswitchcase) cs = {0};
+ struct blockstmt *f = NULL;
+
+ lexexpect(P, '{');
+ while (!lexmatch(P, &tok, '}')) {
+ struct iswitchcase c;
+ vec_t(struct expr) es = {0};
+
+ lexexpect(P, TKkw_case);
+
+ if (lexmatch(P, &tok, TKkw_else)) {
+ if (f)
+ fatal(P, tok.span, "duplicate 'case else' block");
+ f = malloc(sizeof *f);
+ *f = parseblock0(P);
+ } else {
+ do {
+ struct expr ex = parseexpr(P);
+ assert(ex.ty->t == TYint);
+ vec_push(&es, ex);
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, TKkw_do);
+ break;
+ }
+ } while (!lexmatch(P, &tok, TKkw_do)) ;
+ vec_slice_cpy(&c.es, &es);
+ c.t = parseblock0(P);
+ vec_push(&cs, c);
+ }
+ }
+
+ st.iswitch.test = *test;
+ vec_slice_cpy(&st.iswitch.cs, &cs);
+ st.iswitch.f = f;
+ return st;
+}
+
+static struct stmt
+pstswitch(struct parser *P) {
+ struct tok tok;
+ struct expr test;
+
+ if (!lexmatch(P, &tok, '{')) {
+ test = parseexpr(P);
+ if (test.ty->t == TYint)
+ return pstiswitch(P, &test);
+ else
+ assert(0 && "NYI");
+ } else {
+ assert(0 && "NYI");
+ }
+}
+
+static void parsedecl(struct decl *, struct parser *, bool toplevel);
+
+static bool
+isdecltokt(int tokt) {
+ switch (tokt)
+ case TKkw_extern: case TKkw_fn: case TKkw_typedef: case TKkw_defmacro:
+ return 1;
+ return 0;
+}
+
+static void
+mergetoktrees(struct toktree *dst, int n) {
+ size_t total = 0;
+ for (int i = 0; i < n; ++i) {
+ total += dst[i].n;
+ if (i > 0)
+ ++total;
+ }
+ dst->d = xrealloc(dst->d, total * sizeof (struct tok));
+ for (int i = 1; i < n; ++i) {
+ dst->d[dst->n++] = (struct tok) { ',' };
+ memcpy(dst->d + dst->n, dst[i].d, dst[i].n * sizeof (struct tok));
+ dst->n += dst[i].n;
+ }
+ assert(dst->n == total);
+}
+
+static void
+parseexpandmacro(struct parser *P, const struct macro *macro) {
+ vec_t(struct toktree) args = {0};
+ struct tok tok;
+ struct expan expan = {0};
+ struct expanarg *expanargs;
+ struct macrocase c;
+
+ expan.span = lexpeek(P).span;
+ lexexpect(P, '(');
+ while (!lexmatch(P, &tok, ')')) {
+ int pabal = 0, // ( ) parens balance
+ bkbal = 0, // [ ] brackets ..
+ bcbal = 0; // { } braces ..
+ struct span espan = tok.span;
+ vec_t(struct tok) toks = {0};
+ struct toktree ttoks;
+
+ for (;;) {
+ tok = lex(P);
+ switch (tok.t) {
+ case '[': ++bkbal; break;
+ case ']': --bkbal; break;
+ case '{': ++bcbal; break;
+ case '}': --bcbal; break;
+ case '(': ++pabal; break;
+ case ')':
+ if (--pabal >= 0)
+ break;
+ goto arg_done;
+ case ',':
+ if (!pabal && !bkbal && !bcbal)
+ goto arg_done;
+ break;
+ case TKeof:
+ fatal(P, espan, "unterminated macro invokation");
+ }
+ vec_push(&toks, tok);
+ }
+ arg_done:
+
+ vec_slice_cpy(&ttoks, &toks);
+ vec_push(&args, ttoks);
+
+ if (tok.t != ',') {
+ assert(tok.t == ')');
+ break;
+ }
+ }
+
+ for (int i = 0; i < macro->cs.n; ++i) {
+ c = macro->cs.d[i];
+ if (!c.variadic && args.length == c.params.n) {
+ goto ok;
+ } else if (c.variadic && args.length >= c.params.n - 1) {
+ int n = args.length - (c.params.n - 1);
+ if (n == 0) {
+ vec_push(&args, (struct toktree){0});
+ } else {
+ mergetoktrees(args.data + c.params.n - 1, n);
+ }
+ goto ok;
+ }
+ }
+ fatal(P, tok.span, "no match for invoking macro `%s' with %d arguments",
+ macro->name, args.length);
+ok:
+ expanargs = xcalloc(c.params.n, sizeof *expanargs);
+ for (int i = 0; i < c.params.n; ++i) {
+ expanargs[i].name = c.params.d[i];
+ expanargs[i].toks = args.data[i];
+ }
+ expan.prev = P->curexpan;
+ expan.name = macro->name;
+ expan.args.d = expanargs;
+ expan.args.n = c.params.n;
+ expan.toks = c.body;
+ ++P->expanno;
+ P->curexpan = memcpy(xmalloc(sizeof expan), &expan, sizeof expan);
+}
+
+static struct stmt
+parsestmt(struct parser *P) {
+ struct stmt st = {0};
+ struct tok tok;
+
+ if (lexmatch(P, NULL, '{')) {
+ return parseblock(P);
+ } else if (lexmatch(P, NULL, TKkw_let)) {
+ st = pstlet(P);
+ lexexpect(P, ';');
+ } else if (lexmatch(P, NULL, TKkw_if)) {
+ st = pstifelse(P);
+ } else if (lexmatch(P, NULL, TKkw_while)) {
+ st.t = Swhile;
+ st.loop.test = parseexpr(P);
+ if (st.loop.test.ty->t != TYbool && st.loop.test.ty->t != TYptr)
+ fatal(P, st.loop.test.span,
+ "while statement condition must be bool or pointer");
+ lexexpect(P, '{');
+ st.loop.body = parseblock(P).block;
+ } else if (lexmatch(P, NULL, TKkw_for)) {
+ st = pstfor(P);
+ } else if (lexmatch(P, NULL, TKkw_switch)) {
+ st = pstswitch(P);
+ } else if (lexmatch(P, &tok, TKkw_return)) {
+ st.t = Sreturn;
+ if (!lexmatch(P, NULL, ';')) {
+ st.retex = exprdup(parseexpr(P));
+ lexexpect(P, ';');
+ if (P->curfn->retty == ty_void)
+ fatal(P, st.retex->span,
+ "return statement in void function must not return a value");
+ } else if (P->curfn->retty != ty_void) {
+ fatal(P, tok.span,
+ "return statement in non-void function must return a value");
+ }
+ } else if (isdecltokt((tok = lexpeek(P)).t)) {
+ st.t = Sdecl;
+ parsedecl(&st.decl, P, 0);
+ if (st.decl.t == Dfn && !st.decl.externp && !st.decl.fn.body)
+ fatal(P, tok.span,
+ "cannot forward-declare non-external function inside another function");
+
+ if (st.decl.t == Dfn && st.decl.externp && st.decl.fn.body)
+ fatal(P, tok.span,
+ "cannot define external function inside another function");
+ } else {
+ struct expr ex;
+
+ if ((tok = lexpeek(P)).t == TKident) {
+ const struct decl *decl;
+ if ((decl = finddecl(P, tok.str)) && decl->t == Dmacro) {
+ lex(P);
+ parseexpandmacro(P, &decl->macro);
+ return parsestmt(P);
+ }
+ }
+
+ ex = parseexpr(P);
+
+ lexexpect(P, ';');
+ st.t = Sexpr;
+ st.expr = ex;
+ }
+ return st;
+}
+
+static struct blockstmt
+parseblock0(struct parser *P) {
+ vec_t(struct stmt) stmts = {0};
+ struct blockstmt block = {0};
+ int tokt;
+
+ block.env.parent = P->curenv;
+ pushenv(P, &block.env);
+ while ((tokt = lexpeek(P).t) != '}' && tokt != TKkw_case && tokt != ')') {
+ if (lexmatch(P, NULL, ';'))
+ continue;
+ vec_push(&stmts, parsestmt(P));
+ }
+
+ vec_slice_cpy(&block.stmts, &stmts);
+
+ popenv(P);
+ return block;
+}
+
+static struct stmt
+parseblock(struct parser *P) {
+ struct stmt st = { Sblock };
+ st.block = parseblock0(P);
+ lexexpect(P, '}');
+ return st;
+}
+
+static const struct type *
+fntype(const struct fn *fn) {
+ struct type ty = { TYfn, 0, g_targ.sizesize };
+ vec_t(const struct type *) params = {0};
+ ty.fn.retty = fn->retty;
+ for (int i = 0; i < fn->params.n; ++i)
+ vec_push(&params, fn->params.d[i].ty);
+ vec_slice_cpy(&ty.fn.params, &params);
+ ty.fn.variadic = fn->variadic;
+ return interntype(ty);
+}
+
+static void
+parsefn(struct decl *decl, struct parser *P) {
+ vec_t(struct fnparam) params = {0};
+ struct tok tok;
+ struct fn *fn = &decl->fn;
+ const char *name = fn->name;
+ memset(fn, 0, sizeof *fn);
+ fn->name = name;
+
+ lexexpect(P, '(');
+ while (!lexmatch(P, NULL, ')')) {
+ struct fnparam param;
+ if (lexmatch(P, NULL, '...')) {
+ fn->variadic = 1;
+ } else {
+ param.name = lexexpects(P, TKident, "parameter name").str;
+ param.ty = parsetype(P);
+ vec_push(&params, param);
+ }
+ if (!lexmatch(P, NULL, ',')) {
+ lexexpect(P, ')');
+ break;
+ } else if (fn->variadic) {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ vec_slice_cpy(&fn->params, &params);
+ fn->retty = unconstify(parsetype(P));
+ fn->selfty = fntype(fn);
+ if (!lexmatch(P, &tok, ';')) {
+ struct env *env = xcalloc(1, sizeof *env);
+ static int id;
+ fn->id = id++;
+ lexexpect(P, '{');
+ env->parent = P->curenv;
+
+ WITH_TMPCHANGE(struct fn *, P->curfn, fn) {
+ pushenv(P, env);
+ putdecl(P, tok.span, &(struct decl) {
+ Dfn, fn->name, .fn = *fn, ._cname = decl->_cname
+ });
+ for (int i = 0; i < params.length; ++i) {
+ struct decl decl = {
+ Dvar, params.data[i].name, .var = {
+ .ty = params.data[i].ty,
+ .fnid = fn->id,
+ }
+ };
+ putdecl(P, tok.span, &decl);
+ }
+ fn->body = xcalloc(1, sizeof *fn->body);
+ *fn->body = parseblock(P);
+ popenv(P);
+ }
+ }
+}
+
+static struct macrocase
+parsemacrocase(struct parser *P) {
+ struct macrocase c = {0};
+ vec_t(const char *) params = {0};
+ vec_t(struct tok) body = {0};
+ struct tok tok;
+ int pabal = 0, // ( ) parens balance
+ bkbal = 1, // [ ] brackets ..
+ bcbal = 0; // { } braces ..
+ struct span espan;
+ static int gensymid;
+ struct gensyms {
+ struct gensyms *next;
+ const char *name;
+ struct tok tok;
+ } *gensyms = NULL;
+
+ lexexpect(P, '(');
+ while (!lexmatch(P, &tok, ')')) {
+ if (lexmatch(P, &tok, '...')) {
+ vec_push(&params, lexexpects(P, TKident, "parameter name").str);
+ c.variadic = 1;
+ lexmatch(P, &tok, ',');
+ lexexpect(P, ')');
+ break;
+ } else {
+ vec_push(&params, lexexpects(P, TKident, "parameter name").str);
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ }
+
+ espan = tok.span;
+
+ lexexpect(P, '[');
+ while (bkbal || pabal || bcbal) {
+ tok = lex(P);
+ switch (tok.t) {
+ case '(': ++pabal; break;
+ case ')': --pabal; break;
+ case '[': ++bkbal; break;
+ case ']': --bkbal; break;
+ case '{': ++bcbal; break;
+ case '}': --bcbal; break;
+ case TKeof:
+ fatal(P, espan, "unterminated macro definition");
+ }
+ if (tok.t == TKgensym) {
+ struct gensyms *gs;
+ for (gs = gensyms; gs; gs = gs->next) {
+ if (!strcmp(gs->name, tok.str)) {
+ tok = gs->tok;
+ goto found;
+ }
+ }
+ gs = xcalloc(1, sizeof *gs);
+ gs->name = tok.str;
+ gs->tok.t = TKident;
+ gs->tok.span = tok.span;
+ gs->tok.str = xmalloc(strlen(tok.str) + 24);
+ sprintf((char *)gs->tok.str, "__fG%s_%d", tok.str, gensymid++);
+ tok = gs->tok;
+ gs->next = gensyms;
+ gensyms = gs;
+ }
+ found:
+ vec_push(&body, tok);
+ }
+ (void)vec_pop(&body);
+
+ vec_slice_cpy(&c.params, &params);
+ vec_slice_cpy(&c.body, &body);
+ return c;
+}
+
+static struct macro
+parsemacro(struct parser *P) {
+ struct tok tok;
+ struct macro macro;
+ vec_t(struct macrocase) cs = {0};
+
+ if (lexpeek(P).t == '(') {
+ vec_push(&cs, parsemacrocase(P));
+ } else {
+ lexexpect(P, '{');
+ while (!lexmatch(P, &tok, '}')) {
+ vec_push(&cs, parsemacrocase(P));
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+ }
+
+ if (cs.length == 0)
+ fatal(P, tok.span, "macro must have at least one case");
+
+ vec_slice_cpy(&macro.cs, &cs);
+ return macro;
+}
+
+static void
+parsedecl(struct decl *decl, struct parser *P, bool toplevel) {
+ struct tok tok = { .span = P->tokspan };
+ bool externp = 0;
+ memset(decl, 0, sizeof *decl);
+
+ if (lexmatch(P, &tok, TKkw_extern)) {
+ externp = 1;
+ }
+
+ if (lexmatch(P, &tok, TKkw_fn)) {
+ const char *name = lexexpects(P, TKident, "function name").str;
+
+ decl->t = Dfn;
+ decl->name = decl->fn.name = name;
+ decl->_cname = xcalloc(1, sizeof (char *));
+ parsefn(decl, P);
+ decl->externp = externp;
+ } else if (lexmatch(P, &tok, TKkw_typedef)) {
+ decl->t = Dtype;
+ decl->name = lexexpects(P, TKident, "typedef name").str;
+ decl->ty = parsetype(P);
+ decl->_cname = xcalloc(1, sizeof(char *));
+ lexexpect(P, ';');
+ } else if (lexmatch(P, &tok, TKkw_defmacro)) {
+ const char *name = lexexpects(P, TKident, "macro name").str;
+ decl->t = Dmacro;
+ decl->name = name;
+ decl->macro = parsemacro(P);
+ decl->macro.name = name;
+
+ } else {
+ fatal(P, tok.span, "expected declaration (near %s)",
+ tok2str(tok));
+ }
+
+ decl->span = tok.span;
+ putdecl(P, tok.span, decl);
+}
+
+void
+parse(struct transunit *tu, struct parser *P) {
+ vec_t(struct decl) decls = {0};
+ int i = 0;
+ while (!P->eof && lexpeek(P).t != TKeof) {
+ vec_push(&decls, (struct decl){0});
+ parsedecl(&decls.data[i++], P, 1);
+ }
+ vec_slice_cpy(&tu->decls, &decls);
+}
+
+void
+initparser(struct parser *P, const char *fname) {
+ assert(NUM_LEXTOKENS - 1 < '!');
+ memset(P, 0, sizeof *P);
+ P->curfile = fname;
+ if (!(P->fp = fopen(fname, "r")))
+ perror(fname), exit(1);
+ P->tokspan = P->curspan = (struct span) { addfilepath(fname), 0, 1, 1 };
+ P->peekchr = -1;
+ P->curenv = P->tlenv = xcalloc(1, sizeof *P->tlenv);
+ P->primenv = xcalloc(1, sizeof *P->primenv);
+ putprimtypes(P->primenv);
+}
diff --git a/bootstrap/test.cff b/bootstrap/test.cff
new file mode 100644
index 0000000..7749345
--- /dev/null
+++ b/bootstrap/test.cff
@@ -0,0 +1,41 @@
+
+defmacro MAX(x, y) [ ((x) < (y) ? (y) : (x)) ]
+
+defmacro fmt(fmt, ...args) [ printf(fmt, args) ]
+defmacro add {
+(x) [ (x) ],
+(x, y, ...rest) [ (x) + add(y, rest) ]
+}
+
+defmacro swap(x, y) [{
+ let $x = &(x);
+ let $y = &(y);
+ let $z = *($x);
+ *($x) = *($y);
+ *($y) = ($z);
+}]
+
+fn fact(x usize) usize {
+ fn f(acc usize, n usize) usize {
+ return n == 0 ? acc : f(acc * n, n - 1);
+ }
+ return f(1, x);
+}
+
+extern fn main (argc int, argv **u8) int {
+ extern fn printf(fmt *const u8, ...) int;
+ fmt("%d\n", add(1, 2, 3, 4));
+
+ let x = 0;
+ let y = 7;
+ printf("x: %d; y: %d\n", x, y);
+ swap(x, y);
+ printf("x: %d; y: %d\n", x, y);
+ printf("fact(6) = %zu\n", fact(6));
+
+ fn foo(n int) int {
+ return n + 1;
+ }
+
+ return foo(-1);
+}
diff --git a/bootstrap/types.c b/bootstrap/types.c
new file mode 100644
index 0000000..8e735d0
--- /dev/null
+++ b/bootstrap/types.c
@@ -0,0 +1,208 @@
+#include "all.h"
+
+// set of interned types
+static struct {
+ int size, nbuckets;
+ struct typesnode {
+ struct typesnode *next;
+ u32 hash;
+ const struct type ty;
+ } **buckets;
+ char *tags;
+} types;
+
+void
+inittypes() {
+ for (int i = 0; i < types.nbuckets; ++i) {
+ for (struct typesnode *n = types.buckets[i], *next; n; n = next) {
+ next = n->next;
+ free(n);
+ }
+ }
+ if (types.buckets)
+ free(types.buckets);
+
+ types.size = 0;
+ types.buckets = calloc(types.nbuckets = 16, sizeof(struct typesnode *));
+}
+
+static u32
+hashtype(const struct type *ty) {
+ u32 h = 0;
+ h = jkhashv(h, ty->t);
+ h = jkhashv(h, ty->size);
+ h = jkhashv(h, ty->align);
+ h = jkhashv(h, ty->konst);
+ switch (ty->t) {
+ case TYvoid: case TYbool: case TYfloat:
+ break;
+ case TYint:
+ h = jkhashv(h, ty->int_signed);
+ break;
+ case TYptr:
+ case TYslice:
+ h = jkhashv(h, ty->child);
+ break;
+ case TYarr:
+ h = jkhashv(h, ty->child);
+ h = jkhashv(h, ty->length);
+ break;
+ case TYfn:
+ h = jkhashv(h, ty->fn.retty);
+ h = jkhashv(h, ty->fn.params.n);
+ for (int i = 0; i < ty->fn.params.n; ++i)
+ h = jkhashv(h, ty->fn.params.d[i]);
+
+ }
+ return h;
+}
+
+bool
+typeeql(const struct type *lhs, const struct type *rhs) {
+ if (lhs == rhs)
+ return 1;
+ if (lhs->t != rhs->t || lhs->size != rhs->size
+ || lhs->align != rhs->align
+ || lhs->konst != rhs->konst)
+ return 0;
+ switch (lhs->t) {
+ case TYvoid: case TYbool: case TYfloat:
+ return 1;
+ case TYint:
+ return lhs->int_signed == rhs->int_signed;
+ case TYptr:
+ case TYslice:
+ return typeeql(lhs->child, rhs->child);
+ case TYarr:
+ return lhs->length == rhs->length && typeeql(lhs->child, rhs->child);
+ case TYfn:
+ if (lhs->length != rhs->length)
+ return 0;
+ for (int i = 0; i < lhs->fn.params.n; ++i)
+ if (!typeeql(lhs->fn.params.d[i], rhs->fn.params.d[i]))
+ return 0;
+ return 1;
+ }
+ assert(0 && "unreachable");
+}
+
+static const struct type *
+typesfind(const struct type *key) {
+ u32 hash = hashtype(key);
+ size_t idx = hash & (types.nbuckets - 1);
+ for (struct typesnode *n = types.buckets[idx]; n; n = n->next) {
+ if (n->hash == hash && typeeql(key, &n->ty))
+ return &n->ty;
+ }
+ return NULL;
+}
+
+// !!Invariant: type should not be in the types set!
+static const struct type *
+typesput(const struct type *key) {
+ u32 hash = hashtype(key);
+ size_t idx = hash & (types.nbuckets - 1);
+ struct typesnode *n = xmalloc(sizeof *n);
+ types.size++;
+ n->hash = hash;
+ *(struct type *)&n->ty = *key;
+ n->next = types.buckets[idx];
+ types.buckets[idx] = n;
+ return &n->ty;
+}
+
+const struct type *
+interntype(struct type ty) {
+ if (!ty.align)
+ ty.align = ty.size;
+ const struct type *ty2 = typesfind(&ty);
+ if (ty2)
+ return ty2;
+ ty2 = typesput(&ty);
+ assert(ty2);
+ return ty2;
+}
+
+bool
+completetype(const struct type *ty) {
+ if (ty->t == TYvoid)
+ return 0;
+ if (ty->t == TYfn)
+ return 0;
+ if (ty->t == TYarr && ty->length < 0)
+ return 0;
+ return 1;
+}
+
+const struct type
+ *ty_void, *ty_bool, *ty_f32, *ty_f64,
+ *ty_i8, *ty_u8, *ty_i16, *ty_u16,
+ *ty_i32, *ty_u32, *ty_i64, *ty_u64,
+ *ty_int, *ty_uint, *ty_isize, *ty_usize,
+ *ty_iptrint, *ty_uptrint, *ty_c_int, *ty_c_uint,
+ *ty_c_char, *ty_c_schar, *ty_c_uchar, *ty_c_short,
+ *ty_c_ushort, *ty_c_long, *ty_c_ulong, *ty_c_llong,
+ *ty_c_ullong;
+
+void
+putprimtypes(struct env *env) {
+ const struct targ t = g_targ;
+ const struct {
+ const char *name;
+ const struct type **gty;
+ struct type ty;
+ } types[] = {
+ /* name, t, size, align (0 -> = size), ... */
+ {"void", &ty_void, {TYvoid, 0, 1}},
+ {"bool", &ty_bool, {TYbool, 1}},
+ {"f32", &ty_f32, {TYfloat, 4, t.f32align}},
+ {"f64", &ty_f64, {TYfloat, 8, t.f64align}},
+#define IS .int_signed = 1
+#define IU .int_signed = 0
+ {"i8", &ty_i8, {TYint, 1, 1, IS}},
+ {"u8", &ty_u8, {TYint, 1, 1, IU}},
+ {"i16", &ty_i16, {TYint, 2, 2, IS}},
+ {"u16", &ty_u16, {TYint, 2, 2, IU}},
+ {"i32", &ty_i32, {TYint, 4, 4, IS}},
+ {"u32", &ty_u32, {TYint, 4, 4, IU}},
+ {"i64", &ty_i64, {TYint, 8, 8, IS}},
+ {"u64", &ty_u64, {TYint, 8, 8, IU}},
+ {"int", &ty_int, {TYint, t.intsize, IS}},
+ {"uint", &ty_uint, {TYint, t.intsize, IU}},
+ {"isize", &ty_isize, {TYint, t.sizesize, IS}},
+ {"usize", &ty_usize, {TYint, t.sizesize, IU}},
+ {"iptrint", &ty_iptrint, {TYint, t.ptrsize, IS}},
+ {"uptrint", &ty_uptrint, {TYint, t.ptrsize, IU}},
+ {"c_int", &ty_c_int, {TYint, t.intsize, IS}},
+ {"c_uint", &ty_c_uint, {TYint, t.intsize, IU}},
+ {"c_char", &ty_c_char, {TYint, 1, .int_signed = t.charsigned}},
+ {"c_schar", &ty_c_schar, {TYint, 1, IS}},
+ {"c_uchar", &ty_c_uchar, {TYint, 1, IU}},
+ {"c_short", &ty_c_short, {TYint, 2, IS}},
+ {"c_ushort", &ty_c_ushort, {TYint, 2, IU}},
+ {"c_long", &ty_c_long, {TYint, t.longsize, IS}},
+ {"c_ulong", &ty_c_ulong, {TYint, t.longsize, IU}},
+ {"c_llong", &ty_c_llong, {TYint, t.llongsize, IS}},
+ {"c_ullong", &ty_c_ullong, {TYint, t.llongsize, IU}},
+#undef IS
+#undef IU
+ };
+
+ for (int i = 0; i < ARRAY_LENGTH(types); ++i) {
+ struct decl decl = {
+ Dtype,
+ types[i].name,
+ .ty = interntype(types[i].ty),
+ };
+ bool ok = envput(env, &decl);
+ *types[i].gty = decl.ty;
+ assert(ok && "envput prim type");
+ }
+}
+
+void
+visittypes(void (*visitor)(const struct type *, void *), void *arg) {
+ for (int i = 0; i < types.nbuckets; ++i)
+ for (struct typesnode *n = types.buckets[i]; n; n = n->next)
+ visitor(&n->ty, arg);
+}
diff --git a/bootstrap/util.c b/bootstrap/util.c
new file mode 100644
index 0000000..4aebbe6
--- /dev/null
+++ b/bootstrap/util.c
@@ -0,0 +1,105 @@
+#include "all.h"
+
+// Bob Jenkins's one_at_a_time hash
+u32
+jkhash(u32 h, const u8 *data, size_t length) {
+ size_t i = 0;
+ while (i != length) {
+ h += data[i++];
+ h += h << 10;
+ h ^= h >> 6;
+ }
+ h += h << 3;
+ h ^= h >> 11;
+ h += h << 15;
+ return h;
+}
+
+static const char *filepaths[100];
+static int nfilepaths = 0;
+
+int addfilepath(const char *s) {
+ for (int i = 0; i < nfilepaths; ++i)
+ if (!strcmp(filepaths[i], s))
+ return i;
+
+ assert(nfilepaths < sizeof filepaths);
+ filepaths[nfilepaths] = s;
+ return nfilepaths++;
+}
+
+const char *fileid2path(int id) {
+ assert(id < nfilepaths);
+ return filepaths[id];
+}
+
+void *
+xmalloc(size_t n) {
+ void *p = malloc(n);
+ assert(p && "malloc");
+ return p;
+}
+
+void *
+xcalloc(size_t n, size_t m) {
+ void *p = calloc(n,m);
+ assert(p && "calloc");
+ return p;
+}
+
+void *
+xrealloc(void *p, size_t n) {
+ if (!p)
+ return xmalloc(n);
+ if (!n)
+ return free(p), NULL;
+ p = realloc(p, n);
+ assert(p && "realloc");
+ return p;
+}
+
+char *
+xasprintf(const char *fmt, ...) {
+ va_list ap, aq;
+ int n = 32, m;
+ char *str = xcalloc(n, 1);
+ va_start(ap, fmt);
+ m = vsnprintf(str, n, fmt, ap) + 1;
+ if (m > n) {
+ va_copy(aq, ap);
+ str = xrealloc(str, m);
+ vsprintf(str, fmt, ap);
+ va_end(aq);
+ }
+ va_end(ap);
+ return str;
+}
+
+char *
+xstrdup(const char *s) {
+ return strcpy(xmalloc(strlen(s) + 1), s);
+}
+
+
+void noreturn
+fatal(struct parser *P, struct span span, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int i = 0;
+
+ fprintf(stderr, "%s:%d:%d: error: ", fileid2path(span.fileid), span.line, span.col);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ for (struct expan *ep = P->curexpan; ep; ep = ep->prev, ++i) {
+ if (ep->name && (i < 8 || !ep->prev || !ep->prev->prev)) {
+ span = ep->span;
+ fprintf(stderr, " while expanding macro `%s' at %s:%d:%d\n",
+ ep->name,
+ fileid2path(ep->span.fileid), span.line, span.col);
+ } else if (ep->name && i == 10) {
+ fprintf(stderr, " ... (some expansions omitted)\n");
+ }
+ }
+ va_end(ap);
+ exit(127);
+}
diff --git a/bootstrap/vec.c b/bootstrap/vec.c
new file mode 100644
index 0000000..399104e
--- /dev/null
+++ b/bootstrap/vec.c
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2014 rxi
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include "vec.h"
+
+
+int vec_expand_(char **data, int *length, int *capacity, int memsz) {
+ if (*length + 1 > *capacity) {
+ void *ptr;
+ int n = (*capacity == 0) ? 1 : *capacity << 1;
+ ptr = realloc(*data, n * memsz);
+ if (ptr == NULL) return -1;
+ *data = ptr;
+ *capacity = n;
+ }
+ return 0;
+}
+
+
+int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n) {
+ (void) length;
+ if (n > *capacity) {
+ void *ptr = realloc(*data, n * memsz);
+ if (ptr == NULL) return -1;
+ *data = ptr;
+ *capacity = n;
+ }
+ return 0;
+}
+
+
+int vec_reserve_po2_(
+ char **data, int *length, int *capacity, int memsz, int n
+) {
+ int n2 = 1;
+ if (n == 0) return 0;
+ while (n2 < n) n2 <<= 1;
+ return vec_reserve_(data, length, capacity, memsz, n2);
+}
+
+
+int vec_compact_(char **data, int *length, int *capacity, int memsz) {
+ if (*length == 0) {
+ free(*data);
+ *data = NULL;
+ *capacity = 0;
+ return 0;
+ } else {
+ void *ptr;
+ int n = *length;
+ ptr = realloc(*data, n * memsz);
+ if (ptr == NULL) return -1;
+ *capacity = n;
+ *data = ptr;
+ }
+ return 0;
+}
+
+
+int vec_insert_(char **data, int *length, int *capacity, int memsz,
+ int idx
+) {
+ int err = vec_expand_(data, length, capacity, memsz);
+ if (err) return err;
+ memmove(*data + (idx + 1) * memsz,
+ *data + idx * memsz,
+ (*length - idx) * memsz);
+ return 0;
+}
+
+
+void vec_splice_(char **data, int *length, int *capacity, int memsz,
+ int start, int count
+) {
+ (void) capacity;
+ memmove(*data + start * memsz,
+ *data + (start + count) * memsz,
+ (*length - start - count) * memsz);
+}
+
+
+void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
+ int start, int count
+) {
+ (void) capacity;
+ memmove(*data + start * memsz,
+ *data + (*length - count) * memsz,
+ count * memsz);
+}
+
+
+void vec_swap_(char **data, int *length, int *capacity, int memsz,
+ int idx1, int idx2
+) {
+ unsigned char *a, *b, tmp;
+ int count;
+ (void) length;
+ (void) capacity;
+ if (idx1 == idx2) return;
+ a = (unsigned char*) *data + idx1 * memsz;
+ b = (unsigned char*) *data + idx2 * memsz;
+ count = memsz;
+ while (count--) {
+ tmp = *a;
+ *a = *b;
+ *b = tmp;
+ a++, b++;
+ }
+}
+
diff --git a/bootstrap/vec.h b/bootstrap/vec.h
new file mode 100644
index 0000000..bec9658
--- /dev/null
+++ b/bootstrap/vec.h
@@ -0,0 +1,182 @@
+/**
+ * Copyright (c) 2014 rxi
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MIT license. See LICENSE for details.
+ */
+
+#ifndef VEC_H
+#define VEC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#define VEC_VERSION "0.2.1"
+
+
+#define vec_unpack_(v)\
+ (char**)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data)
+
+
+#define vec_t(T)\
+ struct { T *data; int length, capacity; }
+
+
+#define vec_init(v)\
+ memset((v), 0, sizeof(*(v)))
+
+
+#define vec_deinit(v)\
+ ( free((v)->data),\
+ vec_init(v) )
+
+
+#define vec_push(v, val)\
+ (void)( vec_expand_(vec_unpack_(v)) ? -1 :\
+ ((v)->data[(v)->length++] = (val), 0), 0 )
+
+
+#define vec_pop(v)\
+ (v)->data[--(v)->length]
+
+
+#define vec_splice(v, start, count)\
+ ( vec_splice_(vec_unpack_(v), start, count),\
+ (v)->length -= (count) )
+
+
+#define vec_swapsplice(v, start, count)\
+ ( vec_swapsplice_(vec_unpack_(v), start, count),\
+ (v)->length -= (count) )
+
+
+#define vec_insert(v, idx, val)\
+ ( vec_insert_(vec_unpack_(v), idx) ? -1 :\
+ ((v)->data[idx] = (val), 0), (v)->length++, 0 )
+
+
+#define vec_sort(v, fn)\
+ qsort((v)->data, (v)->length, sizeof(*(v)->data), fn)
+
+
+#define vec_swap(v, idx1, idx2)\
+ vec_swap_(vec_unpack_(v), idx1, idx2)
+
+
+#define vec_truncate(v, len)\
+ ((v)->length = (len) < (v)->length ? (len) : (v)->length)
+
+
+#define vec_clear(v)\
+ ((v)->length = 0)
+
+
+#define vec_first(v)\
+ (v)->data[0]
+
+
+#define vec_last(v)\
+ (v)->data[(v)->length - 1]
+
+
+#define vec_reserve(v, n)\
+ vec_reserve_(vec_unpack_(v), n)
+
+
+#define vec_compact(v)\
+ ( vec_compact_(vec_unpack_(v)),\
+ (v)->data )
+
+
+#define vec_pusharr(v, arr, count)\
+ do {\
+ int i__, n__ = (count);\
+ if (vec_reserve_po2_(vec_unpack_(v), (v)->length + n__) != 0) break;\
+ for (i__ = 0; i__ < n__; i__++) {\
+ (v)->data[(v)->length++] = (arr)[i__];\
+ }\
+ } while (0)
+
+
+#define vec_extend(v, v2)\
+ vec_pusharr((v), (v2)->data, (v2)->length)
+
+
+#define vec_find(v, val, idx)\
+ do {\
+ for ((idx) = 0; (idx) < (v)->length; (idx)++) {\
+ if ((v)->data[(idx)] == (val)) break;\
+ }\
+ if ((idx) == (v)->length) (idx) = -1;\
+ } while (0)
+
+
+#define vec_remove(v, val)\
+ do {\
+ int idx__;\
+ vec_find(v, val, idx__);\
+ if (idx__ != -1) vec_splice(v, idx__, 1);\
+ } while (0)
+
+
+#define vec_reverse(v)\
+ do {\
+ int i__ = (v)->length / 2;\
+ while (i__--) {\
+ vec_swap((v), i__, (v)->length - (i__ + 1));\
+ }\
+ } while (0)
+
+
+#define vec_foreach(v, var, iter)\
+ if ( (v)->length > 0 )\
+ for ( (iter) = 0;\
+ (iter) < (v)->length && (((var) = (v)->data[(iter)]), 1);\
+ ++(iter))
+
+
+#define vec_foreach_rev(v, var, iter)\
+ if ( (v)->length > 0 )\
+ for ( (iter) = (v)->length - 1;\
+ (iter) >= 0 && (((var) = (v)->data[(iter)]), 1);\
+ --(iter))
+
+
+#define vec_foreach_ptr(v, var, iter)\
+ if ( (v)->length > 0 )\
+ for ( (iter) = 0;\
+ (iter) < (v)->length && (((var) = &(v)->data[(iter)]), 1);\
+ ++(iter))
+
+
+#define vec_foreach_ptr_rev(v, var, iter)\
+ if ( (v)->length > 0 )\
+ for ( (iter) = (v)->length - 1;\
+ (iter) >= 0 && (((var) = &(v)->data[(iter)]), 1);\
+ --(iter))
+
+
+
+int vec_expand_(char **data, int *length, int *capacity, int memsz);
+int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n);
+int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz,
+ int n);
+int vec_compact_(char **data, int *length, int *capacity, int memsz);
+int vec_insert_(char **data, int *length, int *capacity, int memsz,
+ int idx);
+void vec_splice_(char **data, int *length, int *capacity, int memsz,
+ int start, int count);
+void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
+ int start, int count);
+void vec_swap_(char **data, int *length, int *capacity, int memsz,
+ int idx1, int idx2);
+
+
+typedef vec_t(void*) vec_void_t;
+typedef vec_t(char*) vec_str_t;
+typedef vec_t(int) vec_int_t;
+typedef vec_t(char) vec_char_t;
+typedef vec_t(float) vec_float_t;
+typedef vec_t(double) vec_double_t;
+
+#endif