hare

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

commit 5a1eed1cb0227e1dba686ef16a97baf7ac566057
parent c96533b97d7287ce978702dc73d3962195e38932
Author: Ember Sawady <ecs@d2evs.net>
Date:   Mon,  6 Nov 2023 21:08:01 +0000

fmt: flesh out float formatting

Signed-off-by: Ember Sawady <ecs@d2evs.net>

Diffstat:
Mfmt/+test.ha | 10++++++++++
Mfmt/README | 11+++++++++++
Mfmt/iter.ha | 17+++++++++++++++++
Mfmt/print.ha | 4++--
4 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/fmt/+test.ha b/fmt/+test.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use math; use strconv; @test fn print() void = { @@ -66,6 +67,15 @@ use strconv; assert(bsprintf(buf, "{:.1}", 123.0) == "100"); assert(bsprintf(buf, "{:.5}", 123.0) == "123"); + assert(bsprintf(buf, "{:f}", 1.0e4) == "10000"); + assert(bsprintf(buf, "{:e}", 123.45) == "1.2345e2"); + assert(bsprintf(buf, "{:Fs}", 1.0) == "+1"); + assert(bsprintf(buf, "{:F.}", 1.0) == "1.0"); + assert(bsprintf(buf, "{:FU}", math::INF) == "INFINITY"); + assert(bsprintf(buf, "{:FE}", 1.0e4) == "1E4"); + assert(bsprintf(buf, "{:FS}", 1.0e4) == "1e+4"); + assert(bsprintf(buf, "{:F2}", 1.0e4) == "1e04"); + assert(bsprintf(buf, "{:=5}", "hi") == " hi "); assert(bsprintf(buf, "{:=6}", "hi") == " hi "); diff --git a/fmt/README b/fmt/README @@ -38,6 +38,17 @@ A format modifier can be any of the following: - "X": Format numbers in uppercase hexadecimal. - "o": Format numbers in octal. - "b": Format numbers in binary. +- "e": Format floats in scientific notation. +- "f": Format floats in fixed-point notation. +- "g": Format floats in whichever of scientific and fixed-point notation is + shortest. This is the default. +- "F" followed by "s": Use a sign for both positive and negative numbers. +- "F" followed by ".": Always include at least one digit after the decimal + point. +- "F" followed by "U": Uppercase INFINITY and NAN. +- "F" followed by "E": Uppercase exponent symbols (E and P rather than e and p). +- "F" followed by "S": Use a sign for both positive and negative exponents. +- "F" followed by "2": Show at least two digits of the exponent. - "." followed by a number N: Sets the precision to N. Integers will be left-padded with "0"s between the sign and the number itself. Strings will be truncated to N runes. Floats will only include up to N digits after diff --git a/fmt/iter.ha b/fmt/iter.ha @@ -37,6 +37,8 @@ export type mods = struct { width: size, prec: size, base: strconv::base, + ffmt: strconv::ffmt, + fflags: strconv::fflags, }; type iterator = struct { @@ -142,6 +144,21 @@ fn scan_modifiers(it: *iterator, mod: *mods) void = { case 'X' => mod.base = strconv::base::HEX_UPPER; case 'o' => mod.base = strconv::base::OCT; case 'b' => mod.base = strconv::base::BIN; + // ffmt + case 'e' => mod.ffmt = strconv::ffmt::E; + case 'f' => mod.ffmt = strconv::ffmt::F; + case 'g' => mod.ffmt = strconv::ffmt::G; + // fflags + case 'F' => + switch (getrune(it)) { + case 's' => mod.fflags |= strconv::fflags::SHOW_POS; + case '.' => mod.fflags |= strconv::fflags::SHOW_POINT; + case 'U' => mod.fflags |= strconv::fflags::UPPERCASE; + case 'E' => mod.fflags |= strconv::fflags::UPPER_EXP; + case 'S' => mod.fflags |= strconv::fflags::SHOW_POS_EXP; + case '2' => mod.fflags |= strconv::fflags::SHOW_TWO_EXP_DIGITS; + case => abort("Invalid float flag"); + }; // precision case '.' => mod.prec = scan_sz(it); // width diff --git a/fmt/print.ha b/fmt/print.ha @@ -106,8 +106,8 @@ case let f: types::floating => || mod.base == strconv::base::DEC); // TODO assert(mod.prec <= types::UINT_MAX); // TODO: mod.prec should be (size | void) but that needs @default - return strconv::fftosf(out, f, strconv::ffmt::G, - if (mod.prec != 0) mod.prec: uint else void, 0)?; + return strconv::fftosf(out, f, mod.ffmt, + if (mod.prec != 0) mod.prec: uint else void, mod.fflags)?; case let i: types::integer => let neg = match (i) { case types::unsigned =>