shithub: mc

ref: 6b1a6216b40b804dd9c91c2bd54bee954776acaa
dir: /libstd/fmt.myr/

View raw version
use sys

use "alloc.use"
use "die.use"
use "types.use"
use "utf.use"
use "syswrap.use"
use "syswrap-ss.use"
use "varargs.use"
use "extremum.use"
use "chartype.use"
use "fltfmt.use"

/*
  printf-like functions. These use a different syntax from the C printf,
  as described below:

	  %s	- A string, ie, a utf8 encoded byte slice.
	  %t	- A boolean
	  %b	- A byte.
	  %w	- A 16 bit integer
	  %i	- A 32 bit integer
	  %l	- A 64 bit integer
	  %z	- A size
	  %p	- A pointer
	  %c	- A char
*/

pkg std =
	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)

	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[:])

	$noret const fatal	: (fmt : byte[:], args : ... -> void)
	$noret const fatalv	: (fmt : byte[:], ap : valist -> void)
;;

/* Writes a string of text up to 2 kb in size to stdout */
const put = {fmt, args
	-> fputv(1, fmt, vastart(&args))
}

const fput = {fd, fmt, args
	-> fputv(fd, fmt, vastart(&args))
}

const putv = {fmt, ap
	-> fputv(1, fmt, ap)
}

/* Writes a string of text up to 2kb long to stdout, using a valist
   as the source of the arguments */
const fputv = {fd, fmt, ap
	var buf : byte[2048]
	
	-> write(fd, bfmtv(buf[:], fmt, ap))
}

/* same as 'put', but exits the program after printing */
const fatal = {fmt, args
	putv(fmt, vastart(&args))
	exit(1)
}

/* same as 'putv', but exits the program after printing */
const fatalv = {fmt, ap
	putv(fmt, ap)
	exit(1)
}

/* formats a string, allocating the slice. FIXME: calculate the
   size needed. */
const fmt = {fmt, args
	-> fmtv(fmt, vastart(&args))
}

/* formats a string, allocating the slice. FIXME: calculate the
   size needed. Takes a valist as it's last argument. */
const fmtv = {fmt, ap
	var buf

	buf = slalloc(2048)
	-> bfmtv(buf, fmt, ap)
}

/* formats a string of text as specified by 'fmt' into 'buf' */
const bfmt = {buf, fmt, args
	-> bfmtv(buf, fmt, vastart(&args))
}

const digitchars = [
	'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
]
generic intfmt = {buf : byte[:], bits : @a::(integral,numeric), base, signed, padto, padfill
	var isneg
	var val
	var b : char[32]
	var i, j, n, npad

	n = 0
	i = 0
	if signed && bits < 0
		val = -bits castto(uint64)
		isneg = true
	else
		val = bits castto(uint64)
		val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a)))
		isneg = false
	;;

	if val == 0
		b[0] = '0'
		i++
	;;
	while val != 0
		b[i] = digitchars[val % base]
		val /= base
		i++
	;;

	npad = clamp(padto - i, 0, padto)
	n = 0
	if isneg
		npad--
	;;
	if padfill == '0' && isneg && n < buf.len
		n += encode(buf[n:], '-')
	;;
	for j = 0; j < min(npad, buf.len); j++
		if n >= buf.len
			break
		;;
		n += encode(buf[n:], padfill)
	;;
	if padfill != '0' && isneg && n < buf.len
		n += encode(buf[n:], '-')
	;;
	for j = i; j != 0; j--
		if n >= buf.len
			break
		;;
		n += encode(buf[n:], b[j - 1])
	;;
	-> n 
}

/* formats a string of text as specified by 'fmt' into 'buf',
   using a valist for the arguments */
