mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Rewrite float parsing to use Rust native parsing
Eliminates the fast-float dependency.
This commit is contained in:
parent
838ff86ae7
commit
20e9c9493c
3 changed files with 74 additions and 12 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -78,11 +78,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast-float"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/fish-shell/fast-float-rust?branch=fish#9590c33a3f166a3533ba1cbb7a03e1105acec034"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "0.1.0"
|
||||
|
@ -90,7 +85,6 @@ dependencies = [
|
|||
"bitflags",
|
||||
"cc",
|
||||
"errno",
|
||||
"fast-float",
|
||||
"fish-printf",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
|
|
@ -21,7 +21,6 @@ default-run = "fish"
|
|||
|
||||
[dependencies]
|
||||
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" }
|
||||
|
||||
bitflags = "2.5.0"
|
||||
errno = "0.3.9"
|
||||
|
|
|
@ -1,7 +1,75 @@
|
|||
use super::errors::Error;
|
||||
use super::hex_float;
|
||||
use crate::wchar::IntoCharIter;
|
||||
use fast_float::parse_partial_iter;
|
||||
use std::num::ParseFloatError;
|
||||
|
||||
// Parse a decimal float from a sequence of characters.
|
||||
// Return the parsed float, and (on success) the number of characters consumed.
|
||||
fn parse_dec_float<I>(
|
||||
mut chars: I,
|
||||
decimal_sep: char,
|
||||
consumed: &mut usize,
|
||||
) -> Result<f64, ParseFloatError>
|
||||
where
|
||||
I: Iterator<Item = char> + Clone,
|
||||
{
|
||||
// This uses Rust's native float parsing and a temporary string.
|
||||
// The EBNF grammar is at https://doc.rust-lang.org/std/primitive.f64.html#method.from_str
|
||||
// Note it is case-insensitive and we replace the decimal separator with a period.
|
||||
let mut s = String::new();
|
||||
if matches!(chars.clone().next(), Some('+' | '-')) {
|
||||
s.push(chars.next().unwrap());
|
||||
}
|
||||
for spec in ["infinity", "inf", "nan"] {
|
||||
if chars
|
||||
.clone()
|
||||
.take(spec.len())
|
||||
.map(|c| c.to_ascii_lowercase())
|
||||
.eq(spec.chars())
|
||||
{
|
||||
s.push_str(spec);
|
||||
let res = s.parse::<f64>()?;
|
||||
*consumed = s.len();
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
while chars.clone().next().map_or(false, |c| c.is_ascii_digit()) {
|
||||
s.push(chars.next().unwrap());
|
||||
}
|
||||
if chars.clone().next() == Some(decimal_sep) {
|
||||
chars.next();
|
||||
s.push('.'); // Replace decimal separator with a period.
|
||||
while chars.clone().next().map_or(false, |c| c.is_ascii_digit()) {
|
||||
s.push(chars.next().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// Note that for a string like "5e", we should just parse the "5" out
|
||||
// and leave the "e" as remaining in the string. So we require at least
|
||||
// one digit after the decimal separator. Keep track of how many we have,
|
||||
// and the length before.
|
||||
let len_before_exp = s.len();
|
||||
if matches!(chars.clone().next(), Some('E' | 'e')) {
|
||||
s.push(chars.next().unwrap());
|
||||
if matches!(chars.clone().next(), Some('+' | '-')) {
|
||||
s.push(chars.next().unwrap());
|
||||
}
|
||||
let mut saw_exp_digit = false;
|
||||
while chars.clone().next().map_or(false, |c| c.is_ascii_digit()) {
|
||||
saw_exp_digit = true;
|
||||
s.push(chars.next().unwrap());
|
||||
}
|
||||
if !saw_exp_digit {
|
||||
// We didn't see any digits after the exponent.
|
||||
// Roll back to the length before the exponent.
|
||||
s.truncate(len_before_exp);
|
||||
}
|
||||
}
|
||||
let res = s.parse::<f64>()?;
|
||||
*consumed = s.len(); // note this is the number of chars because only ASCII is recognized.
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn wcstod_inner<I>(mut chars: I, decimal_sep: char, consumed: &mut usize) -> Result<f64, Error>
|
||||
where
|
||||
|
@ -33,14 +101,14 @@ where
|
|||
};
|
||||
}
|
||||
|
||||
let ret = parse_partial_iter(chars.clone().fuse(), decimal_sep);
|
||||
let ret = parse_dec_float(chars.clone(), decimal_sep, consumed);
|
||||
if ret.is_err() {
|
||||
*consumed = 0;
|
||||
return Err(Error::InvalidChar);
|
||||
}
|
||||
let (val, n): (f64, usize) = ret.unwrap();
|
||||
let val = ret.unwrap();
|
||||
|
||||
// Fast-float does not return overflow errors; instead it just returns +/- infinity.
|
||||
// Rust's float parsing does not return overflow errors; instead it just returns +/- infinity.
|
||||
// Check to see if the first character is a digit or the decimal; if so that indicates overflow.
|
||||
if val.is_infinite() {
|
||||
for c in chars {
|
||||
|
@ -53,7 +121,7 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
*consumed = n + whitespace_skipped;
|
||||
*consumed += whitespace_skipped;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
|
@ -112,6 +180,7 @@ pub fn wcstod_underscores<Chars>(s: Chars, consumed: &mut usize) -> Result<f64,
|
|||
where
|
||||
Chars: IntoCharIter,
|
||||
{
|
||||
// TODO: integrate underscore skipping into parse_dec_float?
|
||||
let mut chars = s.chars().peekable();
|
||||
|
||||
let mut leading_whitespace = 0;
|
||||
|
|
Loading…
Reference in a new issue