ref: fd3494af3e5209e7e74662bc4066b28f58ff49cc
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 }