ref: 8fef0e65ac05920b30d13ef4816dd166c3c83c47
dir: /libstd/fmt.myr/
use sys use "alloc.use" use "chartype.use" use "die.use" use "extremum.use" use "fltfmt.use" use "introspect.use" use "syswrap-ss.use" use "syswrap.use" use "types.use" use "utf.use" use "varargs.use" /* printf-like functions. These use a different syntax from the C printf, as described below: %s - A string, ie, a utf8 encoded byte slice. %t - A boolean %b - A byte. %w - A 16 bit integer %i - A 32 bit integer %l - A 64 bit integer %z - A size %p - A pointer %c - A char */ pkg std = const put : (fmt : byte[:], args : ... -> size) const fput : (fd : fd, fmt : byte[:], args : ... -> size) const putv : (fmt : byte[:], ap : valist -> size) const fputv : (fd : fd, fmt : byte[:], ap : valist -> size) const fmt : (fmt : byte[:], args : ... -> byte[:]) const fmtv : (fmt : byte[:], ap : valist -> byte[:]) const bfmt : (buf : byte[:], fmt : byte[:], args : ... -> byte[:]) const bfmtv : (buf : byte[:], fmt : byte[:], ap : valist -> byte[:]) $noret const fatal : (fmt : byte[:], args : ... -> void) $noret const fatalv : (fmt : byte[:], ap : valist -> void) ;; /* Writes a string of text up to 2 kb in size to stdout */ const put = {fmt, args -> fputv(1, fmt, vastart(&args)) } const fput = {fd, fmt, args -> fputv(fd, fmt, vastart(&args)) } const putv = {fmt, ap -> fputv(1, fmt, ap) } /* Writes a string of text up to 2kb long to stdout, using a valist as the source of the arguments */ const fputv = {fd, fmt, ap var buf : byte[2048] -> write(fd, bfmtv(buf[:], fmt, ap)) } /* same as 'put', but exits the program after printing */ const fatal = {fmt, args putv(fmt, vastart(&args)) exit(1) } /* same as 'putv', but exits the program after printing */ const fatalv = {fmt, ap putv(fmt, ap) exit(1) } /* formats a string, allocating the slice. FIXME: calculate the size needed. */ const fmt = {fmt, args -> fmtv(fmt, vastart(&args)) } /* formats a string, allocating the slice. FIXME: calculate the size needed. Takes a valist as it's last argument. */ const fmtv = {fmt, ap var buf buf = slalloc(2048) -> bfmtv(buf, fmt, ap) } /* formats a string of text as specified by 'fmt' into 'buf' */ const bfmt = {buf, fmt, args -> bfmtv(buf, fmt, vastart(&args)) } const digitchars = [ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' ] generic intfmt = {buf : byte[:], bits : @a::(integral,numeric), base, signed, padto, padfill var isneg var val var b : char[32] var i, j, n, npad n = 0 i = 0 if signed && bits < 0 val = -bits castto(uint64) isneg = true else val = bits castto(uint64) val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a))) isneg = false ;; if val == 0 b[0] = '0' i++ ;; while val != 0 b[i] = digitchars[val % base] val /= base i++ ;; npad = clamp(padto - i, 0, padto) n = 0 if isneg npad-- ;; if padfill == '0' && isneg && n < buf.len n += encode(buf[n:], '-') ;; for j = 0; j < min(npad, buf.len); j++ if n >= buf.len break ;; n += encode(buf[n:], padfill) ;; if padfill != '0' && isneg && n < buf.len n += encode(buf[n:], '-') ;; for j = i; j != 0; j-- if n >= buf.len break ;; n += encode(buf[n:], b[j - 1]) ;; -> n } /* formats a string of text as specified by 'fmt' into 'buf', using a valist for the arguments */ const bfmtv = {buf, fmt, ap var b var c var n var padto var base var signed, padfill var s_val : byte[:] var t_val : bool var b_val : int8, ub_val : uint8 var w_val : int16, uw_val : uint16 var i_val : int32, ui_val : uint32 var l_val : int64, ul_val : uint64 var z_val : size var p_val : byte# var c_val : char var f_val : flt64, F_val : flt32 n = 0 while fmt.len != 0 (c, fmt) = striter(fmt) if c == '%' base = 10 padto = 0 signed = true padfill = ' ' (c, fmt) = striter(fmt) /* modifiers */ while fmt.len > 0 match c | 'x': (c, fmt) = striter(fmt) base = 16 signed = false | 'u': (c, fmt) = striter(fmt) signed = false | '0': (c, fmt) = striter(fmt) padfill = '0' | _: /* nothing */ ;; if isdigit(c) && padto == 0 /* We can't get a 0 on the first iteration, since that was matched above. So, no special checks for nonzero on the first iteration. */ padto = 0 while isdigit(c) padto = padto*10 + charval(c, 10) (c, fmt) = striter(fmt) ;; else break ;; ;; /* format specifiers */ match c | 's': (s_val, ap) = vanext(ap) n += strfmt(buf[n:], s_val, padto, padfill) | 't': (t_val, ap) = vanext(ap) n += boolfmt(buf[n:], t_val, padto, padfill) | 'f': (f_val, ap) = vanext(ap) b = buf[n:] /* FIXME(ori): bug, b[n:].len fails since b[n:] isn't an lval */ n += flt64bfmt(buf[n:], f_val, 0, b.len) | 'F': (F_val, ap) = vanext(ap) n += flt64bfmt(buf[n:], F_val castto(flt64), 0, b.len) /* format integers */ | 'b': if signed (b_val, ap) = vanext(ap) n += intfmt(buf[n:], b_val, base, signed, padto, padfill) else (ub_val, ap) = vanext(ap) n += intfmt(buf[n:], ub_val, base, signed, padto, padfill) ;; | 'w': if signed (w_val, ap) = vanext(ap) n += intfmt(buf[n:], w_val, base, signed, padto, padfill) else (uw_val, ap) = vanext(ap) n += intfmt(buf[n:], uw_val, base, signed, padto, padfill) ;; | 'i': if signed (i_val, ap) = vanext(ap) n += intfmt(buf[n:], i_val, base, signed, padto, padfill) else (ui_val, ap) = vanext(ap) n += intfmt(buf[n:], ui_val, base, signed, padto, padfill) ;; | 'l': if signed (l_val, ap) = vanext(ap) n += intfmt(buf[n:], l_val, base, signed, padto, padfill) else (ul_val, ap) = vanext(ap) n += intfmt(buf[n:], ul_val, base, signed, padto, padfill) ;; | 'z': (z_val, ap) = vanext(ap) n += intfmt(buf[n:], z_val castto(int64), base, signed, padto, padfill) | 'p': (p_val, ap) = vanext(ap) n += intfmt(buf[n:], p_val castto(int64), 16, false, padto, padfill) | 'c': (c_val, ap) = vanext(ap) n += encode(buf[n:], c_val) | '%': n += encode(buf[n:], '%') | _: die("Unknown format specifier\n") ;; else n += encode(buf[n:], c) ;; ;; -> buf[:n] } const strfmt = {buf, str, padto, padfill var i, n, npad n = 0 npad = clamp(padto - str.len, 0, padto) for i = 0; i < padto - str.len; i++ n += encode(buf[n:], padfill) ;; for i = 0; i < min(str.len, buf.len); i++ buf[n++] = str[i] ;; -> n } const boolfmt = {buf, val, padto, padfill var s if val s = "true" else s = "false" ;; -> strfmt(buf, s, padto, padfill) }