ref: 58f59a52b75a9bd3d1be9ae6f4532f5949e45896
dir: /libstd/fmt.myr/
use "alloc.use" use "chartype.use" use "die.use" use "extremum.use" use "fltfmt.use" use "hashfuncs.use" use "hasprefix.use" use "htab.use" use "introspect.use" use "intparse.use" use "option.use" use "sleq.use" use "slpush.use" use "strbuf.use" use "strfind.use" use "strsplit.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[:],byte[:])[:] -> void), \ optdesc : (byte[:], bool)[:] \ -> void) $noret const fatal : (fmt : byte[:], args : ... -> void) $noret const fatalv : (fmt : byte[:], ap : valist# -> void) ;; type fmtdesc = struct fn : (sb : strbuf#, ap : valist#, opts : (byte[:],byte[:])[:] -> void) optdesc : (byte[:], bool)[:] ;; /* 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[:], fmtdesc)# const fmtinstall = {ty, fn, optdesc if !fmtmapinited fmtmapinited = true fmtmap = mkht(strhash, streq) ;; htput(fmtmap, ty, [.fn=fn, .optdesc=optdesc]) } 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 nfmt, nparams, pl var c, params, ty 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 f: pl = parseparams(params, f.optdesc) f.fn(sb, ap, pl) std.slfree(pl) | `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 parseparams = {paramstr, optdesc var params, opts var o, a, ha : bool, gotarg : bool opts = [][:] params = strsplit(paramstr, ",") for p in params /* parse out the key/value pair */ match std.strfind(p, "=") | `std.Some idx: o = p[:idx] a = p[idx+1:] gotarg = true | `std.None: o = p a = "" gotarg = false ;; /* verify and add the arg */ for (opt, hasarg) in optdesc if !std.sleq(opt, o) continue ;; ha = hasarg if ha == gotarg opts = std.slpush(opts, (o, a)) else std.fatal("invalid option {}", o) ;; ;; ;; slfree(params) -> opts } 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: uw_val = vanext(ap) intfmt(sb, intparams(params), false, uw_val) | `Tyuint: ui_val = vanext(ap) intfmt(sb, intparams(params), false, ui_val) | `Tyuint32: ui_val = vanext(ap) intfmt(sb, intparams(params), false, ui_val) | `Tyuint64: ul_val = vanext(ap) intfmt(sb, intparams(params), false, ul_val) | `Tyulong: ul_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 ip = [ .base = 10, .padfill = ' ', .padto = 0 ] var pl = parseparams(params, [ ("x", false), ("w", true), ("p", true)][:]) for p in pl match p | ("x", ""): ip.base = 16 | ("w", wid): /* would use get(), but that's a dep loop */ match std.intparse(wid) | `Some w: ip.padto = w; | `None: die("width was not number") ;; | ("p", pad): std.assert(pad.len == 1, "pad takes one character") ip.padfill = decode(pad) | _: ;; ;; std.slfree(pl) -> 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 }