diff options
Diffstat (limited to 'src/parse.cff')
| -rw-r--r-- | src/parse.cff | 153 |
1 files changed, 134 insertions, 19 deletions
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); |