aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap/all.h4
-rw-r--r--bootstrap/env.c4
-rw-r--r--bootstrap/parse.c25
-rw-r--r--bootstrap/types.c16
-rw-r--r--bootstrap/util.c3
-rw-r--r--examples/hello-world.cff8
-rw-r--r--src/cffc.hff103
-rw-r--r--src/common.hff13
-rw-r--r--src/env.cff23
-rw-r--r--src/fmt.cff121
-rw-r--r--src/libc.hff1
-rw-r--r--src/map.hff27
-rw-r--r--src/mem.hff4
-rw-r--r--src/parse.cff327
-rw-r--r--src/targ.cff1
-rw-r--r--src/type.cff146
-rw-r--r--src/util.cff13
-rw-r--r--src/util.hff2
18 files changed, 754 insertions, 87 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h
index e458c15..b86e7f6 100644
--- a/bootstrap/all.h
+++ b/bootstrap/all.h
@@ -500,7 +500,9 @@ static const struct targ {
i32align,
i64align,
longsize,
+ longalign,
llongsize,
+ llongalign,
sizesize,
f32align,
f64align,
@@ -514,6 +516,8 @@ static const struct targ {
.i32align = alignof(int32_t),
.i64align = alignof(int64_t),
.longsize = sizeof(long),
+ .longalign = alignof(long),
+ .llongalign = alignof(long long),
.llongsize = sizeof(long long),
.sizesize = sizeof(size_t),
.f32align = alignof(float),
diff --git a/bootstrap/env.c b/bootstrap/env.c
index 21d9580..7ab3920 100644
--- a/bootstrap/env.c
+++ b/bootstrap/env.c
@@ -36,11 +36,11 @@ envput(struct env *env, const struct decl *decl) {
if (decl->t != d0->t)
return NULL;
- if (decl == d0 || !memcmp(d0, decl, sizeof *d0))
+ if (decl == d0)
return d0;
if (d0->t == Ddef && decl->t == Ddef && !memcmp(&d0->var, &decl->var, sizeof d0->var))
return d0;
- if (d0->t == Dmacro && !memcmp(&decl->macro, &d0->macro, sizeof decl->macro))
+ if (d0->t == Dmacro && decl->macro.cs.d == d0->macro.cs.d)
return d0;
if (d0->t == Dtype && d0->ty == decl->ty)
return d0;
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index 0c619fc..0d9ab16 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -785,7 +785,7 @@ parsetype(struct parser *P) {
lexexpect(P, ']');
} else if (!lexmatch(P, &tok, ']')) {
struct expr ex = parseexpr(P);
- if (!fold(&ex) || ex.t != Eintlit)
+ if (!fold(&ex) || (ex.t != Eintlit && !(ex.t == Eenumval && ex.ty->enu.lax)))
fatal(P, ex.span,
"array length should be a compile-time integral expression");
if ((length = ex.i) < 0)
@@ -1178,8 +1178,9 @@ pexprimary(struct parser *P) {
} else {
const struct aggfield *fld;
for (i = 0; i < ex.ty->agg.flds.n; ++i)
- if (!strcmp((fld = &ex.ty->agg.flds.d[i])->name, vname))
+ if (!strcmp((fld = &ex.ty->agg.flds.d[i])->name, vname)) {
goto found1;
+ }
fatal(P, tok.span, "tagged union %t contains no variant `%s'", ex.ty, vname);
found1:
P->used_targty = 1;
@@ -1187,6 +1188,7 @@ pexprimary(struct parser *P) {
ex.span = tok.span;
ex.euini.fnam = fld->name;
ex.euini.tag = ex.ty->agg.enumty->enu.vals.d[i].i;
+ P->targty = fld->ty;
if (fld->ty)
ex.euini.ini = exprdup(parseexpr(P));
}
@@ -1288,7 +1290,7 @@ pexpostfix(struct parser *P) {
if (lhs.ty->t != TYarr && lhs.ty->t != TYptr && lhs.ty->t != TYslice)
fatal(P, lhs.span, "indexee is not array or pointer type (%t)",
lhs.ty);
- if (rhs.ty->t != TYint)
+ if (rhs.ty->t != TYint && !(rhs.ty->t == TYenum && rhs.ty->enu.lax))
fatal(P, lhs.span, "index expression type is not integral (%t)",
rhs.ty);
if (lexmatch(P, NULL, '::')) {
@@ -1474,6 +1476,7 @@ pexpostfix(struct parser *P) {
fnam, ex.ty);
}
} else {
+ P->used_targty = 0;
break;
}
@@ -1532,7 +1535,7 @@ pexprefix(struct parser *P) {
if (ex.ty->t != TYptr)
fatal(P, ex.span, "invalid %t operand to dereference, not pointer", ex.ty);
if (!completetype(ex.ty->child) && ex.ty->child->t != TYfn)
- fatal(P, ex.span, "invalid %t operand to dereference, incomplete", ex.ty);
+ fatal(P, ex.span, "invalid %t operand to dereference, incomplete");
return (struct expr) {
Eprefix, tok.span, ex.ty->child, .unop = {
'*', exprdup(ex)
@@ -1541,7 +1544,7 @@ pexprefix(struct parser *P) {
} else if (lexmatch(P, &tok, '&')) {
struct expr ex = pexprefix(P);
struct type ty2 = { TYptr, g_targ.ptrsize, .child = ex.ty};
- if (!islvalue(&ex) && !(ex.t == Ename && ex.ref->t == Dfn))
+ if (!islvalue(&ex) && !(ex.t == Ename && ex.ref->t == Dfn) && !(ex.t == Ezeroini || ex.t == Eini))
fatal(P, ex.span, "invalid operand to `&': not an lvalue");
return (struct expr) {
Eprefix, tok.span, interntype(ty2), .unop = {
@@ -1875,7 +1878,7 @@ parsevardecl(decl_yielder_t yield, void *yarg, struct parser *P, bool let, bool
fatal(P, tok.span, "incompatible initializer type (%t, expected %t)",
ini->ty, ty);
- if (ty->t == TYarr && ty->length < 0) {
+ if (ini && ty->t == TYarr && ty->length < 0) {
struct type ty2 = *ty;
if (ini->t == Ezeroini) {
ty2.length = 0;
@@ -1888,7 +1891,7 @@ parsevardecl(decl_yielder_t yield, void *yarg, struct parser *P, bool let, bool
ty2.size = ty->child->size * ty2.length;
ty2.align = ty->child->align;
uninterntype(ty);
- ty = interntype(ty2);
+ ty = ini->ty = interntype(ty2);
}
if (!completetype(ty) && !(!let && externp))
@@ -2102,8 +2105,8 @@ psteuswitch(struct parser *P, const struct expr *test) {
if (lexpeek(P).t != TKident)
lexexpect(P, TKident);
}
+ struct env env = {P->curenv};
if (c.fld->ty && lexmatch(P, &tok, TKident)) {
- struct env env = {P->curenv};
const struct type *ty = c.fld->ty;
c.capt = tok.str;
@@ -2227,7 +2230,7 @@ parseexpandmacro(struct parser *P, const struct macro *macro) {
vec_t(struct tok) toks = {0};
struct toktree ttoks;
- for (;;) {
+ for (;;) {
tok = lex(P);
switch (tok.t) {
case '[': ++bkbal; break; case ']': --bkbal; break;
@@ -2937,8 +2940,8 @@ doimport(struct parser *P, const char *path) {
for (int i = 0; i < seen.length; ++i) {
if (!strcmp(seen.data[i].s, rpath)) {
if (seen.data[i].wip) {
- epri("warning: \"%s\": circular import detected: \"%s\" being processed\n",
- P->curfile, path);
+ // epri("warning: \"%s\": circular import detected: \"%s\" being processed\n",
+ // P->curfile, path);
}
return seen.data[i].cf;
}
diff --git a/bootstrap/types.c b/bootstrap/types.c
index dce85a6..788f001 100644
--- a/bootstrap/types.c
+++ b/bootstrap/types.c
@@ -180,6 +180,7 @@ uninterntype(const struct type *key) {
} else {
types.buckets[idx] = n->next;
}
+ typesvec.data[key->_id] = NULL;
free(n);
break;
}
@@ -189,6 +190,7 @@ uninterntype(const struct type *key) {
bool
completetype(const struct type *ty) {
+ if (ty->konst) return completetype(unconstify(ty));
if (ty->t == TYvoid)
return 0;
if (ty->t == TYfn)
@@ -241,10 +243,10 @@ putprimtypes(struct env *env) {
{"iptrint", &ty_iptrint, {TYint, t.ptrsize, IS}},
{"uptrint", &ty_uptrint, {TYint, t.ptrsize, IU}},
{"c_char", &ty_c_char, {TYint, 1, .int_signed = t.charsigned}},
- {"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}},
+ {"c_long", &ty_c_long, {TYint, t.longsize, t.longalign, IS}},
+ {"c_ulong", &ty_c_ulong, {TYint, t.longsize, t.longalign, IU}},
+ {"c_llong", &ty_c_llong, {TYint, t.llongsize, t.llongalign, IS}},
+ {"c_ullong", &ty_c_ullong, {TYint, t.llongsize, t.llongalign, IU}},
{"va_list", &ty_valist, {TYvalist, t.valistsize, t.valistalign}},
#undef IS
#undef IU
@@ -265,8 +267,10 @@ putprimtypes(struct env *env) {
void
visittypes(void (*visitor)(const struct type *, void *), void *arg) {
int i; const struct type *ty;
- vec_foreach(&typesvec, ty, i)
- visitor(ty, arg);
+ vec_foreach(&typesvec, ty, i) {
+ if (ty)
+ visitor(ty, arg);
+ }
}
const struct type *
diff --git a/bootstrap/util.c b/bootstrap/util.c
index da838b5..b64485b 100644
--- a/bootstrap/util.c
+++ b/bootstrap/util.c
@@ -112,7 +112,8 @@ eprifileline(struct span span) {
close(fd);
return;
}
- filemmaps[span.fileid] = src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ // XXX this maybe causes SIGBUS later if we're at last line + no ending newline + st_size multiple of 4096
+ filemmaps[span.fileid] = src = mmap(NULL, st.st_size + 1, PROT_READ, MAP_PRIVATE, fd, 0);
assert(src && src != (void *)-1 && "mmap");
close(fd);
}
diff --git a/examples/hello-world.cff b/examples/hello-world.cff
new file mode 100644
index 0000000..d127b96
--- /dev/null
+++ b/examples/hello-world.cff
@@ -0,0 +1,8 @@
+
+extern fn printf(fmt *const u8, ...) int;
+extern fn printf2(fmt *const u8, ...) int;
+
+extern fn main(argc int, argc *const *const u8) int {
+ "hello world\n";
+}
+
diff --git a/src/cffc.hff b/src/cffc.hff
index dcb91f7..6910179 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -41,8 +41,7 @@ enum TokT : i32 {
strify,
eof,
}
-def NUM_KEYWORDS = TokT:NUM_KEYWORDS;
-
+def NUM_KEYWORDS int = TokT:NUM_KEYWORDS;
struct Tok {
t TokT,
@@ -69,8 +68,13 @@ struct Type {
Int struct { sgn bool },
Flo,
Ptr *Type,
- Arr struct { child *Type, length i64 },
+ Arr struct { child *const Type, length i64 },
Slice *Type,
+ Fn struct {
+ params [#]*const Type,
+ variadic bool,
+ ret *const Type,
+ },
}
}
@@ -78,6 +82,7 @@ struct Parser {
fp *FILE,
alloc *Allocator,
curfile *const u8,
+ curenv *Env,
tokloc Loc,
curloc Loc,
eof bool,
@@ -85,8 +90,41 @@ struct Parser {
peektok Option<Tok>,
}
+struct Expr {
+ loc Loc,
+ ty *const Type,
+ u enum union {
+ IntLit union { i i64, u u64 },
+ FloLit f64,
+ StrLit [#]const u8,
+ BoolLit bool,
+ NullLit,
+ }
+}
+
+struct Stmt {
+ loc Loc,
+ u enum union {
+ Block [#]Stmt,
+ Expr Expr,
+ }
+}
+
+struct Fn {
+ ty *const Type,
+ paramnames [#]*const u8,
+ variadic bool,
+ id int,
+ body Option<[#]Stmt>
+}
+
struct Decl {
name *const u8,
+ loc Loc,
+ u enum union {
+ Fn Fn,
+ Ty *const Type,
+ },
}
struct DeclList {
@@ -98,6 +136,7 @@ struct Targ {
name *const u8,
ptrsize u8,
intsize u8,
+ i64align u8,
longsize u8, longalign u8,
llongsize u8, llongalign u8,
sizesize u8,
@@ -108,6 +147,7 @@ struct Targ {
}
// parse.cff
+extern static keyword2str [NUM_KEYWORDS]*const u8;
extern fn parser_init(*Parser, path *const u8) void;
extern fn parse(*Parser) [#]Decl;
@@ -116,16 +156,59 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, va_list) vo
extern fn pfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ...) void;
extern fn vefmt(fmt *const u8, ap va_list) void;
extern fn efmt(fmt *const u8, ...) void;
-
-// .. util.cff ..
+extern fn ssfmt(fmt *const u8, ...) *const u8;
extern fn fatal(*Parser, Loc, fmt *const u8, ...) void;
+// targ.cff
+extern static g_targ *const Targ;
+extern fn targ_ini(name *const u8) bool;
+
// type.cff
+extern static ty_void *const Type,
+ ty_bool *const Type,
+ ty_i8 *const Type,
+ ty_u8 *const Type,
+ ty_i16 *const Type,
+ ty_u16 *const Type,
+ ty_i32 *const Type,
+ ty_u32 *const Type,
+ ty_int *const Type,
+ ty_uint *const Type,
+ ty_i64 *const Type,
+ ty_u64 *const Type,
+ ty_isize *const Type,
+ ty_usize *const Type,
+ ty_iptrint *const Type,
+ ty_uptrint *const Type,
+ ty_f32 *const Type,
+ ty_f64 *const Type,
+ ty_voidptr *const Type;
+extern fn interntype(Type) *const Type;
+extern fn putprimtypes(env *Env) void;
+fn constify(ty *const Type) *const Type {
+ if ty.konst or ty.u.#tag == :Fn {
+ return ty;
+ }
+ let ty2 = *ty;
+ ty2.konst = #t;
+ return interntype(ty2);
+}
+fn mkarrtype(len i64, konst bool, child *const Type) *const Type {
+ return interntype({
+ len * child.size,
+ child.align,
+ .u: :Arr { konst ? constify(child) : child, len }
+ });
+}
+fn mkptrtype(child *const Type) *const Type {
+ return interntype({
+ g_targ.ptrsize,
+ .u: :Ptr child
+ });
+}
// env.cff
extern fn mkenv(parent *Env, alloc *Allocator) *Env;
-extern fn envput(*Env, *const Decl) *Decl;
-
-// targ.cff
-extern static g_targ *const Targ;
-extern fn targ_ini(name *const u8) bool;
+extern fn envput(*Env, Decl, **const Decl) *Decl;
+extern fn envfind(*Env, *const u8) *Decl;
+extern fn envfree(*Env) void;
diff --git a/src/common.hff b/src/common.hff
index aac733e..fe90e09 100644
--- a/src/common.hff
+++ b/src/common.hff
@@ -26,6 +26,19 @@ defmacro foreach(x, i, a, ...body) [
]
defmacro streq(a,b) [ (strcmp(a,b) == 0) ]
+defmacro strcieq(a,b) [ (strcasecmp(a,b) == 0) ]
+
+defmacro coalesce(a,b) [
+ (do let $x = a;
+ $x ? $x : b; )
+]
+
+defmacro with_tmpchange(var,x,...body) [
+ { let $tmp = (var);
+ (var) = x;
+ { body }
+ (var) = $tmp; }
+]
// Inline functions
fn bswap32(x u32) u32 {
diff --git a/src/env.cff b/src/env.cff
index d1eab07..0ae465a 100644
--- a/src/env.cff
+++ b/src/env.cff
@@ -1,6 +1,7 @@
import "cffc.hff";
import "map.hff";
import "util.hff";
+import "common.hff";
struct StringKeyTraits {
fn hash(s *const u8) u32 { return fnv1a_s(FNV1A_INI, s); }
@@ -19,11 +20,27 @@ extern fn mkenv(parent *Env, alloc *Allocator) *Env {
return env;
}
-extern fn envput(env *Env, decl *const Decl) *Decl {
+extern fn envput(env *Env, decl Decl, old **const Decl) *Decl {
let l **DeclList = env.decls->get_slot(decl.name);
- let n *DeclList = env.alloc->alloc(sizeof *DeclList);
+ let n *DeclList = anew(env.alloc, DeclList);
+ if *l { // decl with this name exists
+ *old = &(*l).decl;
+ return #null;
+ }
n.link = *l;
- n.decl = *decl;
+ n.decl = decl;
*l = n;
return &n.decl;
}
+
+extern fn envfind(env *Env, name *const u8) *Decl {
+ let l **DeclList = env.decls->get(name);
+ if l == #null { return #null; }
+ let l = *l;
+ assert(l != #null, "l?");
+ return &l.decl;
+}
+
+extern fn envfree(env *Env) void {
+ env.decls->clear();
+}
diff --git a/src/fmt.cff b/src/fmt.cff
index 90fcd47..5635103 100644
--- a/src/fmt.cff
+++ b/src/fmt.cff
@@ -1,5 +1,6 @@
import "cffc.hff";
import "common.hff";
+import "util.hff";
extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) void {
defmacro p(x) [ proc(x, parg) ]
@@ -67,6 +68,8 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
if quote { p('\''); }
case :type;
pfmt(proc, parg, "%t", tok.ty);
+ case :eof;
+ ps("<end of file>");
case else
if tok.t >= 0 and tok.t < NUM_KEYWORDS {
if quote { p('`'); }
@@ -86,6 +89,83 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
}
}
+ fn pritokt(proc typeof(proc), parg *void, t TokT) void {
+ switch t {
+ case :int;
+ ps("integer literal");
+ case :flo;
+ ps("float literal");
+ case :bool;
+ ps("boolean literal");
+ case :str;
+ ps("string literal");
+ case :chr;
+ ps("character literal");
+ case :null;
+ ps("#null");
+ case :ident, :macident;
+ ps("identifier");
+ case :label;
+ ps("label");
+ case :gensym;
+ ps("gensym");
+ case :type;
+ ps("type argument");
+ case else
+ if t >= 0 and t < NUM_KEYWORDS {
+ p('`');
+ ps(keyword2str[t]);
+ p('\'');
+ } else if t > 0 {
+ p('`');
+ let t = bswap32(t);
+ while t != 0 {
+ if t & 0xFF != 0 {
+ p(t);
+ }
+ t >>= 8;
+ }
+ p('\'');
+ }
+ }
+ }
+
+ fn pritype(proc typeof(proc), parg *void, ty *const Type) void {
+ if ty.konst {
+ ps("const ");
+ }
+ switch ty.u {
+ case Void; ps("void");
+ case Bool; ps("bool");
+ case Int i;
+ p(i.sgn ? 'i' : 'u');
+ sprintf(buf, "%zu", ty.size * 8);
+ ps(buf);
+ case Flo;
+ p('f');
+ sprintf(buf, "%zu", ty.size * 8);
+ ps(buf);
+ case Ptr child;
+ pfmt(proc, parg, "*%t", child);
+ case Arr arr;
+ pfmt(proc, parg, "[%I]%t", arr.length, arr.child);
+ case Slice child;
+ pfmt(proc, parg, "[#]%t", child);
+ case Fn f;
+ ps("fn (");
+ foreach(ty, i, f.params,
+ pritype(proc, parg, ty);
+ if f.variadic or i < f.params.#len - 1 {
+ ps(", ");
+ }
+ )
+ if f.variadic {
+ ps("...");
+ }
+ pfmt(proc, parg, ") %t", f.ret);
+ }
+ }
+
for let c u8 = *fmt; c != 0; c = *++fmt {
assert(c != 0, "?");
if c != '%' {
@@ -102,6 +182,12 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
case 'i', 'd';
sprintf(buf, "%d", ap->arg(int));
ps(buf);
+ case 'I', 'D';
+ sprintf(buf, "%lld", as(c_llong)ap->arg(i64));
+ ps(buf);
+ case 'z';
+ sprintf(buf, "%zu", ap->arg(usize));
+ ps(buf);
case 'p';
sprintf(buf, "%p", ap->arg(*void));
ps(buf);
@@ -147,6 +233,11 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
case 'T';
let tok = ap->arg(Tok);
pritok(proc, parg, quote, &tok);
+ case 'k';
+ let tokt = ap->arg(TokT);
+ pritokt(proc, parg, tokt);
+ case 't';
+ pritype(proc, parg, ap->arg(*const Type));
case else
assert(#f, "bad fmt '%c'", c);
}
@@ -175,3 +266,33 @@ extern fn efmt(fmt *const u8, ...) void {
vefmt(fmt, ap);
ap->end();
}
+
+extern fn ssfmt(fmt *const u8, ...) *const u8 {
+ static buf [1024]u8 = {};
+ let ap va_list #?;
+ ap->start(fmt);
+ fn sputc(c u8, arg *void) void {
+ let pidx *int = arg;
+ if *pidx < buf.#len - 1 {
+ buf[*pidx++] = c;
+ }
+ }
+ let idx = 0;
+ vpfmt(&sputc, &idx, fmt, ap);
+ ap->end();
+}
+
+extern fn vdiag(P *Parser, loc Loc, kind *const u8, fmt *const u8, ap va_list) void {
+ efmt("%s:%i:%i: %s: ", fileid2path(loc.fileid), loc.line, loc.col, kind);
+ vefmt(fmt, ap);
+ efmt("\n");
+}
+
+extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void {
+ let ap va_list #?;
+ ap->start(fmt);
+ vdiag(P, loc, "error", fmt, ap);
+ ap->end();
+ exit(1);
+}
+
diff --git a/src/libc.hff b/src/libc.hff
index 488a495..a269f42 100644
--- a/src/libc.hff
+++ b/src/libc.hff
@@ -25,6 +25,7 @@ extern fn free(p *void) void;
// string.h
extern fn strlen(s *const u8) usize;
extern fn strcmp(a *const u8, b *const u8) int;
+extern fn strcasecmp(a *const u8, b *const u8) int;
extern fn memcpy(*void, *const void, usize) *void;
extern fn strcpy(*u8, *const u8) *u8;
diff --git a/src/map.hff b/src/map.hff
index d7883dd..f97ea7e 100644
--- a/src/map.hff
+++ b/src/map.hff
@@ -29,7 +29,7 @@ struct Map<K, V, KTraits> {
}
i = (i + 1) & (self.N - 1);
} while i != h;
- assert(#f, "unreachable");
+ assert(#f, "unreachable1");
}
fn _reallockvb(self *Map) void {
@@ -53,16 +53,17 @@ struct Map<K, V, KTraits> {
b = (oldbmp[i/8] & (1 << (i%8))) != 0;
if (b) {
let h u32 = KTraits:hash(k);
- let i = h & (self.N - 1);
+ let j = h & (self.N - 1);
for ;; {
- if self->_iempty(i) {
- self.keys[i] = k;
- self.vals[i] = v;
- self.bitmap[i/8] |= 1 << (i%8);
- } else if KTraits:eq(self.keys[i], k) {
- assert(#f, "unreachable");
+ if self->_iempty(j) {
+ self.keys[j] = k;
+ self.vals[j] = v;
+ self.bitmap[j/8] |= 1 << (j%8);
+ break;
+ } else if KTraits:eq(self.keys[j], k) {
+ assert(#f, "unreachable2");
}
- i = (i + 1) & (self.N - 1);
+ j = (j + 1) & (self.N - 1);
}
}
@@ -87,6 +88,7 @@ struct Map<K, V, KTraits> {
if self->_iempty(i) {
self.bitmap[i/8] |= (1 << (i%8));
++self.count;
+ self.keys[i] = key;
self.vals[i] = {};
return &self.vals[i];
} else if KTraits:eq(self.keys[i], key) {
@@ -94,7 +96,7 @@ struct Map<K, V, KTraits> {
}
i = (i + 1) & (self.N - 1);
} while i != h;
- assert(#f, "unreachable");
+ assert(#f, "unreachable3");
}
@@ -102,4 +104,9 @@ struct Map<K, V, KTraits> {
*self->get_slot(key) = val;
}
+ fn clear(self *Map) void {
+ free(self.keys);
+ *self = {};
+ }
+
}
diff --git a/src/mem.hff b/src/mem.hff
index 805c3ce..7858f4d 100644
--- a/src/mem.hff
+++ b/src/mem.hff
@@ -1,7 +1,7 @@
import "libc.hff";
defmacro ALIGNUP(x,a) [ (((x) + ((a) - 1)) & -(a)) ]
-def ARENA_SIZE = 16 * 1024;
+def ARENA_SIZE = 4 * 1024;
extern fn xmalloc(n usize) *void;
extern fn xcalloc(n usize, m usize) *void;
extern fn xrealloc(p *void, n usize) *void;
@@ -68,3 +68,5 @@ struct Allocator {
}
}
}
+
+defmacro anew(a, T) [ ((a)->alloc(sizeof T)) ]
diff --git a/src/parse.cff b/src/parse.cff
index 25a2617..0f06259 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -139,7 +139,7 @@ fn eatspaces(P *Parser) void {
}
// !sorted
-static keyword2str []*const u8 = {
+extern static keyword2str [NUM_KEYWORDS]*const u8 = {
"and", "as", "break", "case", "const",
"continue", "def", "defmacro", "do",
"else", "enum", "extern", "fn",
@@ -214,14 +214,64 @@ fn readnumber(s *const u8) Option<Tok> {
}
let tok = Tok {};
- assert(suffix == #null,"sufx");
if flt {
tok.t = :flo;
tok.u.flo = accf;
+
+ switch {
+ case suffix == #null; tok.ty = ty_f64;
+ case strcieq(suffix, "f"); tok.ty = ty_f32;
+ case strcieq(suffix, "f32"); tok.ty = ty_f32;
+ case strcieq(suffix, "f64"); tok.ty = ty_f64;
+ case else; return :None;
+ }
+
return :Some tok;
} else {
tok.t = :int;
tok.u.uint = acc;
+
+ fn ty4intX(i u64) *const Type {
+ if i < 1u64 << ((g_targ.intsize * 8) - 1) {
+ return ty_int;
+ }
+ if i < 1u64 << 31 {
+ return ty_i32;
+ }
+ if i < 1u64 << 63 {
+ return ty_i64;
+ }
+ return ty_u64;
+ }
+
+ fn ty4uintX(i u64) *const Type {
+ if i < 1u64 << (g_targ.intsize * 8) {
+ return ty_uint;
+ }
+ if i < 1u64 << 32 {
+ return ty_u32;
+ }
+ return ty_u64;
+ }
+
+ switch {
+ case suffix == #null; tok.ty = ty4intX(tok.u.uint);
+ case strcieq(suffix, "u"); tok.ty = ty4uintX(tok.u.uint);
+ case strcieq(suffix, "u8"); tok.ty = ty_u8;
+ case strcieq(suffix, "i8"); tok.ty = ty_i8;
+ case strcieq(suffix, "u16"); tok.ty = ty_u16;
+ case strcieq(suffix, "i16"); tok.ty = ty_i16;
+ case strcieq(suffix, "u32"); tok.ty = ty_u32;
+ case strcieq(suffix, "i32"); tok.ty = ty_i32;
+ case strcieq(suffix, "u64"); tok.ty = ty_u64;
+ case strcieq(suffix, "i64"); tok.ty = ty_i64;
+ case strcieq(suffix, "z"); tok.ty = ty_usize;
+ case strcieq(suffix, "zs"); tok.ty = ty_isize;
+ case strcieq(suffix, "p"); tok.ty = ty_uptrint;
+ case strcieq(suffix, "ps"); tok.ty = ty_iptrint;
+ case else; return :None;
+ }
+
return :Some tok;
}
}
@@ -445,23 +495,280 @@ fn lex(P *Parser) Tok {
fatal(P, tok.loc, "stray %qc in program", c);
}
-extern fn parse(P *Parser) [#]Decl {
- fn mallocator_allocf(*void, n usize) *void {
- return xmalloc(n);
+fn lexpeek(P *Parser) Tok {
+ switch P.peektok {
+ case Some t; return t;
}
+ let tok = lex(P);
+ P.peektok = :Some tok;
+ return tok;
+}
- fn mallocator_freef(*void, ptr *void) void {
- free(ptr);
+fn lexmatch(P *Parser, tokp *Tok, t TokT) bool {
+ let tok = lexpeek(P);
+ if tokp {
+ *tokp = tok;
}
+ if tok.t == t {
+ lex(P);
+ return #t;
+ }
+ return #f;
+}
+
+fn lexexpects(P *Parser, t TokT, what *const u8) Tok {
+ let tok = Tok {};
+ if not lexmatch(P, &tok, t) {
+ if what {
+ fatal(P, tok.loc, "expected %s (near %qT)", what, tok);
+ } else {
+ fatal(P, tok.loc, "expected %qk (near %qT)", t, tok);
+ }
+ }
+ return tok;
+}
+defmacro lexexpect(P, t) [lexexpects(P, t, #null)]
+////////////
+// Parser //
+////////////
+
+static primenv *Env = {};
+
+fn finddecl(P *Parser, name *const u8) *Decl {
+ if primenv == #null {
+ primenv = mkenv(#null, P.alloc);
+ putprimtypes(primenv);
+ }
+ let p = envfind(primenv, name);
+ return p ? p : envfind(P.curenv, name);
+}
+
+fn putdecl(P *Parser, decl Decl) *Decl {
+ assert(primenv != #null, "primenv");
+ if envfind(primenv, decl.name) {
+ fatal(P, decl.loc, "cannot shadow primitive `%s'", decl.name);
+ }
+ let d0 *const Decl;
+ let d = envput(P.curenv, decl, &d0);
+ if d == #null {
+ fatal(P, decl.loc, "attempt to redefine `%s'", decl.name);
+ }
+ return d;
+}
+
+///////////////////
+// Types Parsing //
+///////////////////
+
+fn parsetype(P *Parser) *const Type {
+ let tok Tok = {};
+ switch {
+ case lexmatch(P, &tok, :kw_const);
+ return constify(parsetype(P));
+ case lexmatch(P, &tok, '*');
+ return mkptrtype(parsetype(P));
+ case lexmatch(P, &tok, :ident);
+ let decl = finddecl(P, tok.u.ident);
+ if decl == #null {
+ fatal(P, tok.loc, "%qT is not defined", tok);
+ }
+ if decl.u.#tag != :Ty {
+ fatal(P, tok.loc, "%qT is not a type", tok);
+ }
+ return decl.u.Ty;
+
+ case else;
+ fatal(P, tok.loc, "expected type (near %qT)", tok);
+ }
+}
+
+/////////////////////////
+// Expressions Parsing //
+/////////////////////////
+
+fn pexprimary(P *Parser) Expr {
+ let tok Tok = lex(P);
+ switch tok.t {
+ case :int, :chr;
+ return { tok.loc, tok.ty, .u: :IntLit { tok.u.int }};
+ case :flo;
+ return { tok.loc, tok.ty, .u: :FloLit tok.u.flo };
+ case :bool;
+ return { tok.loc, ty_bool, .u: :BoolLit tok.u.bool };
+ case :null;
+ return { tok.loc, ty_voidptr, .u: :NullLit };
+ case :str;
+ return { tok.loc, .u: :StrLit tok.u.str };
+ case else;
+ fatal(P, tok.loc, "expected expression (near %qT)", tok);
+ }
+}
+
+fn pexpostfix(P *Parser) Expr {
+ let ex = pexprimary(P);
+ return ex;
+}
+
+fn pexprefix(P *Parser) Expr {
+ return pexpostfix(P);
+}
+
+fn pexbitarith(P *Parser) Expr {
+ let ex = pexprefix(P);
+ return ex;
+}
+
+fn pexcmp(P *Parser) Expr {
+ let ex = pexbitarith(P);
+ return ex;
+}
+
+fn pexlog(P *Parser) Expr {
+ let ex = pexcmp(P);
+ return ex;
+}
+
+fn pexcond(P *Parser) Expr {
+ let ex = pexlog(P);
+ return ex;
+}
+
+fn pexassign(P *Parser) Expr {
+ let ex = pexcond(P);
+ return ex;
+}
+
+fn parseexpr(P *Parser) Expr {
+ return pexassign(P);
+}
+
+////////////////////////
+// Statements Parsing //
+////////////////////////
+
+typedef DeclYielder *fn(*Decl, *void) void;
+typedef StmtYielder *fn(Stmt, *void) void;
+
+fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void {
+ let tok Tok = {};
+ switch {
+ case lexmatch(P, &tok, :kw_if);
+ case else;
+ return yield({ tok.loc, .u: :Expr parseexpr(P) }, yarg);
+ }
+}
+
+fn parseblock0(P *Parser) [#]Stmt {
+ let sts Vec<Stmt> = {};
+ let tok Tok = {};
+ while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != '}' {
+ if lexmatch(P, #null, ';') { continue; }
+ fn pushstmt(st Stmt, arg *void) void {
+ let sts *Vec<Stmt> = arg;
+ sts->push(st);
+ }
+ parsestmts(P, &pushstmt, &sts);
+ }
+ return sts->move(P.alloc);
+}
+
+fn parseblock(P *Parser) [#]Stmt {
+ let b = parseblock0(P);
+ lexexpect(P, '}');
+ return b;
+}
+
+///////////////////
+// Decls Parsing //
+///////////////////
+
+fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void {
+ decl.name = name;
+ decl.u = :Fn {};
+
+ let Fn = &decl.u.Fn,
+ tok Tok = {},
+ paramnames Vec<*const u8> = {},
+ paramtys Vec<*const Type> = {};
+
+ lexexpect(P, '(');
+ while not lexmatch(P, &tok, ')') {
+ if lexmatch(P, #null, '...') {
+ Fn.variadic = #t;
+ lexmatch(P, #null, ',');
+ lexexpect(P, ')');
+ break;
+ }
+ let name = lexexpect(P, :ident).u.ident;
+ let type = parsetype(P);
+ paramnames->push(name);
+ paramtys->push(type);
+ if not lexmatch(P, &tok, ',') {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ Fn.paramnames = paramnames->move(P.alloc);
+ let retty = parsetype(P);
+ Fn.ty = interntype({ .size: 0, .align: 1, .u: :Fn {
+ paramtys->move(P.alloc), Fn.variadic, retty
+ }});
+ static id int = 0;
+ Fn.id = ++id;
+
+ if not lexmatch(P, #null, '{') {
+ lexexpects(P, ';', "';' or '{'");
+ return;
+ }
+
+ with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf },
+ Fn.body = :Some parseblock(P);
+ (as(*Arena)P.alloc.a)->destroy();
+ );
+}
+
+fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void {
+ let tok = Tok { .loc: P.tokloc },
+ decl = Decl {},
+ externp = #f,
+ name *const u8 = #null;
+
+ if lexmatch(P, &tok, :kw_extern) {
+ externp = #t;
+ }
+
+ switch {
+ case lexmatch(P, &tok, :kw_fn);
+ let name = lexmatch(P, &tok, :ident) ? tok.u.ident : #null;
+ parsefn(P, &decl, externp, name);
+ decl.loc = tok.loc;
+ case else;
+ fatal(P, tok.loc, "expected declaration (near %qT)", tok);
+ }
+
+ yield(putdecl(P, decl), yarg);
+}
+
+extern fn parse(P *Parser) [#]Decl {
let aralloc = Arena {};
let alloc = Allocator { &aralloc, &Arena:allocf, #null };
+ let decls Vec<*Decl> = {};
P.alloc = &alloc;
+ P.curenv = mkenv(#null, P.alloc);
+
while not P.eof {
- let tok = lex(P);
- if tok.t == :eof { break; }
- efmt("* tok: %qT\n", tok);
+ fn yield(decl *Decl, arg *void) void {
+ let decls *Vec<*Decl> = arg;
+ decls->push(decl);
+ }
+ parsedecls(P, &yield, &decls);
+ if lexmatch(P, #null, :eof) {
+ break;
+ }
}
+
+ envfree(P.curenv);
aralloc->destroy();
}
diff --git a/src/targ.cff b/src/targ.cff
index 59078a0..c74e1d3 100644
--- a/src/targ.cff
+++ b/src/targ.cff
@@ -6,6 +6,7 @@ static const targs []const Targ = {
"amd64-sysv",
.ptrsize: 8,
.intsize: 4,
+ .i64align: 8,
.longsize: 8, .longalign: 8,
.llongsize: 8, .llongalign: 8,
.sizesize: 8,
diff --git a/src/type.cff b/src/type.cff
index a3c7021..33d78e6 100644
--- a/src/type.cff
+++ b/src/type.cff
@@ -1,32 +1,138 @@
import "cffc.hff";
import "util.hff";
+import "common.hff";
+import "set.hff";
-fn hashtype(ty *const Type) u32 {
- let h = FNV1A_INI;
- h = fnv1a_i(h, ty.konst ? 1 : 0);
+struct TypeTraits {
+ fn hash(ty *const Type) u32 {
+ let h = FNV1A_INI;
+ h = fnv1a_i(h, as(int)ty.u.#tag);
+ h = fnv1a_i(h, ty.konst ? 1 : 0);
- switch ty.u {
- case Void; case Bool;
+ switch ty.u {
+ case Void; case Bool;
- case Flo;
- h = fnv1a_i(h, ty.size);
- h = fnv1a_i(h, ty.align);
+ case Flo;
+ h = fnv1a_i(h, ty.size);
- case Int i;
- h = fnv1a_i(h, i.sgn ? 1 : 0);
- h = fnv1a_i(h, ty.size);
- h = fnv1a_i(h, ty.align);
+ case Int i;
+ h = fnv1a_i(h, i.sgn ? 1 : 0);
+ h = fnv1a_i(h, ty.size);
- case Ptr child;
- h = fnv1a_i(h, child.id);
+ case Ptr child;
+ h = fnv1a_i(h, child.id);
- case Arr arr;
- h = fnv1a_i(h, arr.child.id);
- h = fnv1a_i(h, arr.length);
+ case Arr arr;
+ h = fnv1a_i(h, arr.child.id);
+ h = fnv1a_i(h, arr.length);
- case Slice child;
- h = fnv1a_i(h, child.id);
+ case Slice child;
+ h = fnv1a_i(h, child.id);
+ }
+ return h;
}
- return h;
+
+ fn eq(a *const Type, b *const Type) bool {
+ if a.u.#tag != b.u.#tag or a.size != b.size
+ or a.align != b.align or a.konst != b.konst {
+ return #f;
+ }
+ switch a.u {
+ case Void; case Bool; case Flo;
+ case Int i;
+ return i.sgn == b.u.Int.sgn;
+ case Ptr child;
+ return child == b.u.Ptr;
+ case Arr arr;
+ let brr = b.u.Arr;
+ return arr.length == brr.length and arr.child == brr.child;
+ case Slice child;
+ return child == b.u.Slice;
+ case Fn f0;
+ let f1 = b.u.Fn;
+ if f0.variadic != f1.variadic {
+ return #f;
+ }
+ if f0.ret != f1.ret {
+ return #f;
+ }
+ if f0.params.#len != f1.params.#len {
+ return #f;
+ }
+ foreach(p, i, f0.params,
+ if p != f1.params[i] {
+ return #f;
+ }
+ )
+ return #t;
+ }
+ return #f;
+ }
+
+ fn dup(ty *const Type) *const Type {
+ let p *Type = xmalloc(sizeof Type);
+ static id int = 0;
+ *p = *ty;
+ p.id = id++;
+ return p;
+ }
+}
+
+extern fn interntype(ty0 Type) *const Type {
+ static set Set<*const Type, TypeTraits> = {};
+
+ if ty0.align == 0 {
+ ty0.align = ty0.size;
+ }
+
+ return *set->intern(&ty0);
+}
+
+extern static ty_void *const Type = {},
+ ty_bool *const Type = {},
+ ty_i8 *const Type = {},
+ ty_u8 *const Type = {},
+ ty_i16 *const Type = {},
+ ty_u16 *const Type = {},
+ ty_i32 *const Type = {},
+ ty_u32 *const Type = {},
+ ty_int *const Type = {},
+ ty_uint *const Type = {},
+ ty_i64 *const Type = {},
+ ty_u64 *const Type = {},
+ ty_isize *const Type = {},
+ ty_usize *const Type = {},
+ ty_iptrint *const Type = {},
+ ty_uptrint *const Type = {},
+ ty_f32 *const Type = {},
+ ty_f64 *const Type = {},
+ ty_voidptr *const Type = {};
+
+extern fn putprimtypes(env *Env) void {
+ let types []struct { name *const u8, gty **const Type, ty Type } = {
+ { "void", &ty_void, { .size: 0, .align: 1, .u: :Void }},
+ { "bool", &ty_bool, { .size: 1, .align: 1, .u: :Bool }},
+ { "f32", &ty_f32, { .size: 4, .u: :Flo }},
+ { "f64", &ty_f64, { .size: 8, g_targ.f64align, .u: :Flo }},
+ { "i8", &ty_i8, { .size: 1, .u: :Int { .sgn: #t }}},
+ { "u8", &ty_u8, { .size: 1, .u: :Int { .sgn: #f }}},
+ { "i16", &ty_i16, { .size: 2, .u: :Int { .sgn: #t }}},
+ { "u16", &ty_u16, { .size: 2, .u: :Int { .sgn: #f }}},
+ { "i32", &ty_i32, { .size: 4, .u: :Int { .sgn: #t }}},
+ { "u32", &ty_u32, { .size: 4, .u: :Int { .sgn: #f }}},
+ { "int", &ty_int, { g_targ.intsize, .u: :Int { .sgn: #t }}},
+ { "uint", &ty_uint, { g_targ.intsize, .u: :Int { .sgn: #f }}},
+ { "isize", &ty_isize,{ g_targ.sizesize, .u: :Int { .sgn: #t }}},
+ { "usize", &ty_usize,{ g_targ.sizesize, .u: :Int { .sgn: #f }}},
+ { "i64", &ty_i64, { .size: 8, g_targ.i64align, .u: :Int { .sgn: #t }}},
+ { "u64", &ty_u64, { .size: 8, g_targ.i64align, .u: :Int { .sgn: #f }}},
+ { "iptrint",&ty_iptrint, { g_targ.ptrsize, .u: :Int { .sgn: #t }}},
+ { "uptrint",&ty_uptrint, { g_targ.ptrsize, .u: :Int { .sgn: #f }}},
+ };
+
+ foreach(type, _, types[0::],
+ envput(env, { type.name, .u: :Ty (*type.gty = interntype(type.ty)) }, #null);
+ )
+ ty_voidptr = interntype({ .size: g_targ.ptrsize, .u: :Ptr ty_void });
}
diff --git a/src/util.cff b/src/util.cff
index 1b81ab4..d3e40bb 100644
--- a/src/util.cff
+++ b/src/util.cff
@@ -67,24 +67,13 @@ extern fn addfilepath(s *const u8) int {
return i;
}
-fn fileid2path(id int) *const u8 {
+extern fn fileid2path(id int) *const u8 {
assert(id >= 0 and id < filepaths.#len, "fileid");
let s = filepaths[id];
assert(s != #null, "fileid");
return s;
}
-extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void {
- let ap va_list #?;
- ap->start(fmt);
-
- efmt("%s:%i:%i: error: ", fileid2path(loc.fileid), loc.line, loc.col);
- vefmt(fmt, ap);
- efmt("\n");
- ap->end();
- exit(1);
-}
-
extern fn internstr(s *const u8) *const u8 {
static set Set<*const u8, struct {
fn hash(s *const u8) u32 { return fnv1a_s(FNV1A_INI, s); }
diff --git a/src/util.hff b/src/util.hff
index 5eb9c88..19c8c41 100644
--- a/src/util.hff
+++ b/src/util.hff
@@ -8,5 +8,5 @@ extern fn fnv1a(h u32, [#]const u8) u32;
extern fn fnv1a_s(h u32, *const u8) u32;
extern fn fnv1a_i(h u32, i64) u32;
extern fn addfilepath(*const u8) int;
-// extern fn fatal(*Parser, Loc, fmt *const u8, ...) void;
+extern fn fileid2path(id int) *const u8;
extern fn internstr(*const u8) *const u8;