import "cffc.hff"; import "common.hff"; import "map.hff"; import "util.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 $s *const u8 = (s); *$s != 0; ++$s { p(*$s); } ] defmacro pch(ch) [ { extern fn isprint(int) int; let $ch u8 = ch; switch { case $ch == '\''; ps("\\'"); case $ch == '\"'; ps("\\\""); case $ch == '\\'; ps("\\\\"); case isprint($ch) != 0; p($ch); case $ch == '\n'; ps("\\n"); case else; ps("\\x"); p("0123456789ABCDEF"[($ch >> 4) & 15]); p("0123456789ABCDEF"[$ch & 15]); } } ] static buf [100]u8 = {}; fn pritok(proc typeof(proc), parg *void, quote bool, tok *const Tok) void { if tok == #null { ps("(???)"); return; } switch tok.t { case :int; sprintf(buf, "%lld", tok.u.int); ps(buf); case :flo; sprintf(buf, "%.14f", tok.u.flo); ps(buf); case :bool; ps(tok.u.bool ? "#t" : "#f"); case :str; pfmt(proc, parg, "%S", &tok.u.str); case :chr; let t = bswap64(tok.u.uint); p('\''); while t != 0 { if t & 0xFF != 0 { pch(t & 0xFF); } t >>= 8; } p('\''); case :null; ps("#null"); case :ident, :macident, :label; if quote { p('`'); } ps(tok.u.ident); if quote { p('\''); } case :gensym; if quote { p('`'); } p('$'); ps(tok.u.ident); if quote { p('\''); } case :typearg; pfmt(proc, parg, "type parameter `%s' (%t)", tok.u.ident, tok.ty); case :eof; ps(""); case else if tok.t >= 0 and tok.t < NUM_KEYWORDS { if quote { p('`'); } ps(tok.u.ident); if quote { p('\''); } } else if tok.t > 0 { if quote { p('`'); } let t = bswap32(tok.t); while t != 0 { if t & 0xFF != 0 { p(t); } t >>= 8; } if quote { p('\''); } } } } fn pritokt(proc typeof(proc), parg *void, t TokT) void { switch t { case :int; ps("integer literal"); case :flo; ps("float literal"); case :bool; ps("boolean literal"); case :str; ps("string literal"); case :chr; ps("character literal"); case :null; ps("#null"); case :ident, :macident; ps("identifier"); case :label; ps("label"); case :gensym; ps("gensym"); case :typearg; ps("type parameter"); case :eof; ps(""); case else if t >= 0 and t < NUM_KEYWORDS { p('`'); ps(keyword2str[t]); p('\''); } else if t > 0 { p('`'); let t = bswap32(t); while t != 0 { if t & 0xFF != 0 { p(t); } t >>= 8; } p('\''); } } } fn pritype(proc typeof(proc), parg *void, ty *const Type) void { assert(ty != #null, "pritype"); if ty.konst { ps("const "); } switch ty.u { case Void; ps("void"); case Bool; ps(ty.size == 1 ? "bool" : "intbool"); case Int i; p(i.sgn ? 'i' : 'u'); sprintf(buf, "%zu", ty.size * 8); ps(buf); case Flo; p('f'); sprintf(buf, "%zu", ty.size * 8); ps(buf); case Ptr child; pfmt(proc, parg, "*%t", child); case Arr arr; if arr.length < 0 { pfmt(proc, parg, "[]%t", arr.child); } else { pfmt(proc, parg, "[%I]%t", arr.length, arr.child); } case Slice child; pfmt(proc, parg, "[#]%t", child); case Fn f; ps("fn ("); foreach(ty, i, f.params) { pfmt(proc, parg, "%t", ty); if f.variadic or i < f.params.#len - 1 { ps(", "); } } if f.variadic { ps("..."); } pfmt(proc, parg, ") %t", f.ret); case Agg agg; if agg.name { ps(agg.name); } else { ps("(anonymous)"); } if agg.tpargs.#ptr { p('<'); for let i = 0z; i < agg.tpargs.#len; ++i { switch agg.tpargs[i] { case Ty ty; pfmt(proc, parg, "%t", ty); case Val tok; pfmt(proc, parg, "%T", &tok); } if i < agg.tpargs.#len - 1 { ps(", "); } } ps("\e[1m>"); } case Enum e; if e.name { ps(e.name); } else { ps("(anonymous)"); } case BitF b; if b.name { ps(b.name); } else { ps("(anonymous)"); } case VaList; ps("va_list") } } 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; for ;; { switch (c = *++fmt) { case '%'; fprintf(stderr, "%%"); case 'q'; quote = #t; continue; case 'i', 'd'; sprintf(buf, "%d", ap->arg(int)); ps(buf); case 'I', 'D'; sprintf(buf, "%lld", as(c_llong)ap->arg(i64)); ps(buf); case 'z'; sprintf(buf, "%zu", ap->arg(usize)); ps(buf); case 'f'; sprintf(buf, "%.17f", ap->arg(f64)); ps(buf); case 'p'; sprintf(buf, "%p", ap->arg(*void)); ps(buf); case 'c'; let ch u32 = ap->arg(int); if quote { p('\''); for ch = bswap32(ch); ch != 0; ch >>= 8 { if ch & 0xFF != 0 { pch(ch); } } 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 s == #null { ps("(null)"); } else if quote { extern fn isprint(int) int; p('\"'); for let c u8 #?; (c = *s++) != 0; { pch(c); } p('\"'); } else { ps(s); } case 'S'; let str = *ap->arg(*[#]const u8); p('"'); foreach(c, i, str) { pch(c); } p('"'); case 'T'; let tok = ap->arg(*Tok); pritok(proc, parg, quote, tok); case 'k'; let tokt = ap->arg(TokT); pritokt(proc, parg, tokt); case 't'; ps("\e[1m"); pritype(proc, parg, ap->arg(*const Type)); ps("\e[0m"); case 'l'; let loc = ap->arg(*Loc); pfmt(proc, parg, "\e[1m%s:%d:%d\e[0m", fileid2path(loc.fileid), loc.line, loc.col); case else assert(#f, "bad fmt '%c'", c); } break; } } } extern fn pfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); vpfmt(proc, parg, fmt, ap); ap->end(); } extern fn vefmt(fmt *const u8, ap va_list) void { fn eputc(c u8, *void) void { fputc(c, stderr); } vpfmt(&eputc, #null, fmt, ap); } extern fn efmt(fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); vefmt(fmt, ap); ap->end(); } extern fn ssfmt(fmt *const u8, ...) *const u8 { static buf [1024]u8 = {}; let ap va_list #?; ap->start(fmt); fn sputc(c u8, arg *void) void { let pidx *uint = arg; if *pidx < buf.#len - 1 { buf[*pidx++] = c; } } let idx = 0u; vpfmt(&sputc, &idx, fmt, ap); ap->end(); } fn eprifileline(loc Loc) void { let path = fileid2path(loc.fileid); let fp = fopen(path, "rb"); if fp == #null { return; } let line_begin = 0; for let i = 0; i < loc.idx; ++i { if fgetc(fp) == '\n' { line_begin = i + 1; } } fseek(fp, line_begin, SEEK_SET); let ln = fprintf(stderr, "%4d | ", loc.line); for let c = fgetc(fp); c != EOF and c != '\n'; c = fgetc(fp) { fputc(c, stderr); } fputc('\n', stderr); fclose(fp); for let i = 0; i < ln + (loc.col - 1); ++i { fputc(' ', stderr); } fprintf(stderr, "^\n"); } extern fn vdiag(P *Parser, loc Loc, kind *const u8, fmt *const u8, ap va_list) void { efmt("\e[1m%s:%i:%i:\e[0m %s: ", fileid2path(loc.fileid), loc.line, loc.col, kind); vefmt(fmt, ap); efmt("\n"); eprifileline(loc); let i = 0; for let ep = P.curexpan; ep; ep = ep.prev { if ep.name != #null and (i++ < 8 or ep.prev == #null or ep.prev.prev == #null) { efmt("* while expanding %s `%s' at %l\n", ep.tepl ? "template" : "macro", ep.name, &ep.loc); eprifileline(ep.loc); } else if ep.name != #null and i == 8 { efmt(" ... (some expansions omitted)\n"); } } } extern fn warn(P *Parser, loc Loc, fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); vdiag(P, loc, "\e[33mwarning\e[0m", fmt, ap); ap->end(); } extern fn err(P *Parser, loc Loc, fmt *const u8, ...) void { P.error = #t; static nerr int = 0; if nerr++ == 7 { efmt("Aborting due to too many errors.\n"); exit(1); } let ap va_list #?; ap->start(fmt); vdiag(P, loc, "\e[31merror\e[0m", fmt, ap); ap->end(); } extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); vdiag(P, loc, "\e[31merror\e[0m", fmt, ap); ap->end(); efmt("Aborting due to previous error.\n"); exit(1); }