diff options
Diffstat (limited to 'src')
| -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 |
5 files changed, 224 insertions, 8 deletions
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); +} |