aboutsummaryrefslogtreecommitdiff
path: root/src/parse.cff
diff options
context:
space:
mode:
author lemon<lsof@mailbox.org>2022-08-15 12:22:47 +0200
committer lemon<lsof@mailbox.org>2022-08-15 12:22:47 +0200
commitf906d0b350b0b4ceb747669c9a9845d11bd0e486 (patch)
tree5f09a7b714e6ce93f6094a06e5f736513110fb8d /src/parse.cff
parentf802bb99263aaa5be492999babd44cd2fdb1c65f (diff)
self hosted progress
Diffstat (limited to 'src/parse.cff')
-rw-r--r--src/parse.cff327
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();
}