ref: 4932b5f0cf6f495208a0c1e1f4b2810ecf84fb9a
dir: /libstd/fmt.myr/
use "alloc.use"
use "chartype.use"
use "die.use"
use "extremum.use"
use "fltfmt.use"
use "htab.use"
use "hashfuncs.use"
use "introspect.use"
use "option.use"
use "strbuf.use"
use "syswrap-ss.use"
use "syswrap.use"
use "types.use"
use "utf.use"
use "varargs.use"
pkg std =
/* write to fd */
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)
/* write to buffer */
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[:])
/* write to strbuf */
const sbfmt : (buf : strbuf#, fmt : byte[:], args : ... -> size)
const sbfmtv : (buf : strbuf#, fmt : byte[:], ap : valist# -> size)
/* add a formatter function */
const fmtinstall : (ty : byte[:], \
fn : (sb : strbuf#, ap : valist#, opts : byte[:] -> void) \
-> void)
$noret const fatal : (fmt : byte[:], args : ... -> void)
$noret const fatalv : (fmt : byte[:], ap : valist# -> void)
;;
/* same as 'put', but exits the program after printing */
const fatal = {fmt, args
var ap
ap = vastart(&args)
putv(fmt, &ap)
exit(1)
}
/* same as 'putv', but exits the program after printing */
const fatalv = {fmt, ap
putv(fmt, ap)
exit(1)
}
var fmtmapinited : bool = false
var fmtmap : htab(byte[:], (sb : strbuf#, ap : valist#, opts : byte[:] -> void))#
const fmtinstall = {ty, fn
if !fmtmapinited
fmtmapinited = true
fmtmap = mkht(strhash, streq)
;;
htput(fmtmap, ty, fn)
}
const put = {fmt, args
var ap
ap = vastart(&args)
-> fputv(1, fmt, &ap)
}
const putv = {fmt, ap
-> fputv(1, fmt, ap)
}
const fput = {fd, fmt, args
var ap
ap = vastart(&args)
-> fputv(fd, fmt, &ap)
}
const fputv = {fd, fmt, ap
var sb, s
sb = mksb()
sbfmtv(sb, fmt, ap)
s = sbfin(sb)
-> writeall(fd, s)
}
const fmt = {fmt, args
var ap
ap = vastart(&args)
-> fmtv(fmt, &ap)
}
const fmtv = {fmt, ap
var sb
sb = mksb()
sbfmtv(sb, fmt, ap)
-> sbfin(sb)
}
const bfmt = {buf, fmt, args
var ap
ap = vastart(&args)
-> bfmtv(buf, fmt, &ap)
}
const bfmtv = {buf, fmt, ap
var sb
sb = mkbufsb(buf)
sbfmtv(sb, fmt, ap)
-> sbfin(sb)
}
const sbfmt = {sb, fmt, args
var ap
ap = vastart(&args)
-> sbfmtv(sb, fmt, &ap)
}
const sbfmtv = {sb, fmt, ap -> size
var c, params, ty
var nfmt, nparams
nparams = ap.tc.nelt
nfmt = 0
if !fmtmapinited
fmtmapinited = true
fmtmap = mkht(strhash, streq)
;;
while fmt.len != 0
(c, fmt) = striter(fmt)
match c
| '{':
if decode(fmt) == '{'
(c, fmt) = striter(fmt)
sbputc(sb, '{')
else
(params, fmt) = getparams(fmt)
nfmt++
if nfmt > nparams
die("too few params for fmt\n")
;;
ty = vatype(ap)
match htget(fmtmap, ty)
| `Some func:
func(sb, ap, params)
| `None:
fallbackfmt(sb, params, ty, ap)
;;
;;
| '}':
if decode(fmt) == '}'
sbputc(sb, '}')
;;
| chr:
sbputc(sb, chr)
;;
:fmtdone
;;
if nfmt != nparams
die("too many params for fmt\n")
;;
-> sb.len
}
const fallbackfmt = {sb, params, tyenc, ap : valist# -> void
/* value types */
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 s_val : byte[:]
var f32_val : flt32, f64_val : flt64
var i8 : int8, i16: int16, i32 : int32
var by : byte
var i : int, i64 : int64, l : long
var ui8 : int8, ui16: int16, ui32 : int32
var ui : int, ui64 : int64, ul : long
match typedesc(tyenc)
| `Tynone: /* nothing */
/* atomic types */
| `Tyvoid:
sbputs(sb, "void")
| `Tybool:
t_val = vanext(ap)
if t_val
sbputs(sb ,"true")
else
sbputs(sb, "false")
;;
| `Tychar:
c_val = vanext(ap)
sbputc(sb, c_val)
| `Tyint8:
b_val = vanext(ap)
intfmt(sb, intparams(params), true, b_val)
| `Tyint16:
w_val = vanext(ap)
intfmt(sb, intparams(params), true, w_val)
| `Tyint:
i_val = vanext(ap)
intfmt(sb, intparams(params), true, i_val)
| `Tyint32:
i_val = vanext(ap)
intfmt(sb, intparams(params), true, i_val)
| `Tyint64:
l_val = vanext(ap)
intfmt(sb, intparams(params), true, l_val)
| `Tylong:
l_val = vanext(ap)
intfmt(sb, intparams(params), true, l_val)
| `Tybyte:
ub_val = vanext(ap)
intfmt(sb, intparams(params), false, ub_val)
| `Tyuint8:
ub_val = vanext(ap)
intfmt(sb, intparams(params), false, ub_val)
| `Tyuint16:
w_val = vanext(ap)
intfmt(sb, intparams(params), false, uw_val)
| `Tyuint:
i_val = vanext(ap)
intfmt(sb, intparams(params), false, ui_val)
| `Tyuint32:
i_val = vanext(ap)
intfmt(sb, intparams(params), false, ui_val)
| `Tyuint64:
l_val = vanext(ap)
intfmt(sb, intparams(params), false, ul_val)
| `Tyulong:
l_val = vanext(ap)
intfmt(sb, intparams(params), false, ul_val)
| `Tyflt32:
f32_val = vanext(ap)
flt32bfmt(sb, f32_val, MNormal, 0)
| `Tyflt64:
f64_val = vanext(ap)
flt64bfmt(sb, f64_val, MNormal, 0)
| `Tyvalist:
sbputs(sb, "...")
/* compound types */
| `Typtr desc:
p_val = vanext(ap)
sbputs(sb, "0x")
intfmt(sb, \
[.base=16, .padto=2*sizeof(void#), .padfill='0'], \
false, p_val castto(intptr))
| `Tyslice desc:
match typedesc(desc)
| `Tybyte:
s_val = vanext(ap)
sbputs(sb, s_val)
| _:
sbputs(sb, "slice[:]")
;;
| `Tyfunc tc:
p_val = vanext(ap)
sbputs(sb, "func{")
intfmt(sb, \
[.base=16, .padto=2*sizeof(void#), .padfill='0'], \
false, p_val castto(intptr))
sbputs(sb, "}")
| `Tyarray (sz, data):
sbputs(sb, "array")
/* aggregate types */
| `Tytuple typecursor:
vabytes(ap)
sbputs(sb, "tuple")
| `Tystruct namecursor:
vabytes(ap)
sbputs(sb, "struct")
| `Tyunion namecursor:
vabytes(ap)
sbputs(sb, "union")
| `Tyname (name, desc):
fallbackfmt(sb, params, desc, ap)
;;
}
type intparams = struct
base : size
padto : size
padfill : char
;;
const getparams = {fmt
var i
for i = 0; i < fmt.len; i++
if fmt[i] == '}' castto(byte)
goto foundparams
;;
;;
die("invalid format string")
:foundparams
-> (fmt[:i], fmt[i+1:])
}
const intparams = {params
var ip : intparams
var c
ip = [
.base = 10,
.padfill = ' ',
.padto = 0
]
while params.len > 0
(c, params) = striter(params)
match c
| 'x': ip.base = 16
| '0': ip.padfill = '0'
| chr:
while isdigit(c)
ip.padto = ip.padto*10 + charval(c, 10)
(c, params) = striter(params)
;;
;;
;;
-> ip
}
const digitchars = [
'0','1','2','3','4',
'5','6','7','8','9',
'a','b','c','d','e','f'
]
generic intfmt = {sb, opts, signed, bits : @a::(integral,numeric)
var isneg
var val
var b : char[32]
var i, j, npad
var base
base = opts.base castto(uint64)
if signed && bits < 0
val = -bits castto(uint64)
isneg = true
else
val = bits castto(uint64)
val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a)))
isneg = false
;;
i = 0
if val == 0
b[0] = '0'
i++
;;
while val != 0
b[i] = digitchars[val % base]
val /= base
i++
;;
npad = clamp(opts.padto - i, 0, opts.padto)
if isneg
npad--
;;
if opts.padfill == '0' && isneg
sbputc(sb, '-')
;;
for j = 0; j < npad; j++
sbputc(sb, opts.padfill)
;;
if opts.padfill != '0' && isneg
sbputc(sb, '-')
;;
for j = i; j != 0; j--
sbputc(sb, b[j - 1])
;;
}
const writeall = {fd, buf
var n, len
len = 0
while true
n = write(fd, buf)
if n <= 0 || n >= len
break
;;
len += n
;;
-> len
}