aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/hello-world.cff6
-rw-r--r--misc/cff.vim2
-rw-r--r--src/cffc.hff24
-rw-r--r--src/fmt.cff7
-rw-r--r--src/fold.cff66
-rw-r--r--src/parse.cff162
-rw-r--r--src/type.cff11
-rw-r--r--src/vec.hff4
8 files changed, 268 insertions, 14 deletions
diff --git a/examples/hello-world.cff b/examples/hello-world.cff
index 29a1c03..3eb198c 100644
--- a/examples/hello-world.cff
+++ b/examples/hello-world.cff
@@ -1,10 +1,6 @@
-extern fn printf(fmt *const u8, ...) int;
-extern fn cos(x f32) f32;
+import "libc.hff";
extern fn main(argc int, argv *const *const u8) int {
- let x = argv,
- z = 3;
- let foo = x + z;
printf("hello world\n");
}
diff --git a/misc/cff.vim b/misc/cff.vim
index 00f586a..0379e90 100644
--- a/misc/cff.vim
+++ b/misc/cff.vim
@@ -14,7 +14,7 @@ syn keyword Macro defmacro
syn keyword Type i8 u8 i16 u16 i32 u32 i64 u64
syn keyword Type int uint isize usize iptrint uptrint
syn keyword Type c_char c_long c_ulong c_llong c_ullong
-syn keyword Type bool void f32 f64 typeof
+syn keyword Type bool intbool void f32 f64 typeof
syn keyword Structure struct enum union
syn keyword Operator sizeof as and or
diff --git a/src/cffc.hff b/src/cffc.hff
index 2252373..aad2ee4 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -68,6 +68,7 @@ struct AggField {
ty *const Type,
off usize,
}
+struct EnumVal { name *const u8, i i64 }
struct Type {
size usize,
@@ -87,13 +88,20 @@ struct Type {
variadic bool,
ret *const Type,
},
+ Enum struct {
+ intty *const Type,
+ name *const u8,
+ lax bool,
+ id int,
+ vals [#]EnumVal,
+ },
Agg struct {
kind AggKind,
name *const u8,
id int,
fwd bool,
flds [#]AggField,
- decls [#]*Decl,
+ decls *Env,
},
},
@@ -190,6 +198,16 @@ struct Var {
fnid int,
}
+struct MacroCase {
+ variadic bool,
+ params [#]*const u8,
+ body [#]Tok,
+}
+
+struct Macro {
+ cs [#]MacroCase
+}
+
struct Decl {
name *const u8,
loc Loc,
@@ -199,6 +217,7 @@ struct Decl {
Static Var,
Def Expr,
Fn Fn,
+ Macro Macro,
Ty *const Type,
},
}
@@ -317,3 +336,6 @@ extern fn envput(*Env, Decl, **const Decl) *Decl;
extern fn envfind(*Env, *const u8) *Decl;
extern fn envfind_noparent(*Env, *const u8) *Decl;
extern fn envfree(*Env) void;
+
+// fold.cff
+extern fn fold(*Expr) bool;
diff --git a/src/fmt.cff b/src/fmt.cff
index 00b9b54..755bbda 100644
--- a/src/fmt.cff
+++ b/src/fmt.cff
@@ -138,7 +138,7 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
}
switch ty.u {
case Void; ps("void");
- case Bool; ps("bool");
+ case Bool; ps(ty.size == 1 ? "bool" : "intbool");
case Int i;
p(i.sgn ? 'i' : 'u');
sprintf(buf, "%zu", ty.size * 8);
@@ -165,6 +165,11 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
ps("...");
}
pfmt(proc, parg, ") %t", f.ret);
+ case Agg agg;
+ if agg.name {
+ ps(agg.name);
+ } else {
+ }
}
}
diff --git a/src/fold.cff b/src/fold.cff
new file mode 100644
index 0000000..7b393e5
--- /dev/null
+++ b/src/fold.cff
@@ -0,0 +1,66 @@
+import "cffc.hff";
+import "common.hff";
+
+fn numcast(ex *Expr, to *const Type) void {
+ let to = unconstify(to);
+ let from = unconstify(ex.ty);
+ let iu = &ex.u.IntLit;
+ let f = &ex.u.FloLit;
+ let b = &ex.u.BoolLit;
+ switch {
+ case to == from;
+ // pass
+ case to->is(:Int) and from->is(:Flo);
+ iu.i = from.size == 4 ? as(f32)*f : *f;
+ case to->is(:Flo) and to.size == 4 and from->is(:Flo);
+ *f = as(f32)*f;
+ case to->is(:Flo) and to.size == 8 and from->is(:Flo);
+ *f = *f;
+ case from->is(:Bool);
+ iu.i = *b ? 1 : 0;
+ case from->is(:Int) and to == ty_u8;
+ iu.i = as(i8)iu.i;
+ case from->is(:Int) and to == ty_u8;
+ iu.i = as(u8)iu.i;
+ case from->is(:Int) and to == ty_i16;
+ iu.i = as(i16)iu.i;
+ case from->is(:Int) and to == ty_u16;
+ iu.i = as(u8)iu.i;
+ case from->is(:Int) and to == ty_i32;
+ iu.i = as(i32)iu.i;
+ case from->is(:Int) and to == ty_i64;
+ iu.i = as(i64)iu.i;
+ case from->is(:Int) and to == ty_u64;
+ iu.u = iu.u;
+ case to == ty_f64 and from == ty_u64;
+ *f = iu.u;
+ case to == ty_f32 and from == ty_u64;
+ *f = as(f32)iu.u;
+ case to == ty_f64;
+ *f = iu.i;
+ case to == ty_f32;
+ *f = as(f32)iu.i;
+ case to->is(:Bool) and from->is(:Int);
+ *b = iu.u == 0;
+ case else;
+ assert(#f, "bad numcast");
+ }
+
+ ex.ty = to;
+ ex.u.#tag = to->is(:Flo) ? :FloLit
+ : to->is(:Int) ? :IntLit
+ : :BoolLit;
+}
+
+extern fn fold(ex *Expr) bool {
+ switch ex.u.#tag {
+ case :IntLit, :FloLit, :BoolLit;
+ numcast(ex, ex.ty);
+ return #t;
+ case :StrLit, :NullLit;
+ return #t;
+ case else;
+ return #f;
+ }
+
+}
diff --git a/src/parse.cff b/src/parse.cff
index c2c1817..f19b586 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -566,8 +566,37 @@ fn putdecl(P *Parser, eloc Loc, decl Decl) *Decl {
// Types Parsing //
///////////////////
+fn parseexpr(P *Parser) Expr;
fn parsetype(P *Parser) *const Type;
+fn parseenum(P *Parser, name *const u8, lax bool) *const Type {
+ lexexpect(P, '{');
+ let iota = 0i64;
+ let max = iota;
+ let vals Vec<EnumVal> = {};
+ while !lexmatch(P, #null, '}') {
+ let name = lexexpects(P, :ident, "variant name").u.ident;
+ if lexmatch(P, #null, '=') {
+ let ex = parseexpr(P);
+ if !fold(&ex) or !ex.ty->is(:Int) {
+ err(P, ex.loc, "expected constant integer expression");
+ }
+ iota = ex.u.IntLit.i;
+ }
+ vals->push({
+ name,
+ iota++,
+ });
+ if !lexmatch(P, #null, ',') {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+ let intty *const Type = #null;
+ static id int = 0;
+ return interntype({ .u: :Enum { intty, name, lax, id++, vals->move(P.alloc) }});
+}
+
fn parseagg(P *Parser, kind AggKind, name *const u8, retdecl **Decl) *const Type {
let loc = P.tokloc;
let tok Tok #?;
@@ -598,7 +627,7 @@ fn parseagg(P *Parser, kind AggKind, name *const u8, retdecl **Decl) *const Type
let flds Vec<AggField> = {};
while !lexmatch(P, #null, '}') {
let off = size;
- let name = lexexpect(P, :ident).u.ident;
+ let name = lexexpects(P, :ident, "field name").u.ident;
let type = parsetype(P);
if type {
off = ALIGNUP(off, type.align);
@@ -730,8 +759,6 @@ fn islvalue(ex Expr) bool {
return #f;
}
-fn parseexpr(P *Parser) Expr;
-
fn pexprimary(P *Parser) Expr {
let tok Tok = lex(P);
switch tok.t {
@@ -764,14 +791,23 @@ fn pexprimary(P *Parser) Expr {
return { tok.loc, var.ty, .u: :Symbol(decl) };
case Static var;
return { tok.loc, var.ty, .u: :Symbol(decl) };
+ case Def ex;
+ ex.loc = tok.loc;
+ return ex;
}
case :kw_sizeof;
+ let ty *const Type #?;
if lexmatch(P, &tok, '(') {
let ex = parseexpr(P);
lexexpect(P, ')');
- return { ex.loc, ty_usize, :IntLit { ex.ty.size }};
+ ty = ex.ty;
+ } else {
+ ty = parsetype(P);
}
- return { tok.loc, ty_usize, :IntLit { parsetype(P).size }};
+ if !completetype(ty) {
+ err(P, tok.loc, "sizeof incomplete type (%t)", ty);
+ }
+ return { tok.loc, ty_usize, :IntLit { ty.size }};
case '(';
let ex = parseexpr(P);
@@ -1448,6 +1484,106 @@ fn doimport(P *Parser, path *const u8) [#]Decl {
return decls;
}
+fn parsemacro(P *Parser, name *const u8) *Decl {
+ fn parsemacrocase(P *Parser) MacroCase {
+ let tok Tok #?;
+ let c MacroCase = {};
+ let params Vec<*const u8> = {};
+ let body Vec<Tok> = {};
+ static gensymid int = 0;
+ struct Gensyms {
+ next *Gensyms,
+ name *const u8,
+ tok Tok,
+ };
+ let gensyms *Gensyms = #null;
+ lexexpect(P, '(');
+ while !lexmatch(P, &tok, ')') {
+ if lexmatch(P, &tok, '...') {
+ params->push(lexexpects(P, :ident, "parameter name").u.ident);
+ c.variadic = #t;
+ lexmatch(P, &tok, ',');
+ lexexpect(P, ')');
+ break;
+ } else {
+ params->push(lexexpects(P, :ident, "parameter name").u.ident);
+ if !lexmatch(P, &tok, ',') {
+ lexexpect(P, ')');
+ break;
+ }
+ }
+ }
+ let eloc = tok.loc;
+ lexexpect(P, '[');
+
+ let bal = 1;
+ while bal != 0 {
+ tok = lex(P);
+ switch tok.t {
+ case '['; ++bal;
+ case ']'; --bal;
+ case :eof;
+ fatal(P, eloc, "unterminated macro definition");
+ }
+ if tok.t == :ident {
+ vec_each(par, _, params,
+ if streq(par, tok.u.ident) {
+ tok.t = :macident;
+ }
+ )
+ } else if tok.t == :gensym {
+ let gs *Gensyms #?;
+ let found = #f;
+ for gs = gensyms; gs; gs = gs.next {
+ if streq(gs.name, tok.u.ident) {
+ tok = gs.tok;
+ found = #t;
+ break;
+ }
+ }
+ if !found {
+ gs = xcalloc(1, sizeof(*gs));
+ gs.name = tok.u.ident;
+ gs.tok.t = :ident;
+ gs.tok.loc = tok.loc;
+ let s [300]u8 = {};
+ snprintf(s, sizeof(s) - 1, "#<gensym %s %d>", tok.u.ident, gensymid++);
+ gs.tok.u.ident = internstr(s);
+ tok = gs.tok;
+ gs.next = gensyms;
+ gensyms = gs;
+ }
+ }
+ body->push(tok);
+ }
+ body->pop();
+
+ for let gs = gensyms, next *Gensyms #?; gs; gs = next {
+ next = gs.next;
+ free(gs);
+ }
+
+ c.params = params->move(P.alloc);
+ c.body = body->move(P.alloc);
+ return c;
+ }
+
+ let tok Tok #?;
+ let cs Vec<MacroCase> = {};
+ if lexpeek(P).t == '(' {
+ cs->push(parsemacrocase(P));
+ } else {
+ lexexpect(P, '{');
+ while !lexmatch(P, &tok, '}') {
+ cs->push(parsemacrocase(P));
+ if !lexmatch(P, &tok, ',') {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+ }
+}
+
fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
let tok = Tok { .loc: P.tokloc },
decl *Decl = {},
@@ -1478,6 +1614,17 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
lexexpect(P, ';');
return;
+ case lexmatch(P, &tok, :kw_enum);
+ if lexmatch(P, &tok, :kw_union) {
+ let name = lexexpect(P, :ident).u.ident;
+ parseagg(P, :EUnion, name, &decl);
+ } else {
+ let name = lexexpect(P, :ident).u.ident;
+ let loc = P.tokloc;
+ let ty = parseenum(P, name, #{lax} #f);
+ decl = putdecl(P, loc, { name, loc, .u: :Ty(ty) });
+ }
+
case lexmatch(P, &tok, :kw_struct);
let name = lexexpect(P, :ident).u.ident;
parseagg(P, :Struct, name, &decl);
@@ -1531,6 +1678,7 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
if !typematchestarg(ty, ini.ty) {
err(P, ini.loc, "incompatible initializer (%t, expected %t)", ini.ty, ty);
}
+ fold(&ini);
yield(putdecl(P, tok.loc, {
name, tok.loc, .u: :Def(ini)
}), yarg);
@@ -1540,6 +1688,10 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
}
} while !lexmatch(P, &tok, ';');
return;
+
+ case lexmatch(P, &tok, :kw_defmacro);
+ let name = lexexpects(P, :ident, "macro name").u.ident;
+ decl = parsemacro(P, name);
case else;
fatal(P, tok.loc, "expected declaration (near %qT)", tok);
diff --git a/src/type.cff b/src/type.cff
index e3989c7..980a83a 100644
--- a/src/type.cff
+++ b/src/type.cff
@@ -10,7 +10,10 @@ struct TypeTraits {
h = fnv1a_i(h, ty.konst ? 1 : 0);
switch ty.u {
- case Void; case Bool;
+ case Void;
+
+ case Bool;
+ h = fnv1a_i(h, ty.size);
case Flo;
h = fnv1a_i(h, ty.size);
@@ -121,12 +124,14 @@ extern fn completetype(ty *const Type) bool {
case Void; return #f;
case Fn; return #f;
case Arr a; return a.length >= 0;
+ case Agg agg; return !agg.fwd;
}
return #t;
}
extern static ty_void *const Type = {},
ty_bool *const Type = {},
+ ty_intbool *const Type = {},
ty_i8 *const Type = {},
ty_u8 *const Type = {},
ty_i16 *const Type = {},
@@ -159,6 +164,7 @@ extern fn putprimtypes(env *Env) void {
{ "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 }}},
+ { "intbool",&ty_intbool,{ .size: g_targ.intsize, .u: :Bool }},
{ "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 }}},
@@ -242,6 +248,9 @@ extern fn typeof2(a *const Type, b *const Type) *const Type {
if a == unconstify(b) {
return b;
}
+ if a->is(:Bool) and b->is(:Bool) {
+ return a.size > b.size ? a : b;
+ }
if a->is(:Arr) and b->is(:Arr) {
a = arraydecay(a);
}
diff --git a/src/vec.hff b/src/vec.hff
index 9933038..003b13a 100644
--- a/src/vec.hff
+++ b/src/vec.hff
@@ -19,6 +19,10 @@ struct Vec<T> {
vec.dat[vec.len++] = x;
}
+ fn pop(vec *Vec) T {
+ return vec.dat[--vec.len];
+ }
+
fn clear(vec *Vec) void {
free(vec.dat);
*vec = {};