aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/all.hff23
-rw-r--r--src/fmt.cff94
-rw-r--r--src/libc.hff14
-rw-r--r--src/parse.cff83
-rw-r--r--src/util.cff18
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);
+}