diff options
Diffstat (limited to 'src/parse.cff')
| -rw-r--r-- | src/parse.cff | 327 |
1 files changed, 317 insertions, 10 deletions
diff --git a/src/parse.cff b/src/parse.cff index 25a2617..0f06259 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -139,7 +139,7 @@ fn eatspaces(P *Parser) void { } // !sorted -static keyword2str []*const u8 = { +extern static keyword2str [NUM_KEYWORDS]*const u8 = { "and", "as", "break", "case", "const", "continue", "def", "defmacro", "do", "else", "enum", "extern", "fn", @@ -214,14 +214,64 @@ fn readnumber(s *const u8) Option<Tok> { } let tok = Tok {}; - assert(suffix == #null,"sufx"); if flt { tok.t = :flo; tok.u.flo = accf; + + switch { + case suffix == #null; tok.ty = ty_f64; + case strcieq(suffix, "f"); tok.ty = ty_f32; + case strcieq(suffix, "f32"); tok.ty = ty_f32; + case strcieq(suffix, "f64"); tok.ty = ty_f64; + case else; return :None; + } + return :Some tok; } else { tok.t = :int; tok.u.uint = acc; + + fn ty4intX(i u64) *const Type { + if i < 1u64 << ((g_targ.intsize * 8) - 1) { + return ty_int; + } + if i < 1u64 << 31 { + return ty_i32; + } + if i < 1u64 << 63 { + return ty_i64; + } + return ty_u64; + } + + fn ty4uintX(i u64) *const Type { + if i < 1u64 << (g_targ.intsize * 8) { + return ty_uint; + } + if i < 1u64 << 32 { + return ty_u32; + } + return ty_u64; + } + + switch { + case suffix == #null; tok.ty = ty4intX(tok.u.uint); + case strcieq(suffix, "u"); tok.ty = ty4uintX(tok.u.uint); + case strcieq(suffix, "u8"); tok.ty = ty_u8; + case strcieq(suffix, "i8"); tok.ty = ty_i8; + case strcieq(suffix, "u16"); tok.ty = ty_u16; + case strcieq(suffix, "i16"); tok.ty = ty_i16; + case strcieq(suffix, "u32"); tok.ty = ty_u32; + case strcieq(suffix, "i32"); tok.ty = ty_i32; + case strcieq(suffix, "u64"); tok.ty = ty_u64; + case strcieq(suffix, "i64"); tok.ty = ty_i64; + case strcieq(suffix, "z"); tok.ty = ty_usize; + case strcieq(suffix, "zs"); tok.ty = ty_isize; + case strcieq(suffix, "p"); tok.ty = ty_uptrint; + case strcieq(suffix, "ps"); tok.ty = ty_iptrint; + case else; return :None; + } + return :Some tok; } } @@ -445,23 +495,280 @@ fn lex(P *Parser) Tok { fatal(P, tok.loc, "stray %qc in program", c); } -extern fn parse(P *Parser) [#]Decl { - fn mallocator_allocf(*void, n usize) *void { - return xmalloc(n); +fn lexpeek(P *Parser) Tok { + switch P.peektok { + case Some t; return t; } + let tok = lex(P); + P.peektok = :Some tok; + return tok; +} - fn mallocator_freef(*void, ptr *void) void { - free(ptr); +fn lexmatch(P *Parser, tokp *Tok, t TokT) bool { + let tok = lexpeek(P); + if tokp { + *tokp = tok; } + if tok.t == t { + lex(P); + return #t; + } + return #f; +} + +fn lexexpects(P *Parser, t TokT, what *const u8) Tok { + let tok = Tok {}; + if not lexmatch(P, &tok, t) { + if what { + fatal(P, tok.loc, "expected %s (near %qT)", what, tok); + } else { + fatal(P, tok.loc, "expected %qk (near %qT)", t, tok); + } + } + return tok; +} +defmacro lexexpect(P, t) [lexexpects(P, t, #null)] +//////////// +// Parser // +//////////// + +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 ? p : envfind(P.curenv, name); +} + +fn putdecl(P *Parser, decl Decl) *Decl { + assert(primenv != #null, "primenv"); + if envfind(primenv, decl.name) { + fatal(P, decl.loc, "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'", decl.name); + } + return d; +} + +/////////////////// +// Types Parsing // +/////////////////// + +fn parsetype(P *Parser) *const Type { + let tok Tok = {}; + switch { + case lexmatch(P, &tok, :kw_const); + return constify(parsetype(P)); + 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; + + case else; + fatal(P, tok.loc, "expected type (near %qT)", tok); + } +} + +///////////////////////// +// Expressions Parsing // +///////////////////////// + +fn pexprimary(P *Parser) Expr { + let tok Tok = lex(P); + switch tok.t { + case :int, :chr; + return { tok.loc, tok.ty, .u: :IntLit { tok.u.int }}; + case :flo; + return { tok.loc, tok.ty, .u: :FloLit tok.u.flo }; + case :bool; + return { tok.loc, ty_bool, .u: :BoolLit tok.u.bool }; + case :null; + return { tok.loc, ty_voidptr, .u: :NullLit }; + case :str; + return { tok.loc, .u: :StrLit tok.u.str }; + case else; + fatal(P, tok.loc, "expected expression (near %qT)", tok); + } +} + +fn pexpostfix(P *Parser) Expr { + let ex = pexprimary(P); + return ex; +} + +fn pexprefix(P *Parser) Expr { + return pexpostfix(P); +} + +fn pexbitarith(P *Parser) Expr { + let ex = pexprefix(P); + return ex; +} + +fn pexcmp(P *Parser) Expr { + let ex = pexbitarith(P); + return ex; +} + +fn pexlog(P *Parser) Expr { + let ex = pexcmp(P); + return ex; +} + +fn pexcond(P *Parser) Expr { + let ex = pexlog(P); + return ex; +} + +fn pexassign(P *Parser) Expr { + let ex = pexcond(P); + return ex; +} + +fn parseexpr(P *Parser) Expr { + return pexassign(P); +} + +//////////////////////// +// Statements Parsing // +//////////////////////// + +typedef DeclYielder *fn(*Decl, *void) void; +typedef StmtYielder *fn(Stmt, *void) void; + +fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { + let tok Tok = {}; + switch { + case lexmatch(P, &tok, :kw_if); + case else; + return yield({ tok.loc, .u: :Expr parseexpr(P) }, yarg); + } +} + +fn parseblock0(P *Parser) [#]Stmt { + let sts Vec<Stmt> = {}; + let tok Tok = {}; + while (tok = lexpeek(P)).t != '}' and tok.t != :kw_case and tok.t != '}' { + if lexmatch(P, #null, ';') { continue; } + fn pushstmt(st Stmt, arg *void) void { + let sts *Vec<Stmt> = arg; + sts->push(st); + } + parsestmts(P, &pushstmt, &sts); + } + return sts->move(P.alloc); +} + +fn parseblock(P *Parser) [#]Stmt { + let b = parseblock0(P); + lexexpect(P, '}'); + return b; +} + +/////////////////// +// Decls Parsing // +/////////////////// + +fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void { + decl.name = name; + decl.u = :Fn {}; + + let Fn = &decl.u.Fn, + tok Tok = {}, + paramnames Vec<*const u8> = {}, + paramtys Vec<*const Type> = {}; + + lexexpect(P, '('); + while not lexmatch(P, &tok, ')') { + if lexmatch(P, #null, '...') { + Fn.variadic = #t; + lexmatch(P, #null, ','); + lexexpect(P, ')'); + break; + } + let name = lexexpect(P, :ident).u.ident; + let type = parsetype(P); + paramnames->push(name); + paramtys->push(type); + if not lexmatch(P, &tok, ',') { + lexexpect(P, ')'); + break; + } + } + Fn.paramnames = paramnames->move(P.alloc); + let retty = parsetype(P); + Fn.ty = interntype({ .size: 0, .align: 1, .u: :Fn { + paramtys->move(P.alloc), Fn.variadic, retty + }}); + static id int = 0; + Fn.id = ++id; + + if not lexmatch(P, #null, '{') { + lexexpects(P, ';', "';' or '{'"); + return; + } + + with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }, + Fn.body = :Some parseblock(P); + (as(*Arena)P.alloc.a)->destroy(); + ); +} + +fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void { + let tok = Tok { .loc: P.tokloc }, + decl = Decl {}, + externp = #f, + name *const u8 = #null; + + if lexmatch(P, &tok, :kw_extern) { + externp = #t; + } + + 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; + case else; + fatal(P, tok.loc, "expected declaration (near %qT)", tok); + } + + yield(putdecl(P, decl), yarg); +} + +extern fn parse(P *Parser) [#]Decl { let aralloc = Arena {}; let alloc = Allocator { &aralloc, &Arena:allocf, #null }; + let decls Vec<*Decl> = {}; P.alloc = &alloc; + P.curenv = mkenv(#null, P.alloc); + while not P.eof { - let tok = lex(P); - if tok.t == :eof { break; } - efmt("* tok: %qT\n", tok); + fn yield(decl *Decl, arg *void) void { + let decls *Vec<*Decl> = arg; + decls->push(decl); + } + parsedecls(P, &yield, &decls); + if lexmatch(P, #null, :eof) { + break; + } } + + envfree(P.curenv); aralloc->destroy(); } |