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 $s *const u8 = (s); *$s != 0; ++$s { p(*$s); } ] defmacro pch(ch) [ { extern fn isprint(int) int; let $ch = ch; if isprint($ch) != 0 { p($ch); } else { p('\\'); p('0' + ($ch % 8)); p('0' + (($ch / 8) % 8)); p('0' + (($ch / 8 / 8) % 8)); } } ] static buf [100]u8 = {}; fn pritok(proc typeof(proc), parg *void, quote bool, tok *const Tok) void { 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, :gensym, :label; if quote { p('`'); } ps(tok.u.ident); if quote { p('\''); } case :type; pfmt(proc, parg, "%t", tok.ty); 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('\''); } } } } 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 'q'; quote = #t; continue; case 'i', 'd'; sprintf(buf, "%d", ap->arg(int)); 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 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 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(); }