hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

print.ha (4114B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use encoding::utf8;
      5 use io;
      6 use math;
      7 use strconv;
      8 use strings;
      9 use types;
     10 
     11 // Formats values for printing using the default format modifiers and writes
     12 // them to an [[io::handle]] separated by spaces.
     13 export fn fprint(h: io::handle, args: formattable...) (size | io::error) = {
     14 	let mod = modset { ... };
     15 	let n = 0z;
     16 	for (let i = 0z; i < len(args); i += 1) {
     17 		n += format(h, args[i], &mod)?;
     18 		if (i != len(args) - 1) {
     19 			n += format(h, " ", &mod)?;
     20 		};
     21 	};
     22 	return n;
     23 };
     24 
     25 // Formats text for printing and writes it to an [[io::handle]].
     26 export fn fprintf(
     27 	h: io::handle,
     28 	fmt: str,
     29 	args: field...
     30 ) (size | io::error) = {
     31 	let n = 0z;
     32 	let it = iter(fmt, args);
     33 	for (true) match (next(&it)) {
     34 	case done => break;
     35 	case let s: str =>
     36 		n += format(h, s, &modset { ... })?;
     37 	case let f: (formattable, modset) =>
     38 		n += format(h, f.0, &f.1)?;
     39 	};
     40 
     41 	assert(!it.checkunused || it.idx == len(args), "Too many parameters given");
     42 	return n;
     43 };
     44 
     45 fn format(
     46 	out: io::handle,
     47 	arg: formattable,
     48 	mod: *modset,
     49 ) (size | io::error) = {
     50 	let start = 0z;
     51 	// guaranteed not to have starting padding in either of these cases
     52 	// saves the extra format_raw()
     53 	if (mod.width > 0 && mod.alignment != alignment::LEFT) {
     54 		let width = format_raw(io::empty, arg, mod)?;
     55 		let pad = if (width > mod.width) 0z else mod.width - width;
     56 
     57 		switch (mod.alignment) {
     58 		case alignment::LEFT => abort();
     59 		case alignment::CENTER => start = (pad + 1) / 2;
     60 		case alignment::RIGHT => start = pad;
     61 		};
     62 	};
     63 
     64 	let z = 0z;
     65 	for (z < start) {
     66 		z += io::write(out, utf8::encoderune(mod.pad)!)?;
     67 	};
     68 	z += format_raw(out, arg, mod)?;
     69 	for (z < mod.width) {
     70 		z += io::write(out, utf8::encoderune(mod.pad)!)?;
     71 	};
     72 
     73 	return z;
     74 };
     75 
     76 fn format_raw(
     77 	out: io::handle,
     78 	arg: formattable,
     79 	mod: *modset,
     80 ) (size | io::error) = match (arg) {
     81 case void =>
     82 	return io::write(out, strings::toutf8("void"));
     83 case let r: rune =>
     84 	return io::write(out, utf8::encoderune(r)!);
     85 case let s: str =>
     86 	if (mod.prec > 0 && mod.prec < len(s)) {
     87 		s = strings::sub(s, 0, mod.prec);
     88 	};
     89 	return io::write(out, strings::toutf8(s));
     90 case let b: bool =>
     91 	return io::write(out, strings::toutf8(if (b) "true" else "false"));
     92 case let p: uintptr =>
     93 	const s = strconv::uptrtos(p, mod.base);
     94 	return io::write(out, strings::toutf8(s));
     95 case let v: nullable *opaque =>
     96 	match (v) {
     97 	case let v: *opaque =>
     98 		let z = io::write(out, strings::toutf8("0x"))?;
     99 		const s = strconv::uptrtos(v: uintptr, strconv::base::HEX_LOWER);
    100 		z += io::write(out, strings::toutf8(s))?;
    101 		return z;
    102 	case null =>
    103 		return io::write(out, strings::toutf8("(null)"));
    104 	};
    105 case let f: types::floating =>
    106 	assert(mod.base == strconv::base::DEFAULT
    107 		|| mod.base == strconv::base::DEC); // TODO
    108 	assert(mod.prec <= types::UINT_MAX);
    109 	// TODO: mod.prec should be (size | void) but that needs @default
    110 	return strconv::fftosf(out, f, mod.ffmt,
    111 		if (mod.prec != 0) mod.prec: uint else void, mod.fflags)?;
    112 case let i: types::integer =>
    113 	let neg = false;
    114 	let i: u64 = match (i) {
    115 	case let i: i8 =>
    116 		neg = math::signi8(i) < 0;
    117 		yield math::absi8(i);
    118 	case let i: i16 =>
    119 		neg = math::signi16(i) < 0;
    120 		yield math::absi16(i);
    121 	case let i: i32 =>
    122 		neg = math::signi32(i) < 0;
    123 		yield math::absi32(i);
    124 	case let i: i64 =>
    125 		neg = math::signi64(i) < 0;
    126 		yield math::absi64(i);
    127 	case let i: int =>
    128 		neg = math::signi(i) < 0;
    129 		yield math::absi(i);
    130 	case let i: u8 => yield i;
    131 	case let i: u16 => yield i;
    132 	case let i: u32 => yield i;
    133 	case let i: u64 => yield i;
    134 	case let i: uint => yield i;
    135 	case let i: size => yield i;
    136 	};
    137 	let sign = if (neg) "-" else {
    138 		yield switch (mod.neg) {
    139 		case neg::PLUS => yield "+";
    140 		case neg::SPACE => yield " ";
    141 		case neg::NONE => yield "";
    142 		};
    143 	};
    144 
    145 	let i = strconv::u64tos(i, mod.base);
    146 	let pad = if (mod.prec < len(sign) + len(i)) {
    147 		yield 0z;
    148 	} else {
    149 		yield mod.prec - len(sign) - len(i);
    150 	};
    151 
    152 	let z = io::write(out, strings::toutf8(sign))?;
    153 	for (let i = 0z; i < pad) {
    154 		i += io::write(out, ['0'])?;
    155 	};
    156 	z += pad;
    157 	return z + io::write(out, strings::toutf8(i))?;
    158 };