Skip to content

Commit

Permalink
core: Shuffle around float parsing
Browse files Browse the repository at this point in the history
Stop using stability to hide the implementation details of ParseFloatError and
instead move the error type into the `dec2flt` module. Also move the
implementation blocks of `FromStr for f{32,64}` into `dec2flt` directly.
  • Loading branch information
alexcrichton committed Aug 18, 2015
1 parent 5990249 commit a2b932c
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 138 deletions.
100 changes: 87 additions & 13 deletions src/libcore/num/dec2flt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@
issue = "0")]

use prelude::v1::*;
use num::ParseFloatError as PFE;
use num::FloatErrorKind;
use fmt;
use str::FromStr;

use self::parse::{parse_decimal, Decimal, Sign};
use self::parse::ParseResult::{self, Valid, ShortcutToInf, ShortcutToZero};
use self::num::digits_to_big;
Expand All @@ -110,14 +111,87 @@ mod num;
pub mod rawfp;
pub mod parse;

/// Entry point for decimal-to-f32 conversion.
pub fn to_f32(s: &str) -> Result<f32, PFE> {
dec2flt(s)
macro_rules! from_str_float_impl {
($t:ty, $func:ident) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for $t {
type Err = ParseFloatError;

/// Converts a string in base 10 to a float.
/// Accepts an optional decimal exponent.
///
/// This function accepts strings such as
///
/// * '3.14'
/// * '-3.14'
/// * '2.5E10', or equivalently, '2.5e10'
/// * '2.5E-10'
/// * '.' (understood as 0)
/// * '5.'
/// * '.5', or, equivalently, '0.5'
/// * 'inf', '-inf', 'NaN'
///
/// Leading and trailing whitespace represent an error.
///
/// # Arguments
///
/// * src - A string
///
/// # Return value
///
/// `Err(ParseFloatError)` if the string did not represent a valid
/// number. Otherwise, `Ok(n)` where `n` is the floating-point
/// number represented by `src`.
#[inline]
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
dec2flt(src)
}
}
}
}
from_str_float_impl!(f32, to_f32);
from_str_float_impl!(f64, to_f64);

/// An error which can be returned when parsing a float.
#[derive(Debug, Clone, PartialEq)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct ParseFloatError {
kind: FloatErrorKind
}

#[derive(Debug, Clone, PartialEq)]
enum FloatErrorKind {
Empty,
Invalid,
}

impl ParseFloatError {
#[unstable(feature = "int_error_internals",
reason = "available through Error trait and this method should \
not be exposed publicly",
issue = "0")]
#[doc(hidden)]
pub fn __description(&self) -> &str {
match self.kind {
FloatErrorKind::Empty => "cannot parse float from empty string",
FloatErrorKind::Invalid => "invalid float literal",
}
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for ParseFloatError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.__description().fmt(f)
}
}

pub fn pfe_empty() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Empty }
}

/// Entry point for decimal-to-f64 conversion.
pub fn to_f64(s: &str) -> Result<f64, PFE> {
dec2flt(s)
pub fn pfe_invalid() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Invalid }
}

/// Split decimal string into sign and the rest, without inspecting or validating the rest.
Expand All @@ -131,9 +205,9 @@ fn extract_sign(s: &str) -> (Sign, &str) {
}

