diff options
Diffstat (limited to 'bootstrap')
| -rw-r--r-- | bootstrap/.gitignore | 4 | ||||
| -rw-r--r-- | bootstrap/all.h | 415 | ||||
| -rwxr-xr-x | bootstrap/build.sh | 10 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 417 | ||||
| -rw-r--r-- | bootstrap/dump.c | 384 | ||||
| -rw-r--r-- | bootstrap/env.c | 48 | ||||
| -rw-r--r-- | bootstrap/main.c | 14 | ||||
| -rw-r--r-- | bootstrap/parse.c | 1733 | ||||
| -rw-r--r-- | bootstrap/test.cff | 41 | ||||
| -rw-r--r-- | bootstrap/types.c | 208 | ||||
| -rw-r--r-- | bootstrap/util.c | 105 | ||||
| -rw-r--r-- | bootstrap/vec.c | 114 | ||||
| -rw-r--r-- | bootstrap/vec.h | 182 |
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(¶ms, fn->params.d[i].ty); + vec_slice_cpy(&ty.fn.params, ¶ms); + 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(¶ms, param); + } + if (!lexmatch(P, NULL, ',')) { + lexexpect(P, ')'); + break; + } else if (fn->variadic) { + lexexpect(P, ')'); + break; + } + } + vec_slice_cpy(&fn->params, ¶ms); + 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(¶ms, lexexpects(P, TKident, "parameter name").str); + c.variadic = 1; + lexmatch(P, &tok, ','); + lexexpect(P, ')'); + break; + } else { + vec_push(¶ms, 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, ¶ms); + 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(¯o.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 |