From 6e30b8a913caf1817d7e9bd0bb9783d9f7aa1ed1 Mon Sep 17 00:00:00 2001 From: lemon Date: Wed, 17 Aug 2022 09:33:14 +0200 Subject: if and loopses --- src/cffc.hff | 13 +++++ src/parse.cff | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 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, + body [#]Stmt, + }, + Return Option, 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:None; + if lexmatch(P, #null, :kw_let) { + let sts Vec = {}; + fn yieldlet(st Stmt, arg *void) void { + let sts *Vec = 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); -- cgit v1.2.3