aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-15 12:22:47 +0200
committerlemon <lsof@mailbox.org>2022-08-15 12:22:47 +0200
commitf906d0b350b0b4ceb747669c9a9845d11bd0e486 (patch)
tree5f09a7b714e6ce93f6094a06e5f736513110fb8d /src
parentf802bb99263aaa5be492999babd44cd2fdb1c65f (diff)
self hosted progress
Diffstat (limited to 'src')
-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
12 files changed, 714 insertions, 67 deletions
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;