Permalink
Cannot retrieve contributors at this time
use "alloc" | |
use "consts" | |
use "chartype" | |
use "die" | |
use "extremum" | |
use "fltfmt" | |
use "hashfuncs" | |
use "hasprefix" | |
use "htab" | |
use "intfmt" | |
use "intparse" | |
use "introspect" | |
use "memops" | |
use "option" | |
use "result" | |
use "sldup" | |
use "slpush" | |
use "sleq" | |
use "strbuf" | |
use "strfind" | |
use "striter" | |
use "strsplit" | |
use "syswrap" | |
use "syswrap-ss" | |
use "traits" | |
use "types" | |
use "utf" | |
use "varargs" | |
use "writeall" | |
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) \ | |
-> void) | |
$noret const fatal : (fmt : byte[:], args : ... -> void) | |
$noret const fatalv : (fmt : byte[:], ap : valist# -> void) | |
;; | |
type parsestate = union | |
`Copy | |
`ParamOpt | |
`ParamArg | |
;; | |
const __init__ = { | |
fmtmap = mkht() | |
} | |
type fmtdesc = struct | |
fn : (sb : strbuf#, ap : valist#, opts : (byte[:],byte[:])[:] -> void) | |
;; | |
/* same as 'put', but exits the program after printing */ | |
const fatal = {fmt, args | |
var ap | |
ap = vastart(&args) | |
fputv(Err, fmt, &ap) | |
exit(1) | |
} | |
/* same as 'putv', but exits the program after printing */ | |
const fatalv = {fmt, ap | |
fputv(Err, fmt, ap) | |
exit(1) | |
} | |
var fmtmap : htab(byte[:], fmtdesc)# | |
const fmtinstall = {ty, fn | |
match std.htget(fmtmap, ty) | |
| `std.Some _: std.fatal("doubly installed format\n") | |
| `std.None: htput(fmtmap, ty, [.fn=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, nw | |
sb = mksb() | |
sbfmtv(sb, fmt, ap) | |
s = sbfin(sb) | |
match writeall(fd, s) | |
| `std.Ok n: nw += n | |
| `std.Err (n, _): nw += n | |
;; | |
slfree(s) | |
-> nw | |
} | |
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 buf : byte[256], param : (byte[:], byte[:])[8] | |
var state, startp, endp, starta, nbuf | |
var nparam | |
var b, i | |
startp = 0 | |
starta = 0 | |
nparam = 0 | |
nbuf = 0 | |
endp = 0 | |
state = `Copy | |
i = 0 | |
while i != fmt.len | |
b = fmt[i++] | |
match (state, (b : char)) | |
/* raw bytes */ | |
| (`Copy, '{'): | |
if (fmt[i] : char) == '{' | |
b = fmt[i++] | |
sbputb(sb, ('{' : byte)) | |
else | |
state = `ParamOpt | |
nparam = 0 | |
startp = 0 | |
starta = 0 | |
nbuf = 0 | |
;; | |
| (`Copy, '}'): | |
if i == fmt.len || (fmt[i] : char) != '}' | |
die("unescaped '}'\n") | |
;; | |
sbputb(sb, ('}' : byte)) | |
i++ | |
| (`Copy, _): | |
sbputb(sb, b) | |
/* {param */ | |
| (`ParamOpt, '='): | |
state = `ParamArg | |
endp = nbuf | |
starta = nbuf | |
| (`ParamOpt, ','): | |
if startp != nbuf | |
param[nparam++] = (buf[startp:nbuf], "") | |
;; | |
startp = nbuf | |
| (`ParamOpt, '}'): | |
state = `Copy | |
if startp != nbuf | |
param[nparam++] = (buf[startp:nbuf], "") | |
;; | |
if ap.tc.nelt < 1 | |
die("too few values for fmt\n") | |
;; | |
fmtval(sb, vatype(ap), ap, param[:nparam]) | |
| (`ParamOpt, '\\'): | |
buf[nbuf++] = fmt[i++] | |
| (`ParamOpt, chr): | |
buf[nbuf++] = b | |
/* {param=arg} */ | |
| (`ParamArg, ','): | |
state = `ParamOpt | |
param[nparam++] = (buf[startp:endp], buf[starta:nbuf]) | |
startp = nbuf | |
endp = nbuf | |
| (`ParamArg, '}'): | |
state = `Copy | |
if startp != nbuf | |
param[nparam++] = (buf[startp:endp], buf[starta:nbuf]) | |
;; | |
if ap.tc.nelt < 1 | |
die("too few values for fmt\n") | |
;; | |
fmtval(sb, vatype(ap), ap, param[:nparam]) | |
| (`ParamArg, '\\'): | |
buf[nbuf++] = fmt[i++] | |
| (`ParamArg, chr): | |
buf[nbuf++] = b | |
;; | |
;; | |
if ap.tc.nelt != 0 | |
die("too many values for fmt\n") | |
;; | |
-> sb.len | |
} | |
const fmtval = {sb, ty, ap, params | |
match htget(fmtmap, ty) | |
| `Some f: | |
f.fn(sb, ap, params) | |
| `None: | |
fallbackfmt(sb, params, ty, ap) | |
;; | |
} | |
const fallbackfmt = {sb, params, tyenc, ap : valist# -> void | |
/* value types */ | |
var subap, subenc, subname | |
var inf, p | |
match typedesc(tyenc) | |
/* shows up in a union with no body */ | |
| `Tynone: | |
/* atomic types */ | |
| `Tyvoid: | |
var val : void = vanext(ap) | |
sbputs(sb, "void") | |
| `Tybool: | |
var val : bool = vanext(ap) | |
if val | |
sbputs(sb ,"true") | |
else | |
sbputs(sb, "false") | |
;; | |
| `Tychar: | |
var val : char = vanext(ap) | |
sbputc(sb, val) | |
| `Tyint8: | |
var val : int8 = vanext(ap) | |
intfmt(sb, intparams(ap, params), true, (val : uint64), 8) | |
| `Tyint16: | |
var val : int16 = vanext(ap) | |
intfmt(sb, intparams(ap, params), true, (val : uint64), 16) | |
| `Tyint: | |
var val : int = vanext(ap) | |
intfmt(sb, intparams(ap, params), true, (val : uint64), 32) | |
| `Tyint32: | |
var val : int32 = vanext(ap) | |
intfmt(sb, intparams(ap, params), true, (val : uint64), 32) | |
| `Tyint64: | |
var val : int64 = vanext(ap) | |
intfmt(sb, intparams(ap, params), true, (val : uint64), 64) | |
| `Tybyte: | |
var val : byte = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 8) | |
| `Tyuint8: | |
var val : uint8 = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 8) | |
| `Tyuint16: | |
var val : uint16 = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 16) | |
| `Tyuint: | |
var val : uint = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 32) | |
| `Tyuint32: | |
var val : uint32 = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 32) | |
| `Tyuint64: | |
var val : uint64 = vanext(ap) | |
intfmt(sb, intparams(ap, params), false, (val : uint64), 64) | |
| `Tyflt32: | |
var val : flt32 = vanext(ap) | |
flt32bfmt(sb, fltparams(ap, params), val) | |
| `Tyflt64: | |
var val : flt64 = vanext(ap) | |
flt64bfmt(sb, fltparams(ap, params), val) | |
| `Tyvalist: | |
sbputs(sb, "...") | |
/* compound types */ | |
| `Typtr desc: | |
var val : void# = vanext(ap) | |
sbputs(sb, "0x") | |
intfmt(sb, | |
[.base=16, .padto=2*sizeof(void#), .padfill='0'], | |
false, (val : uint64), 64) | |
| `Tyslice desc: | |
match typedesc(desc) | |
| `Tybyte: | |
var val : byte[:] = vanext(ap) | |
strfmt(sb, val, ap, params) | |
| _: | |
subap = vaenter(ap) | |
fmtslice(sb, subap, params) | |
vabytes(ap) | |
;; | |
| `Tyfunc tc: | |
var val : intptr[2] = vanext(ap) | |
sbputs(sb, "func{") | |
intfmt(sb, | |
[.base=16, .padto=2*sizeof(void#), .padfill='0'], | |
false, (val[0] : uint64), 0) | |
sbputs(sb, ", ") | |
intfmt(sb, | |
[.base=16, .padto=2*sizeof(void#), .padfill='0'], | |
false, (val[1] : uint64), 0) | |
sbputs(sb, "}") | |
| `Tyarray (sz, desc): | |
subap = vaenter(ap) | |
sbputs(sb, "[") | |
while subap.tc.nelt != 0 | |
fmtval(sb, vatype(&subap), &subap, [][:]) | |
if subap.tc.nelt > 0 | |
sbfmt(sb, ", ") | |
;; | |
;; | |
sbputs(sb, "]") | |
vabytes(ap) | |
/* aggregate types */ | |
| `Tytuple tc: | |
subap = vaenter(ap) | |
sbfmt(sb, "(") | |
var extracomma = subap.tc.nelt == 1 | |
while subap.tc.nelt != 0 | |
fmtval(sb, vatype(&subap), &subap, [][:]) | |
if subap.tc.nelt > 0 | |
sbfmt(sb, ", ") | |
;; | |
;; | |
if extracomma | |
sbfmt(sb, ",") | |
;; | |
sbfmt(sb, ")") | |
vabytes(ap) | |
| `Tystruct nc: | |
subap = vaenter(ap) | |
sbfmt(sb, "[") | |
while subap.tc.nelt != 0 | |
(subname, subenc) = ncpeek(&subap.tc) | |
sbfmt(sb, ".{}=", subname) | |
fmtval(sb, vatype(&subap), &subap, [][:]) | |
if subap.tc.nelt > 0 | |
sbfmt(sb, ", ") | |
;; | |
;; | |
sbfmt(sb, "]") | |
vabytes(ap) | |
| `Tyunion nc: | |
var tag : int32 | |
inf = typeinfo(tcpeek(&ap.tc)) | |
p = (ap.args : size) | |
p = (p + inf.align - 1) & ~(inf.align - 1) | |
tag = (p : int32#)# | |
subap = vaenterunion(ap, tag) | |
for var i = 0; i < tag; i++ | |
ncnext(&nc) | |
;; | |
(subname, subenc) = ncnext(&nc) | |
sbfmt(sb, "`{}", subname) | |
match typedesc(subenc) | |
| `Tynone: | |
| _: | |
sbputc(sb, ' ') | |
fmtval(sb, subenc, &subap, [][:]) | |
;; | |
vabytes(ap) | |
| `Tyname (name, desc): | |
subap = vaenter(ap) | |
fmtval(sb, desc, &subap, params) | |
vabytes(ap) | |
;; | |
} | |
const fmtslice = {sb, subap, params | |
var join, joined | |
join = ", " | |
joined = false | |
for p : params | |
match p | |
| ("j", j): | |
joined = true | |
join = j | |
| (opt, arg): | |
std.write(2, "fmt: \0") | |
std.write(2, opt) | |
std.write(2, "\0arg: ") | |
std.write(2, arg) | |
std.die("unreacahable") | |
;; | |
;; | |
if !joined | |
sbputs(sb, "[") | |
;; | |
while subap.tc.nelt != 0 | |
fmtval(sb, vatype(&subap), &subap, [][:]) | |
if subap.tc.nelt > 0 | |
sbputs(sb, join) | |
;; | |
;; | |
if !joined | |
sbputs(sb, "]") | |
;; | |
} | |
const fltparams = {ap, params | |
var fp : fltparams | |
fp = [ | |
.mode = MNormal, | |
.cutoff = 0, | |
.scientific = false, | |
.padfill = ' ', | |
.padto = 0, | |
] | |
for p : params | |
match p | |
| ("e", ""): fp.scientific = true | |
| ("w", wid): | |
if eq(wid, "?") | |
fp.padto = pullint(ap, "fmt: width = ? must be integer") | |
else | |
fp.padto = getint(wid, "fmt: width must be integer") | |
;; | |
| ("p", pad): fp.padfill = decode(pad) | |
| ("s", sig): | |
fp.mode = MRelative | |
fp.cutoff = getint(sig, "fmt: significant figures must be integer") | |
| (opt, arg): | |
std.write(2, "fmt: ") | |
std.write(2, opt) | |
std.write(2, "arg: ") | |
std.write(2, arg) | |
std.die("\nunreachable\n") | |
;; | |
;; | |
iassert(fp.padto >= 0, "pad must be >= 0") | |
-> fp | |
} | |
const intparams = {ap, params | |
var ip : intparams | |
ip = [ | |
.base = 10, | |
.padfill = ' ', | |
.padto = 0 | |
] | |
for p : params | |
match p | |
| ("b", bas): ip.base = getint(bas, "fmt: base must be integer") | |
| ("x", ""): ip.base = 16 | |
| ("w", wid): | |
if eq(wid, "?") | |
ip.padto = pullint(ap, "fmt: width = ? must be integer") | |
else | |
ip.padto = getint(wid, "fmt: width must be integer") | |
;; | |
| ("p", pad): ip.padfill = decode(pad) | |
| (opt, arg): | |
std.write(2, "fmt: ") | |
std.write(2, opt) | |
std.write(2, "arg: ") | |
std.write(2, arg) | |
std.die("\nunreachable\n") | |
;; | |
;; | |
iassert(ip.padto >= 0, "pad must be >= 0") | |
-> ip | |
} | |
const strfmt = {sb, str, ap, params | |
var w, p, i, raw, esc | |
p = ' ' | |
w = 0 | |
raw = false | |
esc = false | |
for pp : params | |
match pp | |
| ("w", wid): | |
if eq(wid, "?") | |
w = pullint(ap, "fmt: width = ? must be integer") | |
else | |
w = getint(wid, "fmt: width must be integer") | |
;; | |
| ("p", pad): p = decode(pad) | |
| ("r", ""): raw = true | |
| ("e", ""): esc = true | |
| (opt, arg): | |
std.write(2, "fmt: ") | |
std.write(2, opt) | |
std.write(2, "arg: ") | |
std.write(2, arg) | |
std.die("unreachable\n") | |
;; | |
;; | |
iassert(p >= 0, "pad must be >= 0") | |
if raw | |
for b : str | |
if esc | |
sbputs(sb, "\\x") | |
;; | |
intfmt(sb, [.padto=2, .padfill='0', .base=16], false, (b : uint64), 8) | |
;; | |
elif esc | |
for b : str | |
if isprint(b) | |
sbputb(sb, b) | |
else | |
match (b : char) | |
| '\n': sbputs(sb, "\\n") | |
| '\r': sbputs(sb, "\\r") | |
| '\t': sbputs(sb, "\\t") | |
| '\b': sbputs(sb, "\\b") | |
| '\"': sbputs(sb, "\\\"") | |
| '\'': sbputs(sb, "\\\'") | |
| '\\': sbputs(sb, "\\\\") | |
| '\0': sbputs(sb, "\\0") | |
| _: | |
sbputs(sb, "\\x") | |
intfmt(sb, [.padto=2, .padfill='0', .base=16], false, (b : uint64), 8) | |
;; | |
;; | |
;; | |
else | |
for i = 0; i < w - strcellwidth(str); i++ | |
sbputc(sb, p) | |
;; | |
sbputs(sb, str) | |
;; | |
} | |
const isprint = {b | |
-> b >= (' ' : byte) && b < ('~' : byte) | |
} | |
/* would use std.get(), but that's a dependency loop */ | |
const getint = {s, msg | |
match std.intparse(s) | |
| `Some w: -> w; | |
| `None: die(msg) | |
;; | |
} | |
const pullint = {ap, msg | |
if ap.tc.nelt < 1 | |
die("expected argument where none was provided") | |
;; | |
match typedesc(vatype(ap)) | |
| `Tyint8: | |
var val : int8 = vanext(ap) | |
-> val < 0 ? 0 : (val : size) | |
| `Tyint16: | |
var val : int16 = vanext(ap) | |
-> val < 0 ? 0 : (val : size) | |
| `Tyint: | |
var val : int = vanext(ap) | |
-> val < 0 ? 0 : (val : size) | |
| `Tyint32: | |
var val : int32 = vanext(ap) | |
-> val < 0 ? 0 : (val : size) | |
| `Tyint64: | |
var val : int64 = vanext(ap) | |
-> val < 0 ? 0 : (val : size) | |
| `Tyuint8: | |
var val : uint8 = vanext(ap) | |
-> (val : size) | |
| `Tyuint16: | |
var val : uint16 = vanext(ap) | |
-> (val : size) | |
| `Tyuint: | |
var val : uint = vanext(ap) | |
-> (val : size) | |
| `Tyuint32: | |
var val : uint32 = vanext(ap) | |
-> (val : size) | |
| `Tyuint64: | |
var val : uint64 = vanext(ap) | |
-> (val : size) | |
| `Tyname (_, desc): | |
/* This is primarily for handling std.size */ | |
var subap = vaenter(ap) | |
var ret = pullint(&subap, msg) | |
vabytes(ap) /* Pull one element out to keep ap synchronized with subap */ | |
-> ret | |
| _: die(msg) | |
;; | |
-> 0 | |
} |