Use hexponent to implement hex float parsing in wcstod

This teaches wcstod to parse hex floats like 0x1.5p3 via a forked
version of hexponent. This support is necessary for printf.
This commit is contained in:
ridiculousfish 2023-04-01 18:26:20 -07:00
parent 74104f76ad
commit 14c5c94d01
3 changed files with 53 additions and 0 deletions

6
fish-rust/Cargo.lock generated
View file

@ -374,6 +374,7 @@ dependencies = [
"cxx-gen",
"errno",
"fast-float",
"hexponent",
"inventory",
"libc",
"lru",
@ -462,6 +463,11 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hexponent"
version = "0.3.1"
source = "git+https://github.com/fish-shell/hexponent?branch=fish#d2b97417d34adc9ea3ec954c69accc59828cbdb4"
[[package]]
name = "humantime"
version = "2.1.0"

View file

@ -8,6 +8,7 @@ rust-version = "1.67"
widestring-suffix = { path = "./widestring-suffix/" }
pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", branch = "master", default-features = false, features = ["utf32"] }
fast-float = { git = "https://github.com/fish-shell/fast-float-rust", branch="fish" }
hexponent = { git = "https://github.com/fish-shell/hexponent", branch="fish" }
printf-compat = { git = "https://github.com/fish-shell/printf-compat.git", branch="fish" }
autocxx = "0.23.1"

View file

@ -39,6 +39,16 @@ where
}
}
// If it's a hex float, use hexponent.
if is_hex_float(chars.clone()) {
let mut n = 0;
let res = hexponent::parse_hex_float(chars, decimal_sep, &mut n);
if res.is_ok() {
*consumed = whitespace_skipped + n;
}
return res.map_err(hexponent_error);
}
let ret = parse_partial_iter(chars.clone().fuse(), decimal_sep);
if ret.is_err() {
*consumed = 0;
@ -63,6 +73,39 @@ where
Ok(val)
}
/// Check if a character iterator appears to be a hex float.
/// That is, an optional + or -, followed by 0x or 0X, and a hex digit.
pub fn is_hex_float<Chars: Iterator<Item = char>>(mut chars: Chars) -> bool {
match chars.next() {
Some('+' | '-') => {
if chars.next() != Some('0') {
return false;
}
}
Some('0') => (),
_ => return false,
};
match chars.next() {
Some('x') | Some('X') => (),
_ => return false,
};
match chars.next() {
Some(c) => c.is_ascii_hexdigit(),
None => false,
}
}
// Convert a a hexponent error to our error type.
fn hexponent_error(e: hexponent::ParseError) -> Error {
use hexponent::ParseErrorKind;
match e.kind {
ParseErrorKind::MissingPrefix
| ParseErrorKind::MissingDigits
| ParseErrorKind::MissingExponent => Error::InvalidChar,
ParseErrorKind::ExponentOverflow => Error::Overflow,
}
}
#[cfg(test)]
mod test {
#![allow(overflowing_literals)]
@ -101,6 +144,9 @@ mod test {
test_consumed("0.y", Ok(0.0), 2);
test_consumed(".0y", Ok(0.0), 2);
test_consumed("000,,,e1", Ok(0.0), 3);
test_consumed("0x1", Ok(1.0), 3);
test_consumed("0X1p2", Ok(4.0), 5);
test_consumed("0X1P3", Ok(8.0), 5);
test("000e1", Ok(0.0));
test_consumed("000,1e1", Ok(0.0), 3);
test("0", Ok(0.0));