diff options
Diffstat (limited to 'src/fmt.cff')
| -rw-r--r-- | src/fmt.cff | 94 |
1 files changed, 94 insertions, 0 deletions
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(); +} |