/// Convert a decimal string into a floating point number.
fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {
fn dec2flt<T: RawFloat>(s: &str) -> Result<T, ParseFloatError> {
if s.is_empty() {
return Err(PFE { __kind: FloatErrorKind::Empty });
return Err(pfe_empty())
}
let (sign, s) = extract_sign(s);
let flt = match parse_decimal(s) {
Expand All @@ -143,7 +217,7 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {
ParseResult::Invalid => match s {
"inf" => T::infinity(),
"NaN" => T::nan(),
_ => { return Err(PFE { __kind: FloatErrorKind::Invalid }); }
_ => { return Err(pfe_invalid()); }
}
};

Expand All @@ -155,7 +229,7 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {

/// The main workhorse for the decimal-to-float conversion: Orchestrate all the preprocessing
/// and figure out which algorithm should do the actual conversion.
fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, PFE> {
fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, ParseFloatError> {
simplify(&mut decimal);
if let Some(x) = trivial_cases(&decimal) {
return Ok(x);
Expand All @@ -172,7 +246,7 @@ fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, PFE> {
// If we exceed this, perhaps while calculating `f * 10^e` in Algorithm R or Algorithm M,
// we'll crash. So we error out before getting too close, with a generous safety margin.
if max_digits > 375 {
return Err(PFE { __kind: FloatErrorKind::Invalid });
return Err(pfe_invalid());
}
let f = digits_to_big(decimal.integral, decimal.fractional);

Expand Down
17 changes: 8 additions & 9 deletions src/libcore/num/float_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ macro_rules! from_str_radix_float_impl {
($T:ty) => {
fn from_str_radix(src: &str, radix: u32)
-> Result<$T, ParseFloatError> {
use num::FloatErrorKind::*;
use num::ParseFloatError as PFE;
use num::dec2flt::{pfe_empty, pfe_invalid};

// Special values
match src {
Expand All @@ -35,8 +34,8 @@ macro_rules! from_str_radix_float_impl {
}

let (is_positive, src) = match src.slice_shift_char() {
None => return Err(PFE { __kind: Empty }),
Some(('-', "")) => return Err(PFE { __kind: Empty }),
None => return Err(pfe_empty()),
Some(('-', "")) => return Err(pfe_empty()),
Some(('-', src)) => (false, src),
Some((_, _)) => (true, src),
};
Expand Down Expand Up @@ -88,7 +87,7 @@ macro_rules! from_str_radix_float_impl {
break; // start of fractional part
},
_ => {
return Err(PFE { __kind: Invalid });
return Err(pfe_invalid())
},
},
}
Expand Down Expand Up @@ -122,7 +121,7 @@ macro_rules! from_str_radix_float_impl {
break; // start of exponent
},
_ => {
return Err(PFE { __kind: Invalid });
return Err(pfe_invalid())
},
},
}
Expand All @@ -135,7 +134,7 @@ macro_rules! from_str_radix_float_impl {
let base = match c {
'E' | 'e' if radix == 10 => 10.0,
'P' | 'p' if radix == 16 => 2.0,
_ => return Err(PFE { __kind: Invalid }),
_ => return Err(pfe_invalid()),
};

// Parse the exponent as decimal integer
Expand All @@ -144,13 +143,13 @@ macro_rules! from_str_radix_float_impl {
Some(('-', src)) => (false, src.parse::<usize>()),
Some(('+', src)) => (true, src.parse::<usize>()),
Some((_, _)) => (true, src.parse::<usize>()),
None => return Err(PFE { __kind: Invalid }),
None => return Err(pfe_invalid()),
};

match (is_positive, exp) {
(true, Ok(exp)) => base.powi(exp as i32),
(false, Ok(exp)) => 1.0 / base.powi(exp as i32),
(_, Err(_)) => return Err(PFE { __kind: Invalid }),
(_, Err(_)) => return Err(pfe_invalid()),
}
},
None => 1.0, // no exponent
Expand Down
79 changes: 1 addition & 78 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1365,47 +1365,6 @@ pub trait Float: Sized {
fn to_radians(self) -> Self;
}

macro_rules! from_str_float_impl {
($t:ty, $func:ident) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for $t {
type Err = ParseFloatError;

/// Converts a string in base 10 to a float.
/// Accepts an optional decimal exponent.
///
/// This function accepts strings such as
///
/// * '3.14'
/// * '-3.14'
/// * '2.5E10', or equivalently, '2.5e10'
/// * '2.5E-10'
/// * '.' (understood as 0)
/// * '5.'
/// * '.5', or, equivalently, '0.5'
/// * 'inf', '-inf', 'NaN'
///
/// Leading and trailing whitespace represent an error.
///
/// # Arguments
///
/// * src - A string
///
/// # Return value
///
/// `Err(ParseFloatError)` if the string did not represent a valid
/// number. Otherwise, `Ok(n)` where `n` is the floating-point
/// number represented by `src`.
#[inline]
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
dec2flt::$func(src)
}
}
}
}
from_str_float_impl!(f32, to_f32);
from_str_float_impl!(f64, to_f64);

macro_rules! from_str_radix_int_impl {
($($t:ty)*) => {$(
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1548,40 +1507,4 @@ impl fmt::Display for ParseIntError {
}
}

/// An error which can be returned when parsing a float.
#[derive(Debug, Clone, PartialEq)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct ParseFloatError {
#[doc(hidden)]
#[unstable(feature = "float_error_internals",
reason = "should not be exposed publicly",
issue = "0")]
pub __kind: FloatErrorKind
}

#[derive(Debug, Clone, PartialEq)]
#[unstable(feature = "float_error_internals",
reason = "should not be exposed publicly",
issue = "0")]
#[doc(hidden)]
pub enum FloatErrorKind {
Empty,
Invalid,
}

impl ParseFloatError {
#[doc(hidden)]
pub fn __description(&self) -> &str {
match self.__kind {
FloatErrorKind::Empty => "cannot parse float from empty string",
FloatErrorKind::Invalid => "invalid float literal",
}
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for ParseFloatError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.__description().fmt(f)
}
}
pub use num::dec2flt::ParseFloatError;
4 changes: 2 additions & 2 deletions src/libcoretest/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::atomic::*;
use core::atomic::Ordering::SeqCst;
use core::sync::atomic::*;
use core::sync::atomic::Ordering::SeqCst;

#[test]
fn bool_() {
Expand Down
Loading

0 comments on commit a2b932c

Please sign in to comment.