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