ref: c71e69c0817c581bcbe0c321a8dec64433c553c7
dir: /libstd/fmt.myr/
use sys
use "alloc.use"
use "die.use"
use "types.use"
use "utf.use"
use "syswrap.use"
use "syswrap-ss.use"
use "varargs.use"
use "extremum.use"
use "chartype.use"
use "fltfmt.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)
}