• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{fmt, str::FromStr};
2 
3 use crate::{
4     Buffer, ParseError,
5     err::{perr, ParseErrorKind::*},
6     parse::{end_dec_digits, first_byte_or_empty, check_suffix},
7 };
8 
9 
10 
11 /// A floating point literal, e.g. `3.14`, `8.`, `135e12`, or `1.956e2f64`.
12 ///
13 /// This kind of literal has several forms, but generally consists of a main
14 /// number part, an optional exponent and an optional type suffix. See
15 /// [the reference][ref] for more information.
16 ///
17 /// A leading minus sign `-` is not part of the literal grammar! `-3.14` are two
18 /// tokens in the Rust grammar. Further, `27` and `27f32` are both not float,
19 /// but integer literals! Consequently `FloatLit::parse` will reject them.
20 ///
21 ///
22 /// [ref]: https://doc.rust-lang.org/reference/tokens.html#floating-point-literals
23 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
24 pub struct FloatLit<B: Buffer> {
25     /// The whole raw input. The `usize` fields in this struct partition this
26     /// string. Always true: `end_integer_part <= end_fractional_part`.
27     ///
28     /// ```text
29     ///    12_3.4_56e789f32
30     ///        ╷    ╷   ╷
31     ///        |    |   └ end_number_part = 13
32     ///        |    └ end_fractional_part = 9
33     ///        └ end_integer_part = 4
34     ///
35     ///    246.
36     ///       ╷╷
37     ///       |└ end_fractional_part = end_number_part = 4
38     ///       └ end_integer_part = 3
39     ///
40     ///    1234e89
41     ///        ╷  ╷
42     ///        |  └ end_number_part = 7
43     ///        └ end_integer_part = end_fractional_part = 4
44     /// ```
45     raw: B,
46 
47     /// The first index not part of the integer part anymore. Since the integer
48     /// part is at the start, this is also the length of that part.
49     end_integer_part: usize,
50 
51     /// The first index after the fractional part.
52     end_fractional_part: usize,
53 
54     /// The first index after the whole number part (everything except type suffix).
55     end_number_part: usize,
56 }
57 
58 impl<B: Buffer> FloatLit<B> {
59     /// Parses the input as a floating point literal. Returns an error if the
60     /// input is invalid or represents a different kind of literal. Will also
61     /// reject decimal integer literals like `23` or `17f32`, in accordance
62     /// with the spec.
parse(s: B) -> Result<Self, ParseError>63     pub fn parse(s: B) -> Result<Self, ParseError> {
64         match first_byte_or_empty(&s)? {
65             b'0'..=b'9' => {
66                 // TODO: simplify once RFC 2528 is stabilized
67                 let FloatLit {
68                     end_integer_part,
69                     end_fractional_part,
70                     end_number_part,
71                     ..
72                 } = parse_impl(&s)?;
73 
74                 Ok(Self { raw: s, end_integer_part, end_fractional_part, end_number_part })
75             },
76             _ => Err(perr(0, DoesNotStartWithDigit)),
77         }
78     }
79 
80     /// Returns the number part (including integer part, fractional part and
81     /// exponent), but without the suffix. If you want an actual floating
82     /// point value, you need to parse this string, e.g. with `f32::from_str`
83     /// or an external crate.
number_part(&self) -> &str84     pub fn number_part(&self) -> &str {
85         &(*self.raw)[..self.end_number_part]
86     }
87 
88     /// Returns the non-empty integer part of this literal.
integer_part(&self) -> &str89     pub fn integer_part(&self) -> &str {
90         &(*self.raw)[..self.end_integer_part]
91     }
92 
93     /// Returns the optional fractional part of this literal. Does not include
94     /// the period. If a period exists in the input, `Some` is returned, `None`
95     /// otherwise. Note that `Some("")` might be returned, e.g. for `3.`.
fractional_part(&self) -> Option<&str>96     pub fn fractional_part(&self) -> Option<&str> {
97         if self.end_integer_part == self.end_fractional_part {
98             None
99         } else {
100             Some(&(*self.raw)[self.end_integer_part + 1..self.end_fractional_part])
101         }
102     }
103 
104     /// Optional exponent part. Might be empty if there was no exponent part in
105     /// the input. Includes the `e` or `E` at the beginning.
exponent_part(&self) -> &str106     pub fn exponent_part(&self) -> &str {
107         &(*self.raw)[self.end_fractional_part..self.end_number_part]
108     }
109 
110     /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
suffix(&self) -> &str111     pub fn suffix(&self) -> &str {
112         &(*self.raw)[self.end_number_part..]
113     }
114 
115     /// Returns the raw input that was passed to `parse`.
raw_input(&self) -> &str116     pub fn raw_input(&self) -> &str {
117         &self.raw
118     }
119 
120     /// Returns the raw input that was passed to `parse`, potentially owned.
into_raw_input(self) -> B121     pub fn into_raw_input(self) -> B {
122         self.raw
123     }
124 }
125 
126 impl FloatLit<&str> {
127     /// Makes a copy of the underlying buffer and returns the owned version of
128     /// `Self`.
to_owned(&self) -> FloatLit<String>129     pub fn to_owned(&self) -> FloatLit<String> {
130         FloatLit {
131             raw: self.raw.to_owned(),
132             end_integer_part: self.end_integer_part,
133             end_fractional_part: self.end_fractional_part,
134             end_number_part: self.end_number_part,
135         }
136     }
137 }
138 
139 impl<B: Buffer> fmt::Display for FloatLit<B> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result140     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141         write!(f, "{}", &*self.raw)
142     }
143 }
144 
145 /// Precondition: first byte of string has to be in `b'0'..=b'9'`.
146 #[inline(never)]
parse_impl(input: &str) -> Result<FloatLit<&str>, ParseError>147 pub(crate) fn parse_impl(input: &str) -> Result<FloatLit<&str>, ParseError> {
148     // Integer part.
149     let end_integer_part = end_dec_digits(input.as_bytes());
150     let rest = &input[end_integer_part..];
151 
152 
153     // Fractional part.
154     let end_fractional_part = if rest.as_bytes().get(0) == Some(&b'.') {
155         // The fractional part must not start with `_`.
156         if rest.as_bytes().get(1) == Some(&b'_') {
157             return Err(perr(end_integer_part + 1, UnexpectedChar));
158         }
159 
160         end_dec_digits(rest[1..].as_bytes()) + 1 + end_integer_part
161     } else {
162         end_integer_part
163     };
164     let rest = &input[end_fractional_part..];
165 
166     // If we have a period that is not followed by decimal digits, the
167     // literal must end now.
168     if end_integer_part + 1 == end_fractional_part && !rest.is_empty() {
169         return Err(perr(end_integer_part + 1, UnexpectedChar));
170     }
171 
172     // Optional exponent.
173     let end_number_part = if rest.starts_with('e') || rest.starts_with('E') {
174         // Strip single - or + sign at the beginning.
175         let exp_number_start = match rest.as_bytes().get(1) {
176             Some(b'-') | Some(b'+') => 2,
177             _ => 1,
178         };
179 
180         // Find end of exponent and make sure there is at least one digit.
181         let end_exponent = end_dec_digits(rest[exp_number_start..].as_bytes()) + exp_number_start;
182         if !rest[exp_number_start..end_exponent].bytes().any(|b| matches!(b, b'0'..=b'9')) {
183             return Err(perr(
184                 end_fractional_part..end_fractional_part + end_exponent,
185                 NoExponentDigits,
186             ));
187         }
188 
189         end_exponent + end_fractional_part
190     } else {
191         end_fractional_part
192     };
193 
194     // Make sure the suffix is valid.
195     let suffix = &input[end_number_part..];
196     check_suffix(suffix).map_err(|kind| perr(end_number_part..input.len(), kind))?;
197 
198     // A float literal needs either a fractional or exponent part, otherwise its
199     // an integer literal.
200     if end_integer_part == end_number_part {
201         return Err(perr(None, UnexpectedIntegerLit));
202     }
203 
204     Ok(FloatLit {
205         raw: input,
206         end_integer_part,
207         end_fractional_part,
208         end_number_part,
209     })
210 }
211 
212 
213 /// All possible float type suffixes.
214 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
215 #[non_exhaustive]
216 pub enum FloatType {
217     F32,
218     F64,
219 }
220 
221 impl FloatType {
222     /// Returns the type corresponding to the given suffix (e.g. `"f32"` is
223     /// mapped to `Self::F32`). If the suffix is not a valid float type, `None`
224     /// is returned.
from_suffix(suffix: &str) -> Option<Self>225     pub fn from_suffix(suffix: &str) -> Option<Self> {
226         match suffix {
227             "f32" => Some(FloatType::F32),
228             "f64" => Some(FloatType::F64),
229             _ => None,
230         }
231     }
232 
233     /// Returns the suffix for this type, e.g. `"f32"` for `Self::F32`.
suffix(self) -> &'static str234     pub fn suffix(self) -> &'static str {
235         match self {
236             Self::F32 => "f32",
237             Self::F64 => "f64",
238         }
239     }
240 }
241 
242 impl FromStr for FloatType {
243     type Err = ();
from_str(s: &str) -> Result<Self, Self::Err>244     fn from_str(s: &str) -> Result<Self, Self::Err> {
245         Self::from_suffix(s).ok_or(())
246     }
247 }
248 
249 impl fmt::Display for FloatType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result250     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251         self.suffix().fmt(f)
252     }
253 }
254 
255 
256 #[cfg(test)]
257 mod tests;
258