diff options
| -rw-r--r-- | bootstrap/all.h | 10 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 40 | ||||
| -rw-r--r-- | bootstrap/dump.c | 2 | ||||
| -rw-r--r-- | bootstrap/parse.c | 25 | ||||
| -rw-r--r-- | bootstrap/types.c | 7 | ||||
| -rw-r--r-- | src/all.hff | 23 | ||||
| -rw-r--r-- | src/fmt.cff | 94 | ||||
| -rw-r--r-- | src/libc.hff | 14 | ||||
| -rw-r--r-- | src/parse.cff | 83 | ||||
| -rw-r--r-- | src/util.cff | 18 | ||||
| -rw-r--r-- | vgcore.175407 | bin | 0 -> 6742016 bytes | |||
| -rw-r--r-- | vgcore.175479 | bin | 0 -> 6746112 bytes | |||
| -rw-r--r-- | vgcore.175493 | bin | 0 -> 6746112 bytes |
13 files changed, 295 insertions, 21 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index 6a0de81..f8eb769 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -157,6 +157,7 @@ enum typetype { TYstruct, TYunion, TYeunion, + TYvalist, }; struct type { @@ -327,6 +328,9 @@ enum exprtype { Elen, Eeuini, Eeutag, + Evastart, + Evaarg, + Evaend, }; struct blockstmt { @@ -488,7 +492,9 @@ static const struct targ { llongsize, sizesize, f32align, - f64align; + f64align, + valistsize, + valistalign; bool charsigned; } g_targ = { .ptrsize = sizeof(void *), @@ -499,6 +505,8 @@ static const struct targ { .sizesize = sizeof(size_t), .f32align = alignof(float), .f64align = alignof(double), + .valistsize = sizeof(va_list), + .valistalign = alignof(va_list), .charsigned = CHAR_MIN < 0, }; diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 1629151..6321e92 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -37,6 +37,9 @@ gentype(const struct type *ty) { case TYenum: gentype(ty->enu.intty); break; + case TYvalist: + pri("__builtin_va_list"); + break; } if (ty->konst) pri(" const"); @@ -168,6 +171,7 @@ geneuiniex(struct expr *ex) { static void genexpr(struct expr *ex) { + assert(ex); const struct type *ty = unconstify(ex->ty); struct decl *decl; @@ -284,6 +288,15 @@ genexpr(struct expr *ex) { geneuiniex(ex); pri(")"); break; + case Evastart: + pri("__builtin_va_start(%e, %e)", ex->binop.lhs, ex->binop.rhs); + break; + case Evaarg: + pri("__builtin_va_arg(%e, %t)", ex->child, ex->ty); + break; + case Evaend: + pri("__builtin_va_end(%e)", ex->child); + break; } } @@ -343,15 +356,15 @@ genstmt(struct stmt *stmt) { } break; case Swhile: - pri("while (%e) {", &stmt->loop.test); + pri("while (%e) {_cont%d:;", &stmt->loop.test, stmt->loop.id); genblock(stmt->loop.body); - pri("_cont%d:;} _brk%d:;\n", stmt->loop.id, stmt->loop.id); + pri("} _brk%d:;\n", stmt->loop.id); break; case Sdowhile: - pri("do {"); + pri("do {_cont%d:;",stmt->loop.id); genblock(stmt->loop.body); - pri("_cont%d:;} while (%e); _brk%d:;\n", - stmt->loop.id, &stmt->loop.test, stmt->loop.id); + pri("} while (%e); _brk%d:;\n", + &stmt->loop.test, stmt->loop.id); break; case Sfor: pri("{\n"); @@ -361,10 +374,12 @@ genstmt(struct stmt *stmt) { pri("%e;", &stmt->loop.test); if (stmt->loop.next) pri(" %e", stmt->loop.next); - pri(") {"); + pri(") {goto _skip%d; _cont%d:", stmt->loop.id, stmt->loop.id); + if (stmt->loop.next) + pri(" %e;", stmt->loop.next); + pri("_skip%d:;", stmt->loop.id); genblock(stmt->loop.body); - pri("_cont%d:; }", stmt->loop.id); - pri("} _brk%d:;\n", stmt->loop.id); + pri("}} _brk%d:;\n", stmt->loop.id); break; case Siswitch: pri("switch (%e) {", &stmt->iswitch.test); @@ -505,6 +520,13 @@ liftnestedex(struct expr *ex) { case Eeuini: liftnestedex(ex->euini.ini); break; + case Evastart: + liftnestedex(ex->binop.lhs); + liftnestedex(ex->binop.rhs); + break; + case Evaarg: case Evaend: + liftnestedex(ex->child); + break; } } @@ -676,7 +698,7 @@ defctype(const struct type *ty, void *_) { if (!ty->_cname) switch (ty->t) { - case TYvoid: case TYbool: + case TYvoid: case TYbool: case TYvalist: case TYenum: case TYint: case TYfloat: break; case TYptr: diff --git a/bootstrap/dump.c b/bootstrap/dump.c index 3bd915f..bf63d22 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -66,6 +66,8 @@ pritype(const struct type *ty) { epri("%bc", '>'); } break; + case TYvalist: + epri("va_list"); } } diff --git a/bootstrap/parse.c b/bootstrap/parse.c index d0bea8e..7c31ce1 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -1416,7 +1416,32 @@ pexpostfix(struct parser *P) { if (lexpeek(P).t != '(') lexexpect(P, '('); + } else if (ex.ty->t == TYvalist) { + if (!strcmp(fnam, "start")) { + lexexpect(P, '('); + struct expr ex2 = parseexpr(P); + lexexpect(P, ')'); + ex = (struct expr) { + Evastart, ex.span, ty_void, + .binop.lhs = exprdup(ex), + .binop.rhs = exprdup(ex2) + }; + } else if (!strcmp(fnam, "arg")) { + lexexpect(P, '('); + ty = parsetype(P); + lexexpect(P, ')'); + ex = (struct expr) { + Evaarg, ex.span, ty, .child = exprdup(ex) + }; + } else if (!strcmp(fnam, "end")) { + lexexpect(P, '('); + lexexpect(P, ')'); + ex = (struct expr) { + Evaend, ex.span, ty_void, .child = exprdup(ex) + }; + } else goto badmet; } else { + badmet: fatal(P, tok.span, "cannot call `->%s': left-hand-side is not an aggregate (%t)", fnam, ex.ty); } diff --git a/bootstrap/types.c b/bootstrap/types.c index 9118392..66ae840 100644 --- a/bootstrap/types.c +++ b/bootstrap/types.c @@ -36,7 +36,7 @@ hashtype(const struct type *ty) { epri(""); switch (ty->t) { - case TYvoid: case TYbool: + case TYvoid: case TYbool: case TYvalist: break; case TYint: case TYfloat: h = fnv1az(h, ty->size); @@ -75,7 +75,7 @@ typeeql(const struct type *lhs, const struct type *rhs) { if (lhs->t != rhs->t || lhs->konst != rhs->konst) return 0; switch (lhs->t) { - case TYvoid: case TYbool: + case TYvoid: case TYbool: case TYvalist: return 1; case TYfloat: return lhs->size == rhs->size; @@ -206,7 +206,7 @@ const struct type *ty_iptrint, *ty_uptrint, *ty_c_int, *ty_c_uint, *ty_c_char, *ty_c_schar, *ty_c_uchar, *ty_c_short, *ty_c_ushort, *ty_c_long, *ty_c_ulong, *ty_c_llong, - *ty_c_ullong; + *ty_c_ullong, *ty_valist; void putprimtypes(struct env *env) { @@ -248,6 +248,7 @@ putprimtypes(struct env *env) { {"c_ulong", &ty_c_ulong, {TYint, t.longsize, IU}}, {"c_llong", &ty_c_llong, {TYint, t.llongsize, IS}}, {"c_ullong", &ty_c_ullong, {TYint, t.llongsize, IU}}, + {"va_list", &ty_valist, {TYvalist, t.valistsize, t.valistalign}}, #undef IS #undef IU }; diff --git a/src/all.hff b/src/all.hff index e1cb26a..a35694b 100644 --- a/src/all.hff +++ b/src/all.hff @@ -4,11 +4,11 @@ import "option.hff"; /// Macros defmacro assert { -(ex, s) [ +(ex, s, ...args) [ (do if not (ex) { fprintf(stderr, "%s:%d: assertion failed: ", #FILE, #LINE); - fprintf(stderr, "`%s'", (s)); + fprintf(stderr, s, args); fprintf(stderr, "\n"); abort(); } @@ -44,7 +44,7 @@ struct Loc { } #[lax] -enum TokT { +enum TokT : i32 { // !sorted kw_and, kw_as, kw_break, kw_case, kw_const, kw_continue, kw_def, kw_defmacro, kw_do, @@ -68,10 +68,11 @@ enum TokT { strify, eof, } +def NUM_KEYWORDS = TokT:NUM_KEYWORDS; struct Tok { - t int, + t TokT, loc Loc, ty *const Type, u union { @@ -108,3 +109,17 @@ def FNV1A_INI u32 = 0x811c9dc5; extern fn fnv1a(h u32, [#]const u8) u32; extern fn fnv1a_s(h u32, *const u8) u32; extern fn addfilepath(*const u8) int; +extern fn fatal(*Parser, Loc, fmt *const u8, ...) void; + +// fmt.cff +extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, va_list) void; +extern fn vefmt(fmt *const u8, ap va_list) void; +extern fn efmt(fmt *const u8, ...) void; + +// Inline functions +fn bswap32(x u32) u32 { + return (x >> 24) + | ((x >> 8) & 0x00FF00) + | ((x << 8) & 0xFF0000) + | (x << 24); +} diff --git a/src/fmt.cff b/src/fmt.cff new file mode 100644 index 0000000..885ea50 --- /dev/null +++ b/src/fmt.cff @@ -0,0 +1,94 @@ +import "all.hff"; + +extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) void { + defmacro p(x) [ proc(x, parg) ] + defmacro ps(s) [ + for let $i = 0; (s)[$i] != 0; ++$i { + p(s[$i]); + } + ] + let buf [100]u8 = {}; + for let c u8 = *fmt; c != 0; c = *++fmt { + assert(c != 0, "?"); + if c != '%' { + p(c); + if fmt[1] == 0 { break; } + continue; + } + let quote = #f; + #'fmt do { + switch (c = *++fmt) { + case 'i'; + sprintf(buf, "%d", ap->arg(int)); + ps(buf); + case 'q'; + quote = #t; + continue #'fmt; + case 'c'; + let ch u32 = ap->arg(int); + if quote { + extern fn isprint(int) int; + p('\''); + for ch = bswap32(ch); ch != 0; ch >>= 8 { + if ch & 0xFF != 0 { + if isprint(ch) != 0 { p(ch); } + else { + p('\\'); + p('0' + (ch % 8)); + p('0' + ((ch / 8) % 8)); + p('0' + ((ch / 8 / 8) % 8)); + } + } + } + p('\''); + } else { + if ch == 0 { p(0); } + else { + for ch = bswap32(ch); ch != 0; ch >>= 8 { + if ch & 0xFF != 0 { + p(ch & 0xFF); + } + } + } + } + case 's'; + let s = ap->arg(*const u8); + if quote { + extern fn isprint(int) int; + p('\"'); + for let c u8 #?; (c = *s++) != 0; { + if isprint(c) != 0 { + p(c); + } else { + p('\\'); + p('0' + (c % 8)); + p('0' + ((c / 8) % 8)); + p('0' + ((c / 8 / 8) % 8)); + } + } + p('\"'); + } else { + ps(s); + } + case else + // assert(#f, "bad fmt '%c' @ %d", c, i); + } + } while #f; + } +} + +extern fn vefmt(fmt *const u8, ap va_list) void { + fn epri(c u8, *void) void { + fputc(c, stderr); + } + + vpfmt(&epri, #null, fmt, ap); +} + +extern fn efmt(fmt *const u8, ...) void { + let ap va_list #?; + ap->start(fmt); + + vefmt(fmt, ap); + ap->end(); +} diff --git a/src/libc.hff b/src/libc.hff index 7e2268c..a231614 100644 --- a/src/libc.hff +++ b/src/libc.hff @@ -3,11 +3,14 @@ struct FILE; extern static stdin *FILE, stdout *FILE, stderr *FILE; -extern fn printf(fmt *const u8, ...) void; -extern fn fprintf(fp *FILE, fmt *const u8, ...) void; +extern fn printf(fmt *const u8, ...) int; +extern fn fprintf(fp *FILE, fmt *const u8, ...) int; +extern fn sprintf(*u8, fmt *const u8, ...) int; +extern fn snprintf(*u8, usize, fmt *const u8, ...) int; extern fn fopen(path *const u8, mode *const u8) *FILE; -extern fn fclose(fp *FILE) int; -extern fn fgetc(fp *FILE) int; +extern fn fclose(*FILE) int; +extern fn fgetc(*FILE) int; +extern fn fputc(int, *FILE) int; def EOF = -1; // stdlib.h @@ -21,3 +24,6 @@ extern fn free(p *void) void; // string.h extern fn strlen(s *const u8) usize; extern fn strcmp(a *const u8, b *const u8) int; + +//ctype.h +extern fn tolower(int) int; diff --git a/src/parse.cff b/src/parse.cff index f659322..d672173 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -80,6 +80,20 @@ fn issep(c u8) bool { return #f; } +fn readtilsep(P *Parser, buf [#]u8, dot bool) int { + let i = 0, + c u8 #?; + while (not issep(c = chrpeek(P))) or (dot and c == '.') { + chr(P); + if i >= buf.#len - 1 { + return -1; + } + buf[i++] = c; + } + buf[i++] = 0; + return i; +} + fn eatspaces(P *Parser) void { for ;;chr(P) { if not isspace(chrpeek(P)) { @@ -115,6 +129,59 @@ fn str2keyword(s *const u8) int { return -1; } +fn readnumber(s *const u8) Option<Tok> { + let c u8 #?, + acc = 0u64, + accf = 0.0f64, + fmul = 0.1, + base = 10, + flt = #f, + nused = 0, + suffix *const u8 = #null; + + for let i = 0; (c = s[i]) != 0; ++i { + if i == 0 and c == '0' { + --nused; + } + if i == 1 and tolower(c) == 'x' { + base = 16; + continue; + } + if not flt and c == '.' and base == 10 { + flt = #t; + accf = acc; + continue; + } + if nused > 0 and c == '_' { continue; } + if (base == 16 and not isdigit(c)) + or (base != 16 and (c < '0' or c > ('0' + base) - 1)) { + suffix = s + i; + } + + ++nused; + if flt { + accf = accf + ((c - '0') * fmul); + fmul *= 0.1; + } else { + c = tolower(c); + acc = (acc * base) + (c <= '9' ? c - '0' : (c - 'a') + 10); + } + } + + let tok = Tok {}; + if flt { + tok.t = :flo; + tok.u.flo = accf; + return :Some tok; + } else { + tok.t = :int; + tok.u.uint = acc; + return :Some tok; + } + + return :None; +} + fn lex(P *Parser) Tok { let c int #?; let tok Tok = {}; @@ -129,7 +196,22 @@ fn lex(P *Parser) Tok { tok.loc = (P.tokloc = P.curloc); if isdigit(c = chrpeek(P)) { let s [80]u8 = {}; + if readtilsep(P, s[0::], #t) < 0 { + // fatal + } + switch readnumber(s) { + case None; + fatal(P, tok.loc, "bad number literal %qs", s); + case Some tok; + tok.loc = P.tokloc; + return tok; + } + } + if c == EOF { + tok.t = :eof; + return tok; } + fatal(P, tok.loc, "stray %qc in program", c); } extern fn parse(P *Parser) [#]Decl { @@ -137,6 +219,7 @@ extern fn parse(P *Parser) [#]Decl { } extern fn parser_init(P *Parser, path *const u8) void { + assert(NUM_KEYWORDS - 1 < '!', "2manykw"); *P = {}; P.curfile = path; if (P.fp = fopen(path, "r")) == #null { diff --git a/src/util.cff b/src/util.cff index 33bce84..c3a08cb 100644 --- a/src/util.cff +++ b/src/util.cff @@ -47,3 +47,21 @@ extern fn addfilepath(s *const u8) int { filepaths[i] = s; return i; } + +fn fileid2path(id int) *const u8 { + assert(id >= 0 and id < filepaths.#len, "fileid"); + let s = filepaths[id]; + assert(s != #null, "fileid"); + return s; +} + +extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void { + let ap va_list #?; + ap->start(fmt); + + efmt("%s:%i:%i: error: ", fileid2path(loc.fileid), loc.line, loc.col); + vefmt(fmt, ap); + efmt("\n"); + ap->end(); + exit(1); +} diff --git a/vgcore.175407 b/vgcore.175407 Binary files differnew file mode 100644 index 0000000..a8392d9 --- /dev/null +++ b/vgcore.175407 diff --git a/vgcore.175479 b/vgcore.175479 Binary files differnew file mode 100644 index 0000000..45a85d8 --- /dev/null +++ b/vgcore.175479 diff --git a/vgcore.175493 b/vgcore.175493 Binary files differnew file mode 100644 index 0000000..99dd454 --- /dev/null +++ b/vgcore.175493 |