hare

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

print.ha (3668B)


      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...) (io::error | size) = {
     14 	let mod = mods { ... };
     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 ) (io::error | size) = {
     31 	let n = 0z, i = 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, &mods { ... })?;
     37 	case let f: (formattable, mods) =>
     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: *mods,
     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: *mods,
     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 = match (i) {
    114 	case types::unsigned =>
    115 		yield false;
    116 	case let s: types::signed =>
    117 		i = math::absi(s);
    118 		yield math::signi(s) < 0;
    119 	};
    120 	let sign = if (neg) "-" else {
    121 		yield switch (mod.neg) {
    122 		case neg::PLUS => yield "+";
    123 		case neg::SPACE => yield " ";
    124 		case neg::NONE => yield "";
    125 		};
    126 	};
    127 
    128 	let i = strconv::integertos(i, mod.base);
    129 	let pad = if (mod.prec < len(sign) + len(i)) {
    130 		yield 0z;
    131 	} else {
    132 		yield mod.prec - len(sign) - len(i);
    133 	};
    134 
    135 	let z = io::write(out, strings::toutf8(sign))?;
    136 	for (let i = 0z; i < pad) {
    137 		i += io::write(out, ['0'])?;
    138 	};
    139 	z += pad;
    140 	return z + io::write(out, strings::toutf8(i))?;
    141 };