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 };