1 use rustc_ast
::ast
::{Lit, LitFloatType, LitIntType, LitKind}
;
4 #[derive(Debug, PartialEq, Copy, Clone)]
13 /// Returns a reasonable digit group size for this radix.
15 fn suggest_grouping(self) -> usize {
17 Self::Binary
| Self::Hexadecimal
=> 4,
18 Self::Octal
| Self::Decimal
=> 3,
23 /// A helper method to format numeric literals with digit grouping.
24 /// `lit` must be a valid numeric literal without suffix.
25 pub fn format(lit
: &str, type_suffix
: Option
<&str>, float
: bool
) -> String
{
26 NumericLiteral
::new(lit
, type_suffix
, float
).format()
30 pub struct NumericLiteral
<'a
> {
31 /// Which radix the literal was represented in.
33 /// The radix prefix, if present.
34 pub prefix
: Option
<&'a
str>,
36 /// The integer part of the number.
38 /// The fraction part of the number.
39 pub fraction
: Option
<&'a
str>,
40 /// The exponent separator (b'e' or b'E') including preceding underscore if present
41 /// and the exponent part.
42 pub exponent
: Option
<(&'a
str, &'a
str)>,
44 /// The type suffix, including preceding underscore if present.
45 pub suffix
: Option
<&'a
str>,
48 impl<'a
> NumericLiteral
<'a
> {
49 pub fn from_lit(src
: &'a
str, lit
: &Lit
) -> Option
<NumericLiteral
<'a
>> {
50 NumericLiteral
::from_lit_kind(src
, &lit
.kind
)
53 pub fn from_lit_kind(src
: &'a
str, lit_kind
: &LitKind
) -> Option
<NumericLiteral
<'a
>> {
54 if lit_kind
.is_numeric() && src
.chars().next().map_or(false, |c
| c
.is_digit(10)) {
55 let (unsuffixed
, suffix
) = split_suffix(src
, lit_kind
);
56 let float
= matches
!(lit_kind
, LitKind
::Float(..));
57 Some(NumericLiteral
::new(unsuffixed
, suffix
, float
))
64 pub fn new(lit
: &'a
str, suffix
: Option
<&'a
str>, float
: bool
) -> Self {
65 // Determine delimiter for radix prefix, if present, and radix.
66 let radix
= if lit
.starts_with("0x") {
68 } else if lit
.starts_with("0b") {
70 } else if lit
.starts_with("0o") {
76 // Grab part of the literal after prefix, if present.
77 let (prefix
, mut sans_prefix
) = if let Radix
::Decimal
= radix
{
80 let (p
, s
) = lit
.split_at(2);
84 if suffix
.is_some() && sans_prefix
.ends_with('_'
) {
85 // The '_' before the suffix isn't part of the digits
86 sans_prefix
= &sans_prefix
[..sans_prefix
.len() - 1];
89 let (integer
, fraction
, exponent
) = Self::split_digit_parts(sans_prefix
, float
);
101 pub fn is_decimal(&self) -> bool
{
102 self.radix
== Radix
::Decimal
105 pub fn split_digit_parts(digits
: &str, float
: bool
) -> (&str, Option
<&str>, Option
<(&str, &str)>) {
106 let mut integer
= digits
;
107 let mut fraction
= None
;
108 let mut exponent
= None
;
111 for (i
, c
) in digits
.char_indices() {
114 integer
= &digits
[..i
];
115 fraction
= Some(&digits
[i
+ 1..]);
118 let exp_start
= if digits
[..i
].ends_with('_'
) { i - 1 }
else { i }
;
120 if integer
.len() > exp_start
{
121 integer
= &digits
[..exp_start
];
123 fraction
= Some(&digits
[integer
.len() + 1..exp_start
]);
125 exponent
= Some((&digits
[exp_start
..=i
], &digits
[i
+ 1..]));
133 (integer
, fraction
, exponent
)
136 /// Returns literal formatted in a sensible way.
137 pub fn format(&self) -> String
{
138 let mut output
= String
::new();
140 if let Some(prefix
) = self.prefix
{
141 output
.push_str(prefix
);
144 let group_size
= self.radix
.suggest_grouping();
151 self.radix
== Radix
::Hexadecimal
,
154 if let Some(fraction
) = self.fraction
{
156 Self::group_digits(&mut output
, fraction
, group_size
, false, false);
159 if let Some((separator
, exponent
)) = self.exponent
{
160 output
.push_str(separator
);
161 Self::group_digits(&mut output
, exponent
, group_size
, true, false);
164 if let Some(suffix
) = self.suffix
{
165 if output
.ends_with('
.'
) {
169 output
.push_str(suffix
);
175 pub fn group_digits(output
: &mut String
, input
: &str, group_size
: usize, partial_group_first
: bool
, pad
: bool
) {
176 debug_assert
!(group_size
> 0);
178 let mut digits
= input
.chars().filter(|&c
| c
!= '_'
);
180 let first_group_size
;
182 if partial_group_first
{
183 first_group_size
= (digits
.clone().count() - 1) % group_size
+ 1;
185 for _
in 0..group_size
- first_group_size
{
190 first_group_size
= group_size
;
193 for _
in 0..first_group_size
{
194 if let Some(digit
) = digits
.next() {
199 for (c
, i
) in iter
::zip(digits
, (0..group_size
).cycle()) {
208 fn split_suffix
<'a
>(src
: &'a
str, lit_kind
: &LitKind
) -> (&'a
str, Option
<&'a
str>) {
209 debug_assert
!(lit_kind
.is_numeric());
210 lit_suffix_length(lit_kind
).map_or((src
, None
), |suffix_length
| {
211 let (unsuffixed
, suffix
) = src
.split_at(src
.len() - suffix_length
);
212 (unsuffixed
, Some(suffix
))
216 fn lit_suffix_length(lit_kind
: &LitKind
) -> Option
<usize> {
217 debug_assert
!(lit_kind
.is_numeric());
218 let suffix
= match lit_kind
{
219 LitKind
::Int(_
, int_lit_kind
) => match int_lit_kind
{
220 LitIntType
::Signed(int_ty
) => Some(int_ty
.name_str()),
221 LitIntType
::Unsigned(uint_ty
) => Some(uint_ty
.name_str()),
222 LitIntType
::Unsuffixed
=> None
,
224 LitKind
::Float(_
, float_lit_kind
) => match float_lit_kind
{
225 LitFloatType
::Suffixed(float_ty
) => Some(float_ty
.name_str()),
226 LitFloatType
::Unsuffixed
=> None
,