aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cffc.hff22
-rw-r--r--src/env.cff5
-rw-r--r--src/parse.cff172
-rw-r--r--src/type.cff18
4 files changed, 184 insertions, 33 deletions
diff --git a/src/cffc.hff b/src/cffc.hff
index 6147377..32683eb 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -57,6 +57,18 @@ struct Tok {
},
}
+enum AggKind {
+ Struct,
+ Union,
+ EUnion,
+}
+
+struct AggField {
+ name *const u8,
+ ty *const Type,
+ off usize,
+}
+
struct Type {
size usize,
align usize,
@@ -75,6 +87,14 @@ struct Type {
variadic bool,
ret *const Type,
},
+ Agg struct {
+ kind AggKind,
+ name *const u8,
+ id int,
+ fwd bool,
+ flds [#]AggField,
+ decls [#]*Decl,
+ },
},
fn is(ty *const Type, tag typeof((Type{}).u.#tag)) bool {
@@ -172,6 +192,7 @@ struct Decl {
u enum union {
Let Var,
Static Var,
+ Def Expr,
Fn Fn,
Ty *const Type,
},
@@ -289,4 +310,5 @@ extern fn mkenv(parent *Env, alloc *Allocator) *Env;
extern fn envparent(*Env) *Env;
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;
diff --git a/src/env.cff b/src/env.cff
index 001e92c..f18fe49 100644
--- a/src/env.cff
+++ b/src/env.cff
@@ -50,6 +50,11 @@ extern fn envfind(env *Env, name *const u8) *Decl {
return &l.decl;
}
+extern fn envfind_noparent(env *Env, name *const u8) *Decl {
+ let l **DeclList = env.decls->get(name);
+ return l ? &(*l).decl : #null;
+}
+
extern fn envfree(env *Env) void {
env.decls->clear();
free(env);
diff --git a/src/parse.cff b/src/parse.cff
index 098d916..13c89f5 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -541,26 +541,23 @@ defmacro lexexpect(P, t) [lexexpects(P, t, #null)]
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 ?? envfind(P.curenv, name);
}
-fn putdecl(P *Parser, decl Decl) *Decl {
- if primenv == #null {
- primenv = mkenv(#null, P.alloc);
- putprimtypes(primenv);
- }
+fn finddecl_noparent(P *Parser, name *const u8) *Decl {
+ let p = envfind(primenv, name);
+ return p ?? envfind_noparent(P.curenv, name);
+}
+
+fn putdecl(P *Parser, eloc Loc, decl Decl) *Decl {
if envfind(primenv, decl.name) {
- fatal(P, decl.loc, "cannot shadow primitive `%s'", decl.name);
+ fatal(P, eloc, "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' (previously defined at %l)", decl.name, d0.loc);
+ fatal(P, eloc, "attempt to redefine `%s' (previously defined at %l)", decl.name, d0.loc);
}
return d;
}
@@ -569,6 +566,45 @@ fn putdecl(P *Parser, decl Decl) *Decl {
// Types Parsing //
///////////////////
+fn parseagg(P *Parser, kind AggKind, name *const u8, retdecl **Decl) *const Type {
+ let loc = P.tokloc;
+ let tok Tok #?;
+ static id int = 0;
+ let decl *Decl = name ? finddecl_noparent(P, name) : #null;
+ if decl {
+ switch decl.u {
+ case Ty ty; if !ty->is(:Agg) or !ty.u.Agg.fwd { decl = #null; }
+ }
+ }
+ let ty = decl ? decl.u.Ty : interntype({ .u: :Agg { kind, name, id++ }});
+ let agg = &(as(*Type)ty).u.Agg;
+
+ if name != #null and lexmatch(P, &tok, ';') {
+ agg.fwd = #t;
+ if decl == #null {
+ decl = putdecl(P, loc, { name, loc, .u: :Ty(ty)});
+ if retdecl {
+ *retdecl = decl;
+ }
+ }
+ return ty;
+ } else {
+ agg.fwd = #f;
+
+ }
+}
+
+fn xident2type(P *Parser, eloc Loc, name *const u8) *const Type {
+ let decl = finddecl(P, name);
+ if decl == #null {
+ fatal(P, eloc, "`%s' is not defined", name);
+ }
+ if decl.u.#tag != :Ty {
+ fatal(P, eloc, "`%s' is not a type", name);
+ }
+ return decl.u.Ty;
+}
+
fn parsetype(P *Parser) *const Type {
let tok Tok = {};
switch {
@@ -577,15 +613,7 @@ fn parsetype(P *Parser) *const Type {
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;
-
+ return xident2type(P, tok.loc, tok.u.ident);
case else;
fatal(P, tok.loc, "expected type (near %qT)", tok);
}
@@ -1186,6 +1214,7 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
} else if lexmatch(P, #null, '#?') {
ini = :None;
} else {
+ ini = :None;
fwd = #t;
}
}
@@ -1201,7 +1230,7 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
if !letp {
decl.u.#tag = :Static;
}
- yield(putdecl(P, decl), yarg);
+ yield(putdecl(P, tok.loc, decl), yarg);
if !lexmatch(P, &tok, ',') {
lexexpects(P, ';', "`,' or `;'");
@@ -1210,7 +1239,7 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
} while !lexmatch(P, &tok, ';');
}
-fn parsefn(P *Parser, externp bool, name *const u8) void {
+fn parsefn(P *Parser, externp bool, name *const u8) *Decl {
let decl Decl = {
name,
P.tokloc,
@@ -1231,8 +1260,19 @@ fn parsefn(P *Parser, externp bool, name *const u8) void {
lexexpect(P, ')');
break;
}
- let name = lexexpect(P, :ident).u.ident;
- let type = parsetype(P);
+ let name *const u8 = #null;
+ let type *const Type #?;
+ if lexmatch(P, &tok, :ident) {
+ let tok2 Tok #?;
+ if (tok2 = lexpeek(P)).t == ',' or tok2.t == ')' {
+ type = xident2type(P, tok.loc, tok.u.ident);
+ } else {
+ name = tok.u.ident;
+ type = parsetype(P);
+ }
+ } else {
+ type = parsetype(P);
+ }
paramnames->push(name);
paramtys->push(type);
if !lexmatch(P, &tok, ',') {
@@ -1247,11 +1287,11 @@ fn parsefn(P *Parser, externp bool, name *const u8) void {
}});
static id int = 0;
Fn.id = ++id;
- putdecl(P, decl);
+ let decl = putdecl(P, tok.loc, decl);
if !lexmatch(P, #null, '{') {
lexexpects(P, ';', "';' or '{'");
- return;
+ return decl;
}
with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf },
@@ -1260,7 +1300,7 @@ fn parsefn(P *Parser, externp bool, name *const u8) void {
P.curenv = env;
foreach(name, i, Fn.paramnames,
if name {
- putdecl(P, { name, tok.loc, .u: :Let {
+ putdecl(P, tok.loc, { name, tok.loc, .u: :Let {
Fn.ty.u.Fn.params[i], :None, #f, Fn.id,
}});
}
@@ -1271,6 +1311,7 @@ fn parsefn(P *Parser, externp bool, name *const u8) void {
(as(*Arena)P.alloc.a)->destroy();
);
);
+ return decl;
}
fn doimport(P *Parser, path *const u8) [#]Decl {
@@ -1313,13 +1354,14 @@ fn doimport(P *Parser, path *const u8) [#]Decl {
P2.is_header = #t;
let e = seen->put(rpath, { {}, #t });
let decls = parse(&P2);
+
*e = { decls, #f };
return decls;
}
fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
let tok = Tok { .loc: P.tokloc },
- decl = Decl {},
+ decl *Decl = {},
externp = #f,
name *const u8 = #null;
@@ -1333,25 +1375,85 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void {
switch {
case lexmatch(P, &tok, :kw_fn);
let name = lexmatch(P, &tok, :ident) ? tok.u.ident : #null;
- parsefn(P, externp, name);
- return;
+ decl = parsefn(P, externp, name);
+
case lexmatch(P, &tok, :kw_import);
let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr;
let decls = doimport(P, path);
foreach(decl, _, decls,
- let decl = putdecl(P, decl);
+ let decl = putdecl(P, tok.loc, decl);
if yield {
yield(decl, yarg);
}
)
lexexpect(P, ';');
return;
+
+ case lexmatch(P, &tok, :kw_struct);
+ let name = lexexpect(P, :ident).u.ident;
+ parseagg(P, :Struct, name, &decl);
+
+ case lexmatch(P, &tok, :kw_static);
+ struct Arg {
+ P *Parser,
+ externp bool,
+ toplevel bool,
+ yield DeclYielder,
+ yarg *void,
+ }
+ fn varyield(decl *Decl, arg *void) void {
+ let a *Arg = arg;
+ let Var = decl.u.Static;
+ if !a.toplevel and !a.externp and Var.fwd {
+ err(a.P, decl.loc, "static variable inside function cannot be forward-declared");
+ }
+ if !a.toplevel and a.externp and !Var.fwd {
+ err(a.P, decl.loc, "extern variable inside function cannot be initialized");
+ }
+ if a.P.is_header and a.externp and !Var.fwd {
+ err(a.P, decl.loc, "extern variable inside header cannot be initialized");
+ }
+ if a.yield {
+ a.yield(decl, a.yarg);
+ }
+ }
+ parsevardecl(P, toplevel, externp, #{let?} #t, &varyield, &Arg { P, externp, toplevel, yield, yarg });
+ return;
+
+ case lexmatch(P, &tok, :kw_def);
+ do {
+ let tok = lexexpect(P, :ident),
+ name = tok.u.ident,
+ ini Expr #?,
+ ty *const Type = #null;
+
+ if lexmatch(P, #null, '=') {
+ ini = parseexpr(P);
+ ty = unconstify(ini.ty);
+ } else {
+ ty = parsetype(P);
+ lexexpect(P, '=');
+ ini = parseexpr(P);
+ }
+ if !typematchestarg(ty, ini.ty) {
+ err(P, ini.loc, "incompatible initializer (%t, expected %t)", ini.ty, ty);
+ }
+ yield(putdecl(P, tok.loc, {
+ name, tok.loc, .u: :Def(ini)
+ }), yarg);
+ if !lexmatch(P, &tok, ',') {
+ lexexpects(P, ';', "`,' or `;'");
+ break;
+ }
+ } while !lexmatch(P, &tok, ';');
+ return;
+
case else;
fatal(P, tok.loc, "expected declaration (near %qT)", tok);
}
- if yield {
- yield(putdecl(P, decl), yarg);
+ if yield != #null and decl != #null {
+ yield(decl, yarg);
}
}
@@ -1361,6 +1463,10 @@ extern fn parse(P *Parser) [#]Decl {
let decls Vec<Decl> = {};
P.alloc = &alloc;
P.curenv = mkenv(#null, P.alloc);
+ if primenv == #null {
+ primenv = mkenv(#null, P.alloc);
+ putprimtypes(primenv);
+ }
while !P.eof {
fn yield(decl *Decl, arg *void) void {
diff --git a/src/type.cff b/src/type.cff
index a4a4e3d..b18c5c4 100644
--- a/src/type.cff
+++ b/src/type.cff
@@ -29,7 +29,16 @@ struct TypeTraits {
case Slice child;
h = fnv1a_i(h, child.id);
+ case Fn f;
+ h = fnv1a_i(h, f.ret.id);
+ foreach(p, _, f.params,
+ h = fnv1a_i(h, p.id);
+ )
+
+ case Agg agg;
+ h = fnv1a_i(h, agg.id);
}
+
return h;
}
@@ -40,15 +49,20 @@ struct TypeTraits {
}
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 {
@@ -66,6 +80,10 @@ struct TypeTraits {
}
)
return #t;
+
+ case Agg agg;
+ let bgg = b.u.Agg;
+ return agg.id == bgg.id;
}
return #f;
}