aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cffc.hff13
-rw-r--r--src/parse.cff153
2 files changed, 147 insertions, 19 deletions
diff --git a/src/cffc.hff b/src/cffc.hff
index bc49f29..6147377 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -82,10 +82,12 @@ struct Type {
}
}
+struct Fn;
struct Parser {
fp *FILE,
alloc *Allocator,
curfile *const u8,
+ curfn *Fn,
curenv *Env,
tokloc Loc,
curloc Loc,
@@ -128,10 +130,21 @@ struct Expr {
}
}
+
+struct Stmt;
struct Stmt {
loc Loc,
u enum union {
Block [#]Stmt,
+ If struct { test Expr, t [#]Stmt, f [#]Stmt },
+ While struct { test Expr, body [#]Stmt },
+ For struct {
+ ini [#]Stmt,
+ test Expr,
+ next Option<Expr>,
+ body [#]Stmt,
+ },
+ Return Option<Expr>,
Expr Expr,
Decl *Decl,
}
diff --git a/src/parse.cff b/src/parse.cff
index db161d7..098d916 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -998,6 +998,10 @@ fn parseexpr(P *Parser) Expr {
// Statements Parsing //
////////////////////////
+fn stmtdup(alloc *Allocator, st Stmt) *Stmt {
+ return memcpy(alloc->alloc(sizeof(st)), &st, sizeof(st));
+}
+
typedef DeclYielder *fn(*Decl, *void) void;
typedef StmtYielder *fn(Stmt, *void) void;
fn parseblock(P *Parser) [#]Stmt;
@@ -1016,6 +1020,97 @@ fn pstlet(P *Parser, yield StmtYielder, yarg *void) void {
parsevardecl(P, #{toplevel?} #f, #{extern?} #f, #{let?} #t, &varyield, &Arg { P, yield, yarg });
}
+fn pstif(P *Parser) Stmt {
+ let loc = P.tokloc;
+ let test = parseexpr(P);
+ if !test.ty->is(:Bool) and !test.ty->is(:Ptr) {
+ err(P, test.loc, "if condition must be bool or pointer (%t)", test.ty);
+ }
+ lexexpect(P, '{');
+ let t = parseblock(P);
+ if lexmatch(P, #null, :kw_else) {
+ if lexmatch(P, #null, :kw_if) {
+ let f = stmtdup(P.alloc, pstif(P));
+ return { loc, :If { test, t, f[0::1] }};
+ } else {
+ lexexpect(P, '{');
+ let f = parseblock(P);
+ return { loc, :If { test, t, f }};
+ }
+ }
+ return { loc, :If { test, t }};
+}
+
+fn pstreturn(P *Parser) Stmt {
+ let loc = P.tokloc;
+ if P.curfn == #null {
+ fatal(P, loc, "return outside function");
+ }
+ let retty = P.curfn.ty.u.Fn.ret;
+ if lexmatch(P, #null, ';') {
+ if retty != ty_void {
+ err(P, loc, "return with no value in function returning %t", retty);
+ }
+ return { loc, :Return{} };
+ } else {
+ let ex = parseexpr(P);
+ lexexpect(P, ';');
+ if !typematchestarg(retty, ex.ty) {
+ err(P, ex.loc, "incompatible type in return statement (%t, expected %t)",
+ ex.ty, retty);
+ }
+ return { loc, :Return(:Some(ex)) };
+ }
+}
+
+fn pstwhile(P *Parser) Stmt {
+ let loc = P.tokloc;
+ let test = parseexpr(P);
+ if !test.ty->is(:Bool) and !test.ty->is(:Ptr) {
+ err(P, test.loc, "while condition must be bool or pointer (%t)", test.ty);
+ }
+ lexexpect(P, '{');
+ let t = parseblock(P);
+ return { loc, :While { test, t }};
+}
+
+fn pstfor(P *Parser) Stmt {
+ let loc = P.tokloc;
+ let ini [#]Stmt = {};
+ let test Expr #?;
+ let next = Option<Expr>:None;
+ if lexmatch(P, #null, :kw_let) {
+ let sts Vec<Stmt> = {};
+ fn yieldlet(st Stmt, arg *void) void {
+ let sts *Vec<Stmt> = arg;
+ sts->push(st);
+ }
+ pstlet(P, &yieldlet, &sts);
+ ini = sts->move(P.alloc);
+ } else if lexmatch(P, #null, ';') {
+ // pass
+ } else {
+ let ex = parseexpr(P);
+ ini = stmtdup(P.alloc, { ex.loc, :Expr(ex) })[0::1];
+ }
+
+ if lexmatch(P, #null, ';') {
+ // for (...; ; ...;) -> for (...; #t; ...;)
+ test = { P.tokloc, ty_bool, :BoolLit(#t) };
+ } else {
+ test = parseexpr(P);
+ lexexpect(P, ';');
+ }
+ if !test.ty->is(:Bool) and !test.ty->is(:Ptr) {
+ err(P, test.loc, "for condition must be bool or pointer (%t)", test.ty);
+ }
+ if !lexmatch(P, #null, '{') {
+ next = :Some(parseexpr(P));
+ lexexpect(P, '{');
+ }
+ return { loc, :For { ini, test, next, parseblock(P) }};
+}
+
fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void {
let tok Tok = {};
switch {
@@ -1023,8 +1118,22 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void {
return yield({
tok.loc, .u: :Block(parseblock(P))
}, yarg);
+
case lexmatch(P, &tok, :kw_let);
return pstlet(P, yield, yarg);
+
+ case lexmatch(P, &tok, :kw_if);
+ return yield(pstif(P), yarg);
+
+ case lexmatch(P, &tok, :kw_return);
+ return yield(pstreturn(P), yarg);
+
+ case lexmatch(P, &tok, :kw_while);
+ return yield(pstwhile(P), yarg);
+
+ case lexmatch(P, &tok, :kw_for);
+ return yield(pstfor(P), yarg);
+
case else;
let ex = parseexpr(P);
lexexpect(P, ';');
@@ -1101,9 +1210,13 @@ fn parsevardecl(P *Parser, toplevel bool, externp bool, letp bool, yield DeclYie
} while !lexmatch(P, &tok, ';');
}
-fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void {
- decl.name = name;
- decl.u = :Fn {};
+fn parsefn(P *Parser, externp bool, name *const u8) void {
+ let decl Decl = {
+ name,
+ P.tokloc,
+ externp,
+ .u: :Fn {}
+ };
let Fn = &decl.u.Fn,
tok Tok = {},
@@ -1134,6 +1247,7 @@ fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void {
}});
static id int = 0;
Fn.id = ++id;
+ putdecl(P, decl);
if !lexmatch(P, #null, '{') {
lexexpects(P, ';', "';' or '{'");
@@ -1141,19 +1255,21 @@ fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void {
}
with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf },
- let env = mkenv(P.curenv, P.alloc);
- P.curenv = env;
- foreach(name, i, Fn.paramnames,
- if name {
- putdecl(P, { name, tok.loc, .u: :Let {
- Fn.ty.u.Fn.params[i], :None, #f, Fn.id,
- }});
- }
- )
- Fn.body = :Some(parseblock(P));
- P.curenv = envparent(env);
- envfree(env);
- (as(*Arena)P.alloc.a)->destroy();
+ with_tmpchange(P.curfn, Fn,
+ let env = mkenv(P.curenv, P.alloc);
+ P.curenv = env;
+ foreach(name, i, Fn.paramnames,
+ if name {
+ putdecl(P, { name, tok.loc, .u: :Let {
+ Fn.ty.u.Fn.params[i], :None, #f, Fn.id,
+ }});
+ }
+ )
+ Fn.body = :Some(parseblock(P));
+ P.curenv = envparent(env);
+ envfree(env);
+ (as(*Arena)P.alloc.a)->destroy();
+ );
);
}
@@ -1217,9 +1333,8 @@ 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, &decl, externp, name);
- decl.loc = tok.loc;
- decl.externp = externp;
+ parsefn(P, externp, name);
+ return;
case lexmatch(P, &tok, :kw_import);
let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr;
let decls = doimport(P, path);