shithub: mc

ref: 94acc736ec2d8d80533439a24742d7c796aaab4f
dir: /libstd/fmt.myr/

View raw version
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
}