const bfmtv = {buf, fmt, ap
	var b
	var c
	var n
	var padto
	var base
	var signed, padfill
	var s_val : byte[:]
	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 f_val : flt64, F_val : flt32

	n = 0
	while fmt.len != 0
		(c, fmt) = striter(fmt)
		if c == '%'
			base = 10
			padto = 0
			signed = true
			padfill = ' '
			(c, fmt) = striter(fmt)
			/* modifiers */
			while fmt.len > 0
				match c
				| 'x':
					(c, fmt) = striter(fmt)
					base = 16
					signed = false
						
				| 'u':
					(c, fmt) = striter(fmt)
					signed = false
				| '0':
					(c, fmt) = striter(fmt)
					padfill = '0'
				| _:	/* nothing */
				;;
				if isdigit(c) && padto == 0
					/*
					We can't get a 0 on the first iteration, since
					that was matched above. So, no special checks
					for nonzero on the first iteration.
					*/
					padto = 0
					while isdigit(c)
						padto = padto*10 + charval(c, 10)
						(c, fmt) = striter(fmt)
					;;
				else
					break
				;;
			;;
			/* format specifiers */
			match c
			| 's':
				(s_val, ap) = vanext(ap)
				n += strfmt(buf[n:], s_val, padto, padfill)
			| 't':
				(t_val, ap) = vanext(ap)
				n += boolfmt(buf[n:], t_val, padto, padfill)
			| 'f':
				(f_val, ap) = vanext(ap)
				b = buf[n:]
				/* FIXME(ori): bug, b[n:].len fails since b[n:] isn't an lval */
				n += flt64bfmt(buf[n:], f_val, 0, b.len)
			| 'F':
				(F_val, ap) = vanext(ap)
				n += flt64bfmt(buf[n:], F_val castto(flt64), 0, b.len)
			/* format integers */
			| 'b':
				if signed
					(b_val, ap) = vanext(ap)
					n += intfmt(buf[n:], b_val, base, signed, padto, padfill)
				else
					(ub_val, ap) = vanext(ap)
					n += intfmt(buf[n:], ub_val, base, signed, padto, padfill)
				;;
			| 'w':
				if signed
					(w_val, ap) = vanext(ap)
					n += intfmt(buf[n:], w_val, base, signed, padto, padfill)
				else
					(uw_val, ap) = vanext(ap)
					n += intfmt(buf[n:], uw_val, base, signed, padto, padfill)
				;;
			| 'i':
				if signed
					(i_val, ap) = vanext(ap)
					n += intfmt(buf[n:], i_val, base, signed, padto, padfill)
				else
					(ui_val, ap) = vanext(ap)
					n += intfmt(buf[n:], ui_val, base, signed, padto, padfill)
				;;
			| 'l':
				if signed
					(l_val, ap) = vanext(ap)
					n += intfmt(buf[n:], l_val, base, signed, padto, padfill)
				else
					(ul_val, ap) = vanext(ap)
					n += intfmt(buf[n:], ul_val, base, signed, padto, padfill)
				;;

			| 'z':
				(z_val, ap) = vanext(ap)
				n += intfmt(buf[n:], z_val castto(int64), base, signed, padto, padfill)
			| 'p':
				(p_val, ap) = vanext(ap)
				n += intfmt(buf[n:], p_val castto(int64), 16, false, padto, padfill)
			| 'c':
				(c_val, ap) = vanext(ap)
				n += encode(buf[n:], c_val)
			| '%':
				n += encode(buf[n:], '%')
                        | _:
                                die("Unknown format specifier\n")
			;;
		else
			n += encode(buf[n:], c)
		;;
	;;
	-> buf[:n]
}

const strfmt = {buf, str, padto, padfill
	var i, n, npad

	n = 0
	npad = clamp(padto - str.len, 0, padto)
	for i = 0; i < padto - str.len; i++
		n += encode(buf[n:], padfill)
	;;
	for i = 0; i < min(str.len, buf.len); i++
		buf[n++] = str[i]
	;;
	-> n
}

const boolfmt = {buf, val, padto, padfill
	var s

	if val
		s = "true"
	else
		s = "false"
	;;
	-> strfmt(buf, s, padto, padfill)
}