1
use alloc::borrow::Cow;
2
use alloc::string::String;
3
use core::fmt::Display;
4
use core::mem;
5
use core::ops::Range;
6

            
7
use unicode_ident::{is_xid_continue, is_xid_start};
8

            
9
use crate::tokenizer::char_iterator::CharIterator;
10

            
11
mod char_iterator;
12

            
13
/// A token in an Rsn document.
14
#[derive(Clone, Eq, PartialEq, Debug)]
15
pub struct Token<'a> {
16
    /// The byte range of this token.
17
    pub location: Range<usize>,
18
    /// The kind of this token.
19
    pub kind: TokenKind<'a>,
20
}
21

            
22
impl<'a> Token<'a> {
23
    #[must_use]
24
1587
    const fn new(location: Range<usize>, kind: TokenKind<'a>) -> Self {
25
1587
        Self { location, kind }
26
1587
    }
27
}
28

            
29
/// A kind of a token in an Rsn document.
30
#[derive(Clone, Debug)]
31
pub enum TokenKind<'a> {
32
    /// An integer literal.
33
    Integer(Integer),
34
    /// A floating point literal.
35
    Float(f64),
36
    /// A boolean literal.
37
    Bool(bool),
38
    /// A character literal.
39
    Character(char),
40
    /// The `:` character.
41
    Colon,
42
    /// The `,` character.
43
    Comma,
44
    /// A byte literal.
45
    Byte(u8),
46
    /// A string literal.
47
    String(Cow<'a, str>),
48
    /// A byte string literal.
49
    Bytes(Cow<'a, [u8]>),
50
    /// An identifier (name).
51
    Identifier(&'a str),
52
    /// The opening variant of a [`Balanced`] token.
53
    Open(Balanced),
54
    /// The closing variant of a [`Balanced`] token.
55
    Close(Balanced),
56
    /// A comment
57
    Comment(&'a str),
58
    /// Whitespace between other tokens.
59
    Whitespace(&'a str),
60
}
61

            
62
impl<'a> Eq for TokenKind<'a> {}
63

            
64
impl<'a> PartialEq for TokenKind<'a> {
65
132
    fn eq(&self, other: &Self) -> bool {
66
132
        match (self, other) {
67
58
            (Self::Integer(l0), Self::Integer(r0)) => l0 == r0,
68
20
            (Self::Float(l0), Self::Float(r0)) => l0.total_cmp(r0).is_eq(),
69
2
            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
70
8
            (Self::Character(l0), Self::Character(r0)) => l0 == r0,
71
7
            (Self::Byte(l0), Self::Byte(r0)) => l0 == r0,
72
8
            (Self::String(l0), Self::String(r0)) => l0 == r0,
73
13
            (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
74
4
            (Self::Comment(l0), Self::Comment(r0))
75
10
            | (Self::Identifier(l0), Self::Identifier(r0)) => l0 == r0,
76
2
            (Self::Open(l0), Self::Open(r0)) | (Self::Close(l0), Self::Close(r0)) => l0 == r0,
77
4
            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
78
        }
79
132
    }
80
}
81

            
82
/// An integer literal.
83
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
84
pub enum Integer {
85
    /// An unsigned integer that fits within an `usize`.
86
    Usize(usize),
87
    /// A signed integer that fits within an `isize`.
88
    Isize(isize),
89
    /// An unsigned integer that is too large to fit within an `usize`.
90
    UnsignedLarge(UnsignedLarge),
91
    /// A signed integer that is too large to fit within an `isize`.
92
    SignedLarge(SignedLarge),
93
}
94

            
95
macro_rules! fn_integer_into {
96
    ($name:ident, $type:ty) => {
97
        /// Returns this integer as a
98
        #[doc = stringify!($type)]
99
        /// if the value can fit in a
100
        #[doc = stringify!($type)]
101
        #[inline]
102
        #[must_use]
103
202
        pub fn $name(self) -> Option<$type> {
104
202
            match self {
105
165
                Integer::Usize(value) => value.try_into().ok(),
106
37
                Integer::Isize(value) => value.try_into().ok(),
107
                Integer::UnsignedLarge(value) => value.try_into().ok(),
108
                Integer::SignedLarge(value) => value.try_into().ok(),
109
            }
110
202
        }
111
    };
112
}
113

            
114
impl Integer {
115
    fn_integer_into!(as_u8, u8);
116

            
117
    fn_integer_into!(as_u16, u16);
118

            
119
    fn_integer_into!(as_u32, u32);
120

            
121
    fn_integer_into!(as_u64, u64);
122

            
123
    fn_integer_into!(as_u128, u128);
124

            
125
    fn_integer_into!(as_usize, usize);
126

            
127
    fn_integer_into!(as_i8, i8);
128

            
129
    fn_integer_into!(as_i16, i16);
130

            
131
    fn_integer_into!(as_i32, i32);
132

            
133
    fn_integer_into!(as_i64, i64);
134

            
135
    fn_integer_into!(as_i128, i128);
136

            
137
    fn_integer_into!(as_isize, isize);
138

            
139
    /// Returns true if this number is equal to 0.
140
    #[inline]
141
    #[must_use]
142
    pub const fn is_zero(self) -> bool {
143
        match self {
144
            Integer::Usize(value) => value == 0,
145
            Integer::Isize(value) => value == 0,
146
            Integer::UnsignedLarge(value) => value == 0,
147
            Integer::SignedLarge(value) => value == 0,
148
        }
149
    }
150

            
151
    /// Returns this number cast to an `f64`.
152
    #[must_use]
153
    #[allow(clippy::cast_precision_loss)]
154
    pub fn as_f64(self) -> f64 {
155
        match self {
156
            Integer::Usize(value) => value as f64,
157
            Integer::Isize(value) => value as f64,
158
            Integer::UnsignedLarge(value) => value as f64,
159
            Integer::SignedLarge(value) => value as f64,
160
        }
161
    }
162
}
163

            
164
#[cfg(feature = "integer128")]
165
type SignedLarge = i128;
166
#[cfg(feature = "integer128")]
167
type UnsignedLarge = u128;
168

            
169
#[cfg(not(feature = "integer128"))]
170
type SignedLarge = i64;
171
#[cfg(not(feature = "integer128"))]
172
type UnsignedLarge = u64;
173

            
174
impl From<usize> for Integer {
175
200
    fn from(value: usize) -> Self {
176
200
        Self::Usize(value)
177
200
    }
178
}
179

            
180
impl From<isize> for Integer {
181
43
    fn from(value: isize) -> Self {
182
43
        Self::Isize(value)
183
43
    }
184
}
185

            
186
macro_rules! impl_from_primitive {
187
    ($variant:ident, $target:ty, $ty:ty) => {
188
        impl From<$ty> for Integer {
189
4
            fn from(value: $ty) -> Self {
190
4
                Self::$variant(<$target>::from(value))
191
4
            }
192
        }
193
    };
194
}
195

            
196
impl_from_primitive!(Usize, usize, u8);
197
impl_from_primitive!(Usize, usize, u16);
198
impl_from_primitive!(Isize, isize, i8);
199
impl_from_primitive!(Isize, isize, i16);
200

            
201
macro_rules! impl_from_primitive_with_fallback {
202
    ($variant:ident, $target:ty, $larger:ident, $ty:ty) => {
203
        impl From<$ty> for Integer {
204
6
            fn from(value: $ty) -> Self {
205
6
                match <$target>::try_from(value) {
206
6
                    Ok(value) => Self::$variant(value),
207
                    Err(_) => Self::$larger(<$larger>::from(value)),
208
                }
209
6
            }
210
        }
211
    };
212
}
213

            
214
impl_from_primitive_with_fallback!(Usize, usize, UnsignedLarge, u32);
215
impl_from_primitive_with_fallback!(Isize, isize, SignedLarge, i32);
216
impl_from_primitive_with_fallback!(Usize, usize, UnsignedLarge, u64);
217
impl_from_primitive_with_fallback!(Isize, isize, SignedLarge, i64);
218

            
219
macro_rules! impl_try_from_primitive {
220
    ($variant:ident, $target:ty, $larger:ident, $ty:ty) => {
221
        impl TryFrom<$ty> for Integer {
222
            type Error = core::num::TryFromIntError;
223

            
224
2
            fn try_from(value: $ty) -> Result<Self, Self::Error> {
225
2
                match <$target>::try_from(value) {
226
2
                    Ok(value) => Ok(Self::$variant(value)),
227
                    Err(_) => Ok(Self::$larger(<$larger>::try_from(value)?)),
228
                }
229
2
            }
230
        }
231
    };
232
}
233

            
234
impl_try_from_primitive!(Usize, usize, UnsignedLarge, u128);
235
impl_try_from_primitive!(Isize, isize, SignedLarge, i128);
236

            
237
/// A token kind that is expected to have a balanced number of open and closing
238
/// variants.
239
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
240
pub enum Balanced {
241
    /// Parentheses
242
    Paren,
243
    /// Curly Braces
244
    Brace,
245
    /// Square Brackets
246
    Bracket,
247
}
248

            
249
/// Parses Rsn into a sequence of [`Token`]s.
250
#[derive(Debug, Clone)]
251
pub struct Tokenizer<'a, const INCLUDE_ALL: bool> {
252
    chars: CharIterator<'a>,
253
    scratch: String,
254
}
255

            
256
impl<'a> Tokenizer<'a, false> {
257
    /// Returns a tokenizer that ignores whitespace and comments.
258
    #[must_use]
259
173
    pub fn minified(source: &'a str) -> Self {
260
173
        Self::new(source)
261
173
    }
262
}
263

            
264
impl<'a> Tokenizer<'a, true> {
265
    /// Returns a tokenizer that includes whitespace and comments.
266
    #[must_use]
267
6
    pub fn full(source: &'a str) -> Self {
268
6
        Self::new(source)
269
6
    }
270
}
271

            
272
impl<'a, const INCLUDE_ALL: bool> Tokenizer<'a, INCLUDE_ALL> {
273
179
    fn new(source: &'a str) -> Self {
274
179
        Self {
275
179
            chars: CharIterator::new(source),
276
179
            scratch: String::new(),
277
179
        }
278
179
    }
279

            
280
    /// Returns the current byte offset of the tokenizer.
281
    #[must_use]
282
1358
    pub const fn current_offset(&self) -> usize {
283
1358
        self.chars.current_offset()
284
1358
    }
285

            
286
1821
    fn next_or_eof(&mut self) -> Result<char, Error> {
287
1821
        self.chars
288
1821
            .next()
289
1821
            .ok_or_else(|| self.error_at_last_char(ErrorKind::UnexpectedEof))
290
1821
    }
291

            
292
    fn error(&self, kind: ErrorKind) -> Error {
293
        Error::new(self.chars.marked_range(), kind)
294
    }
295

            
296
    fn error_at_last_char(&self, kind: ErrorKind) -> Error {
297
        Error::new(self.chars.last_char_range(), kind)
298
    }
299

            
300
    fn error_at_next_char(&mut self, kind: ErrorKind) -> Error {
301
        let range = if let Some((offset, ch)) = self.chars.peek_full() {
302
            offset..offset + ch.len_utf8()
303
        } else {
304
            let mut range = self.chars.last_char_range();
305
            range.start = range.end;
306
            range
307
        };
308
        Error::new(range, kind)
309
    }
310

            
311
    #[allow(clippy::cast_possible_truncation)]
312
219
    fn tokenize_positive_integer<I>(&mut self, mut value: I) -> Result<Token<'a>, Error>
313
219
    where
314
219
        I: Integral,
315
219
        Integer: From<I> + TryFrom<I::Larger>,
316
219
    {
317
219
        let mut has_decimal = false;
318
219
        let mut has_exponent = false;
319
219
        let mut had_underscores = false;
320
219
        let mut overflowing = false;
321
877
        while let Some(ch) = self.chars.peek() {
322
865
            if let Some(digit_value) = ch.to_digit(10) {
323
638
                if let Some(new_value) = value
324
638
                    .checked_mul(I::from(10))
325
638
                    .and_then(|value| value.checked_add(I::from(digit_value as u8)))
326
638
                {
327
638
                    value = new_value;
328
638
                    self.chars.next();
329
638
                } else {
330
                    overflowing = true;
331
                    break;
332
                }
333
227
            } else if ch == '.' {
334
11
                has_decimal = true;
335
11
                self.chars.next();
336
11
                break;
337
216
            } else if ch == 'e' || ch == 'E' {
338
3
                has_decimal = true;
339
3
                has_exponent = true;
340
3
                self.chars.next();
341
3
                break;
342
213
            } else if ch == '_' {
343
20
                self.chars.next();
344
20
                had_underscores = true;
345
20
            } else {
346
193
                break;
347
            }
348
        }
349

            
350
219
        if overflowing {
351
            let mut value: I::Larger = value.into_larger();
352
            while let Some(ch) = self.chars.peek() {
353
                if let Some(digit_value) = ch.to_digit(10) {
354
                    if let Some(new_value) = value
355
                        .checked_mul(<I::Larger>::from(10))
356
                        .and_then(|value| value.checked_add(<I::Larger>::from(digit_value as u8)))
357
                    {
358
                        value = new_value;
359
                        self.chars.next();
360
                    } else {
361
                        return Err(self.error(ErrorKind::IntegerTooLarge));
362
                    }
363
                } else if ch == '.' {
364
                    has_decimal = true;
365
                    self.chars.next();
366
                    break;
367
                } else if ch == 'e' || ch == 'E' {
368
                    has_decimal = true;
369
                    has_exponent = true;
370
                    self.chars.next();
371
                    break;
372
                } else if ch == '_' {
373
                    self.chars.next();
374
                    had_underscores = true;
375
                } else {
376
                    break;
377
                }
378
            }
379

            
380
            if !has_decimal {
381
                let integer = Integer::try_from(value).map_err(|_| {
382
                    Error::new(self.chars.marked_range(), ErrorKind::IntegerTooLarge)
383
                })?;
384
                return Ok(Token::new(
385
                    self.chars.marked_range(),
386
                    TokenKind::Integer(integer),
387
                ));
388
            }
389
219
        }
390

            
391
219
        if has_decimal {
392
14
            self.tokenize_float(had_underscores, has_exponent)
393
        } else {
394
205
            Ok(Token::new(
395
205
                self.chars.marked_range(),
396
205
                TokenKind::Integer(Integer::from(value)),
397
205
            ))
398
        }
399
219
    }
400

            
401
    #[allow(clippy::cast_possible_truncation)]
402
44
    fn tokenize_negative_integer<I>(&mut self, mut value: I) -> Result<Token<'a>, Error>
403
44
    where
404
44
        I: Integral,
405
44
        Integer: From<I> + TryFrom<I::Larger>,
406
44
    {
407
44
        let mut has_decimal = false;
408
44
        let mut has_exponent = false;
409
44
        let mut overflowing = false;
410
44
        let mut had_underscores = false;
411
364
        while let Some(ch) = self.chars.peek() {
412
356
            if let Some(digit_value) = ch.to_digit(10) {
413
310
                if let Some(new_value) = value
414
310
                    .checked_mul(I::from(10))
415
310
                    .and_then(|value| value.checked_sub(I::from(digit_value as u8)))
416
310
                {
417
310
                    value = new_value;
418
310
                    self.chars.next();
419
310
                } else {
420
                    overflowing = true;
421
                    break;
422
                }
423
46
            } else if ch == '.' {
424
5
                has_decimal = true;
425
5
                self.chars.next();
426
5
                break;
427
41
            } else if ch == 'e' || ch == 'E' {
428
1
                has_decimal = true;
429
1
                has_exponent = true;
430
1
                self.chars.next();
431
1
                break;
432
40
            } else if ch == '_' {
433
10
                had_underscores = true;
434
10
                self.chars.next();
435
10
            } else {
436
30
                break;
437
            }
438
        }
439

            
440
44
        if overflowing {
441
            let mut value: I::Larger = value.into_larger();
442
            while let Some(ch) = self.chars.peek() {
443
                if let Some(digit_value) = ch.to_digit(10) {
444
                    if let Some(new_value) = value
445
                        .checked_mul(<I::Larger>::from(10))
446
                        .and_then(|value| value.checked_sub(<I::Larger>::from(digit_value as u8)))
447
                    {
448
                        value = new_value;
449
                        self.chars.next();
450
                    } else {
451
                        return Err(self.error(ErrorKind::IntegerTooLarge));
452
                    }
453
                } else if ch == '.' {
454
                    has_decimal = true;
455
                    self.chars.next();
456
                    break;
457
                } else if ch == 'e' || ch == 'E' {
458
                    has_decimal = true;
459
                    has_exponent = true;
460
                    self.chars.next();
461
                    break;
462
                } else if ch == '_' {
463
                    had_underscores = true;
464
                    self.chars.next();
465
                } else {
466
                    break;
467
                }
468
            }
469

            
470
            if !has_decimal {
471
                let integer = Integer::try_from(value).map_err(|_| {
472
                    Error::new(self.chars.marked_range(), ErrorKind::IntegerTooLarge)
473
                })?;
474
                return Ok(Token::new(
475
                    self.chars.marked_range(),
476
                    TokenKind::Integer(integer),
477
                ));
478
            }
479
44
        }
480

            
481
44
        if has_decimal {
482
6
            self.tokenize_float(had_underscores, has_exponent)
483
        } else {
484
38
            Ok(Token::new(
485
38
                self.chars.marked_range(),
486
38
                TokenKind::Integer(Integer::from(value)),
487
38
            ))
488
        }
489
44
    }
490

            
491
20
    fn tokenize_float(
492
20
        &mut self,
493
20
        had_underscores: bool,
494
20
        mut has_exponent: bool,
495
20
    ) -> Result<Token<'a>, Error> {
496
20
        self.scratch.clear();
497
20
        let already_read_chars = self.chars.marked_str();
498
20
        if had_underscores {
499
            self.scratch
500
                .extend(already_read_chars.chars().filter(|ch| ch != &'_'));
501
20
        } else {
502
20
            self.scratch.push_str(already_read_chars);
503
20
        }
504

            
505
20
        if !has_exponent {
506
            // Read any decimal digits
507
31
            while let Some(ch) = self.chars.peek() {
508
27
                if ch.is_ascii_digit() {
509
15
                    self.scratch.push(ch);
510
15
                    self.chars.next();
511
15
                } else if !has_exponent && ch == 'e' || ch == 'E' {
512
8
                    self.scratch.push(ch);
513
8
                    has_exponent = true;
514
8
                    self.chars.next();
515
8

            
516
8
                    break;
517
                } else {
518
4
                    break;
519
                }
520
            }
521
4
        }
522

            
523
20
        if has_exponent {
524
            // Handle the exponent sign
525
12
            if let Some(ch) = self.chars.peek() {
526
12
                if ch == '+' || ch == '-' {
527
4
                    self.scratch.push(ch);
528
4
                    self.chars.next();
529
8
                }
530
            }
531

            
532
            // Require at least one digit for the exponent, but allow
533
            // skipping underscores.
534
12
            let mut has_exponent_digit = false;
535
30
            while let Some(ch) = self.chars.peek() {
536
20
                let is_digit = ch.is_ascii_digit();
537
20

            
538
20
                if is_digit || ch == '_' {
539
18
                    has_exponent_digit |= is_digit;
540
18
                    self.scratch.push(ch);
541
18
                    self.chars.next();
542
18
                } else {
543
2
                    break;
544
                }
545
            }
546

            
547
12
            if !has_exponent_digit {
548
                return Err(self.error_at_next_char(ErrorKind::ExpectedDigit));
549
12
            }
550
8
        }
551

            
552
20
        let parsed = self
553
20
            .scratch
554
20
            .parse::<f64>()
555
20
            .map_err(|_| self.error(ErrorKind::InvalidFloat))?;
556

            
557
20
        Ok(Token::new(
558
20
            self.chars.marked_range(),
559
20
            TokenKind::Float(parsed),
560
20
        ))
561
20
    }
562

            
563
    fn tokenize_radix_large_number<const BITS: u32>(
564
        &mut self,
565
        signed: bool,
566
        negative: bool,
567
        value: usize,
568
        first_hex_value: u32,
569
    ) -> Result<Token<'a>, Error> {
570
        assert!(BITS == 1 || BITS == 3 || BITS == 4);
571
        let max = 2_u8.pow(BITS);
572
        let mut value = value as UnsignedLarge;
573
        value <<= BITS;
574
        value |= first_hex_value as UnsignedLarge;
575

            
576
        while let Some(result) = self.chars.peek().map(|ch| ch.to_digit(BITS * 4).ok_or(ch)) {
577
            match result {
578
                Ok(radix_value) => {
579
                    self.chars.next();
580
                    if let Some(next_value) = value
581
                        .checked_mul(max as UnsignedLarge)
582
                        .and_then(|value| value.checked_add(radix_value as UnsignedLarge))
583
                    {
584
                        value = next_value;
585
                    } else {
586
                        return Err(self.error(ErrorKind::IntegerTooLarge));
587
                    }
588
                }
589
                Err('_') => {
590
                    self.chars.next();
591
                }
592
                Err(_) => break,
593
            }
594
        }
595

            
596
        Ok(Token::new(
597
            self.chars.marked_range(),
598
            TokenKind::Integer(match (signed, negative) {
599
                (_, true) => Integer::SignedLarge(-(value as SignedLarge)),
600
                (true, _) => Integer::SignedLarge(value as SignedLarge),
601
                (false, _) => Integer::UnsignedLarge(value),
602
            }),
603
        ))
604
    }
605

            
606
    #[allow(clippy::cast_possible_wrap)]
607
43
    fn tokenize_radix_number<const RADIX: u32>(
608
43
        &mut self,
609
43
        signed: bool,
610
43
        negative: bool,
611
43
    ) -> Result<Token<'a>, Error> {
612
43
        assert!(RADIX == 1 || RADIX == 3 || RADIX == 4);
613
43
        let max = 2_u32.pow(RADIX);
614
43
        let mut value = 0usize;
615
43
        let mut read_at_least_one_digit = false;
616

            
617
738
        while let Some(result) = self.chars.peek().map(|ch| ch.to_digit(max).ok_or(ch)) {
618
701
            match result {
619
572
                Ok(radix_value) => {
620
572
                    read_at_least_one_digit = true;
621
572
                    self.chars.next();
622
572
                    if let Some(next_value) = value
623
572
                        .checked_mul(max as usize)
624
572
                        .and_then(|value| value.checked_add(radix_value as usize))
625
572
                    {
626
572
                        value = next_value;
627
572
                    } else {
628
                        // Overflowed
629
                        return self.tokenize_radix_large_number::<RADIX>(
630
                            signed,
631
                            negative,
632
                            value,
633
                            radix_value,
634
                        );
635
                    }
636
                }
637
123
                Err('_') => {
638
123
                    self.chars.next();
639
123
                }
640
6
                Err(_) => break,
641
            }
642
        }
643

            
644
43
        if read_at_least_one_digit {
645
            Ok(Token::new(
646
43
                self.chars.marked_range(),
647
43
                TokenKind::Integer(match (signed, negative) {
648
9
                    (_, true) => Integer::Isize(-(value as isize)),
649
9
                    (true, _) => Integer::Isize(value as isize),
650
25
                    (false, _) => Integer::Usize(value),
651
                }),
652
            ))
653
        } else {
654
            Err(self.error(ErrorKind::ExpectedDigit))
655
        }
656
43
    }
657

            
658
125
    fn tokenize_leading_zero_number(
659
125
        &mut self,
660
125
        signed: bool,
661
125
        negative: bool,
662
125
    ) -> Result<Token<'a>, Error> {
663
125
        match self.chars.peek() {
664
            Some('x' | 'X') => {
665
15
                self.chars.next();
666
15
                return self.tokenize_radix_number::<4>(signed, negative);
667
            }
668
            Some('b' | 'B') => {
669
14
                self.chars.next();
670
14
                return self.tokenize_radix_number::<1>(signed, negative);
671
            }
672
            Some('o' | 'O') => {
673
14
                self.chars.next();
674
14
                return self.tokenize_radix_number::<3>(signed, negative);
675
            }
676
82
            _ => {}
677
82
        }
678
82

            
679
82
        match (signed, negative) {
680
1
            (_, true) => self.tokenize_negative_integer(0isize),
681
1
            (true, _) => self.tokenize_positive_integer(0isize),
682
80
            (false, _) => self.tokenize_positive_integer(0usize),
683
        }
684
125
    }
685

            
686
    #[allow(clippy::cast_possible_wrap)]
687
310
    fn tokenize_number(&mut self, start_char: u8) -> Result<Token<'a>, Error> {
688
310
        let negative = start_char == b'-';
689
310
        let signed = negative || start_char == b'+';
690
        // Check for inf/NaN
691
310
        if signed && matches!(self.chars.peek(), Some('i' | 'N')) {
692
            // We pass in 'i', but it doesn't matter as long as we provide a
693
            // xid_start character -- identifiers are always borrowed, so the
694
            // char is never passed to the output.
695
4
            return self.tokenize_identifier('i', false);
696
306
        }
697
306

            
698
306
        if signed {
699
73
            let next_char = self.next_or_eof()?;
700
73
            if next_char == '0' {
701
20
                self.tokenize_leading_zero_number(signed, negative)
702
53
            } else if let Some(value) = next_char.to_digit(10) {
703
53
                let value = value as isize;
704
53
                if negative {
705
43
                    self.tokenize_negative_integer(-value)
706
                } else {
707
10
                    self.tokenize_positive_integer(value)
708
                }
709
            } else {
710
                Err(self.error(ErrorKind::ExpectedDigit))
711
            }
712
233
        } else if start_char == b'0' {
713
105
            self.tokenize_leading_zero_number(signed, negative)
714
        } else {
715
128
            let value = (start_char - b'0') as usize;
716
128
            self.tokenize_positive_integer(value)
717
        }
718
310
    }
719

            
720
24
    fn tokenize_char(&mut self) -> Result<Token<'a>, Error> {
721
24
        let ch = match self.next_or_eof()? {
722
18
            '\\' => self
723
18
                .tokenize_escaped_char::<false, true>()?
724
18
                .expect("underscore disallowed"),
725
            ch @ ('\n' | '\r' | '\t') => {
726
                return Err(self.error_at_last_char(ErrorKind::Unexpected(ch)))
727
            }
728
6
            ch => ch,
729
        };
730

            
731
        // Handle the trailing quote
732
24
        match self.next_or_eof()? {
733
24
            '\'' => Ok(Token::new(
734
24
                self.chars.marked_range(),
735
24
                TokenKind::Character(ch),
736
24
            )),
737
            other => Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
738
        }
739
24
    }
740

            
741
11
    fn tokenize_byte(&mut self) -> Result<Token<'a>, Error> {
742
11
        let ch = match self.next_or_eof()? {
743
9
            '\\' => self
744
9
                .tokenize_escaped_char::<false, false>()?
745
9
                .expect("underscore disallowed"),
746
2
            ch if ch.is_ascii() && !matches!(ch, '\n' | '\r' | '\t') => ch,
747
            ch => return Err(self.error_at_last_char(ErrorKind::Unexpected(ch))),
748
        } as u8;
749

            
750
        // Handle the trailing quote
751
11
        match self.next_or_eof()? {
752
11
            '\'' => Ok(Token::new(self.chars.marked_range(), TokenKind::Byte(ch))),
753
            other => Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
754
        }
755
11
    }
756

            
757
36
    fn tokenize_string(&mut self) -> Result<Token<'a>, Error> {
758
        loop {
759
251
            match self.next_or_eof()? {
760
                '"' => {
761
                    // This string had no escapes, we can borrow.
762
29
                    let range = self.chars.marked_range();
763
29
                    let contents = &self.chars.source[range.start + 1..range.end - 1];
764
29
                    return Ok(Token::new(
765
29
                        range,
766
29
                        TokenKind::String(Cow::Borrowed(contents)),
767
29
                    ));
768
                }
769
7
                '\\' => break,
770
                '\r' => {
771
                    self.forbid_isolated_cr()?;
772
                }
773
215
                _ => {}
774
            }
775
        }
776

            
777
        // We've encountered an escape sequence, which requires us to use our
778
        // scratch buffer to decode the escape sequences. First, we need to copy
779
        // everything over that wasn't escaped.
780
7
        self.scratch.clear();
781
7
        let starting_range = self.chars.marked_range();
782
7
        self.scratch
783
7
            .push_str(&self.chars.source[starting_range.start + 1..starting_range.end - 1]);
784

            
785
        loop {
786
            // Handle the escape sequence
787
48
            if let Some(ch) = self.tokenize_escaped_char::<true, true>()? {
788
47
                self.scratch.push(ch);
789
47
            }
790
            // and then we resume a loop looking for the next escape sequence.
791
            loop {
792
143
                match self.next_or_eof()? {
793
                    '"' => {
794
7
                        return Ok(Token::new(
795
7
                            self.chars.marked_range(),
796
7
                            TokenKind::String(Cow::Owned(self.scratch.clone())),
797
7
                        ));
798
                    }
799
41
                    '\\' => break,
800
                    '\r' => {
801
                        self.forbid_isolated_cr()?;
802
                        self.scratch.push('\r');
803
                    }
804
95
                    ch => {
805
95
                        self.scratch.push(ch);
806
95
                    }
807
                }
808
            }
809
        }
810
36
    }
811

            
812
75
    fn tokenize_escaped_char<const ALLOW_CONTINUE: bool, const ALLOW_UNICODE: bool>(
813
75
        &mut self,
814
75
    ) -> Result<Option<char>, Error> {
815
75
        // Now we need to handle the current escape sequnce
816
75
        match self.next_or_eof()? {
817
14
            ch @ ('"' | '\'' | '\\') => Ok(Some(ch)),
818
4
            'r' => Ok(Some('\r')),
819
2
            'n' => Ok(Some('\n')),
820
4
            't' => Ok(Some('\t')),
821
16
            '0' => Ok(Some('\0')),
822
2
            'u' if ALLOW_UNICODE => self.tokenize_unicode_escape().map(Some),
823
            'x' => {
824
32
                let escape_start = self.chars.last_offset();
825
32
                match self.tokenize_ascii_escape()? {
826
32
                    byte if byte.is_ascii() => Ok(Some(byte as char)),
827
                    _ => Err(Error::new(
828
                        escape_start..self.chars.last_offset(),
829
                        ErrorKind::InvalidAscii,
830
                    )),
831
                }
832
            }
833
            '\r' if ALLOW_CONTINUE => {
834
                self.forbid_isolated_cr()?;
835
                self.eat_whitespace_for_string_continue();
836
                Ok(None)
837
            }
838
1
            '\n' if ALLOW_CONTINUE => {
839
1
                self.eat_whitespace_for_string_continue();
840
1
                Ok(None)
841
            }
842
            ch => Err(self.error_at_last_char(ErrorKind::Unexpected(ch))),
843
        }
844
75
    }
845

            
846
2
    fn tokenize_unicode_escape(&mut self) -> Result<char, Error> {
847
2
        let start = self.chars.last_offset();
848
2
        // Open brace
849
2
        match self.next_or_eof()? {
850
2
            '{' => {}
851
            other => return Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
852
        }
853

            
854
2
        let mut possible_char = 0u32;
855
        loop {
856
14
            match self.next_or_eof()? {
857
                '}' => {
858
2
                    break;
859
                }
860
2
                '_' => continue,
861
10
                ch => {
862
10
                    let radix_value = ch
863
10
                        .to_digit(16)
864
10
                        .ok_or_else(|| self.error_at_last_char(ErrorKind::InvalidUnicode))?;
865

            
866
10
                    if let Some(next_value) = possible_char.checked_shl(4) {
867
10
                        possible_char = next_value | radix_value;
868
10
                    } else {
869
                        // Overflowed
870
                        return Err(Error::new(
871
                            start..self.chars.last_offset(),
872
                            ErrorKind::InvalidUnicode,
873
                        ));
874
                    }
875
                }
876
            }
877
        }
878

            
879
2
        char::from_u32(possible_char)
880
2
            .ok_or_else(|| Error::new(start..self.chars.last_offset(), ErrorKind::InvalidUnicode))
881
2
    }
882

            
883
    #[allow(clippy::cast_possible_truncation)]
884
189
    fn tokenize_ascii_escape(&mut self) -> Result<u8, Error> {
885
189
        let first_digit =
886
189
            self.next_or_eof()?
887
189
                .to_digit(16)
888
189
                .ok_or_else(|| self.error_at_last_char(ErrorKind::InvalidAscii))? as u8;
889

            
890
189
        let second_digit =
891
189
            self.next_or_eof()?
892
189
                .to_digit(16)
893
189
                .ok_or_else(|| self.error_at_last_char(ErrorKind::InvalidAscii))? as u8;
894
189
        Ok((first_digit << 4) | second_digit)
895
189
    }
896

            
897
24
    fn tokenize_byte_string(&mut self) -> Result<Token<'a>, Error> {
898
        loop {
899
110
            match self.next_or_eof()? {
900
                '"' => {
901
11
                    let range = self.chars.marked_range();
902
11
                    let contents = &self.chars.source[range.start + 2..range.end - 1];
903
11
                    return Ok(Token::new(
904
11
                        range,
905
11
                        TokenKind::Bytes(Cow::Borrowed(contents.as_bytes())),
906
11
                    ));
907
                }
908
13
                '\\' => break,
909
86
                ch if ch.is_ascii() => {}
910
                _ => return Err(self.error_at_last_char(ErrorKind::InvalidAscii)),
911
            }
912
        }
913

            
914
        // We need a scratch buffer to handle escape sequences
915
13
        let mut scratch = mem::take(&mut self.scratch).into_bytes();
916
13
        scratch.clear();
917
13
        let start_range = self.chars.marked_range();
918
13
        scratch.extend_from_slice(
919
13
            self.chars.source[start_range.start + 2..start_range.end - 1].as_bytes(),
920
13
        );
921

            
922
        loop {
923
174
            let byte = match self.next_or_eof()? {
924
5
                ch @ ('"' | '\'' | '\\') => Some(ch as u8),
925
2
                'r' => Some(b'\r'),
926
1
                'n' => Some(b'\n'),
927
2
                't' => Some(b'\t'),
928
6
                '0' => Some(b'\0'),
929
157
                'x' => Some(self.tokenize_ascii_escape()?),
930
                '\r' => {
931
                    self.forbid_isolated_cr()?;
932
                    self.eat_whitespace_for_string_continue();
933
                    None
934
                }
935
                '\n' => {
936
1
                    self.eat_whitespace_for_string_continue();
937
1
                    None
938
                }
939
                ch => return Err(self.error_at_last_char(ErrorKind::Unexpected(ch))),
940
            };
941

            
942
174
            if let Some(byte) = byte {
943
173
                scratch.push(byte);
944
173
            }
945

            
946
            loop {
947
268
                match self.next_or_eof()? {
948
                    '"' => {
949
                        // We want to keep the scratch buffer around to keep
950
                        // from needing to constantly grow it while parsing.
951
13
                        let contents = scratch.clone();
952
13
                        scratch.clear();
953
13
                        self.scratch = String::from_utf8(scratch).expect("empty vec");
954
13
                        return Ok(Token::new(
955
13
                            self.chars.marked_range(),
956
13
                            TokenKind::Bytes(Cow::Owned(contents)),
957
13
                        ));
958
                    }
959
161
                    '\\' => break,
960
94
                    ch if ch.is_ascii() => {
961
94
                        scratch.push(ch as u8);
962
94
                    }
963
                    _ => return Err(self.error_at_last_char(ErrorKind::InvalidAscii)),
964
                }
965
            }
966
        }
967
24
    }
968

            
969
10
    fn tokenize_raw(&mut self) -> Result<Token<'a>, Error> {
970
10
        let mut pound_count = 0;
971
        // Count the number of leading pound signs
972
        loop {
973
21
            match self.next_or_eof()? {
974
11
                '#' => pound_count += 1,
975
6
                '"' => break,
976
4
                ch if pound_count == 1 && is_xid_start(ch) => {
977
4
                    return self.tokenize_identifier(ch, true);
978
                }
979
                other => return Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
980
            }
981
        }
982

            
983
        // String contents
984
        'contents: loop {
985
87
            match self.next_or_eof()? {
986
                '\r' => {
987
                    self.forbid_isolated_cr()?;
988
                }
989
                '"' => {
990
14
                    let mut pounds_needed = pound_count;
991
23
                    while self.chars.peek() == Some('#') && pounds_needed > 0 {
992
9
                        self.chars.next();
993
9
                        pounds_needed -= 1;
994
9
                    }
995

            
996
                    // Only break if the correct number of pound signs has been
997
                    // encountered.
998
14
                    if pounds_needed == 0 {
999
6
                        break 'contents;
8
                    }
                }
73
                _ => {}
            }
        }

            
6
        let source_range = self.chars.marked_range();
6
        let value = &self.chars.source
6
            [source_range.start + pound_count + 2..source_range.end - pound_count - 1];
6
        Ok(Token::new(
6
            source_range,
6
            TokenKind::String(Cow::Borrowed(value)),
6
        ))
10
    }

            
6
    fn tokenize_raw_byte_string(&mut self) -> Result<Token<'a>, Error> {
6
        // Count the number of leading pound signs
6
        let mut pound_count = 0;
        loop {
13
            match self.next_or_eof()? {
7
                '#' => pound_count += 1,
6
                '"' => break,
                other if pound_count == 0 => return self.tokenize_identifier(other, true),
                other => return Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
            }
        }

            
        // String contents
        'contents: loop {
87
            match self.next_or_eof()? {
                '\r' => {
                    self.forbid_isolated_cr()?;
                }
                '"' => {
14
                    let mut pounds_needed = pound_count;
23
                    while self.chars.peek() == Some('#') && pounds_needed > 0 {
9
                        self.chars.next();
9
                        pounds_needed -= 1;
9
                    }

            
                    // Only break if the correct number of pound signs has been
                    // encountered.
14
                    if pounds_needed == 0 {
6
                        break 'contents;
8
                    }
                }
73
                ch if ch.is_ascii() => {}
                _ => return Err(self.error_at_last_char(ErrorKind::InvalidAscii)),
            }
        }

            
6
        let source_range = self.chars.marked_range();
6
        let value = &self.chars.source
6
            [source_range.start + pound_count + 3..source_range.end - pound_count - 1];
6
        Ok(Token::new(
6
            source_range,
6
            TokenKind::Bytes(Cow::Borrowed(value.as_bytes())),
6
        ))
6
    }

            
    fn forbid_isolated_cr(&mut self) -> Result<(), Error> {
        if self.chars.peek() == Some('\n') {
            Ok(())
        } else {
            Err(self.error_at_last_char(ErrorKind::InvalidAscii))
        }
    }

            
2
    fn eat_whitespace_for_string_continue(&mut self) {
18
        while self
18
            .chars
18
            .peek()
18
            .map_or(false, |ch| matches!(ch, ' ' | '\n' | '\r' | '\t'))
16
        {
16
            self.chars.next();
16
        }
2
    }

            
352
    fn tokenize_identifier(
352
        &mut self,
352
        initial_char: char,
352
        is_raw: bool,
352
    ) -> Result<Token<'a>, Error> {
352
        let require_start = initial_char != '_';
352
        let initial_char_index = self.chars.last_offset();
        // Validate the first character
352
        let start_is_valid = if require_start {
350
            is_xid_start(initial_char)
        } else {
2
            is_xid_continue(initial_char)
        };

            
352
        if start_is_valid {
1607
            while let Some(ch) = self.chars.peek() {
1590
                if is_xid_continue(ch) {
1256
                    self.chars.next();
1256
                } else {
334
                    break;
                }
            }

            
351
            let source = &self.chars.source[initial_char_index..self.chars.current_offset()];
351

            
351
            Ok(Token::new(
351
                self.chars.marked_range(),
1
                match source {
351
                    "true" if !is_raw => TokenKind::Bool(true),
338
                    "false" if !is_raw => TokenKind::Bool(false),
323
                    "inf" | "+inf" if !is_raw => TokenKind::Float(f64::INFINITY),
321
                    "NaN" | "+NaN" if !is_raw => TokenKind::Float(f64::NAN),
319
                    "-inf" if !is_raw => TokenKind::Float(-f64::INFINITY),
318
                    "-NaN" if !is_raw => TokenKind::Float(-f64::NAN),
319
                    _ => TokenKind::Identifier(source),
                },
            ))
        } else {
1
            Err(Error {
1
                location: initial_char_index..self.chars.current_offset(),
1
                kind: ErrorKind::Unexpected(initial_char),
1
            })
        }
352
    }

            
18
    fn tokenize_comment(&mut self) -> Result<Token<'a>, Error> {
18
        match self.next_or_eof()? {
16
            '*' => self.tokenize_block_comment(),
2
            '/' => self.tokenize_single_line_comment(),
            other => Err(self.error_at_last_char(ErrorKind::Unexpected(other))),
        }
18
    }

            
16
    fn tokenize_block_comment(&mut self) -> Result<Token<'a>, Error> {
16
        let mut nests = 1;
37
        while nests > 0 {
37
            match self.next_or_eof()? {
                '*' => {
20
                    if self.chars.peek() == Some('/') {
17
                        self.chars.next();
17
                        nests -= 1;
17
                        if nests == 0 {
16
                            break;
1
                        }
3
                    }
                }
                '/' => {
1
                    if self.chars.peek() == Some('*') {
1
                        self.chars.next();
1
                        nests += 1;
1
                    }
                }
                '\r' => {
                    self.forbid_isolated_cr()?;
                }
16
                _ => {}
            }
        }

            
16
        Ok(Token::new(
16
            self.chars.marked_range(),
16
            TokenKind::Comment(&self.chars.source[self.chars.marked_range()]),
16
        ))
16
    }

            
2
    fn tokenize_single_line_comment(&mut self) -> Result<Token<'a>, Error> {
        loop {
12
            match self.chars.peek() {
                Some('\r') => {
                    self.forbid_isolated_cr()?;
                    break;
                }
2
                Some('\n') | None => break,
10
                _ => {
10
                    self.chars.next();
10
                }
            }
        }
2
        let range = self.chars.marked_range();
2
        Ok(Token::new(
2
            range.clone(),
2
            TokenKind::Comment(&self.chars.source[range]),
2
        ))
2
    }
}

            
impl<'a, const INCLUDE_ALL: bool> Iterator for Tokenizer<'a, INCLUDE_ALL> {
    type Item = Result<Token<'a>, Error>;

            
1645
    fn next(&mut self) -> Option<Self::Item> {
        loop {
2042
            self.chars.mark_start();
2042
            let ch = self.chars.next()?;
1434
            let result = match ch {
1009
                '0'..='9' | '-' | '+' => self.tokenize_number(ch as u8),
36
                '"' => self.tokenize_string(),
24
                '\'' => self.tokenize_char(),
14
                'r' => match self.chars.peek() {
10
                    Some('#' | '"') => self.tokenize_raw(),
4
                    _ => self.tokenize_identifier(ch, false),
                },
86
                'b' => match self.chars.peek() {
                    Some('r') => {
6
                        self.chars.next();
6
                        self.tokenize_raw_byte_string()
                    }
                    Some('"') => {
24
                        self.chars.next();
24
                        self.tokenize_byte_string()
                    }
                    Some('\'') => {
11
                        self.chars.next();
11
                        self.tokenize_byte()
                    }
45
                    _ => self.tokenize_identifier(ch, false),
                },
23
                '(' => Ok(Token::new(
23
                    self.chars.marked_range(),
23
                    TokenKind::Open(Balanced::Paren),
23
                )),
23
                ')' => Ok(Token::new(
23
                    self.chars.marked_range(),
23
                    TokenKind::Close(Balanced::Paren),
23
                )),
34
                '{' => Ok(Token::new(
34
                    self.chars.marked_range(),
34
                    TokenKind::Open(Balanced::Brace),
34
                )),
34
                '}' => Ok(Token::new(
34
                    self.chars.marked_range(),
34
                    TokenKind::Close(Balanced::Brace),
34
                )),
19
                '[' => Ok(Token::new(
19
                    self.chars.marked_range(),
19
                    TokenKind::Open(Balanced::Bracket),
19
                )),
18
                ']' => Ok(Token::new(
18
                    self.chars.marked_range(),
18
                    TokenKind::Close(Balanced::Bracket),
18
                )),
276
                ':' => Ok(Token::new(self.chars.marked_range(), TokenKind::Colon)),
245
                ',' => Ok(Token::new(self.chars.marked_range(), TokenKind::Comma)),
711
                ch if is_rust_whitespace(ch) => {
                    loop {
762
                        match self.chars.peek() {
757
                            Some(ch) if is_rust_whitespace(ch) => {
364
                                self.chars.next();
364
                            }
398
                            _ => break,
398
                        }
398
                    }
398
                    if INCLUDE_ALL {
1
                        Ok(Token::new(
1
                            self.chars.marked_range(),
1
                            TokenKind::Whitespace(self.chars.marked_str()),
1
                        ))
                    } else {
397
                        continue;
                    }
                }
18
                '/' => self.tokenize_comment(),
295
                ch => self.tokenize_identifier(ch, false),
            };
1434
            break Some(result);
        }
1623
    }
}

            
1468
fn is_rust_whitespace(ch: char) -> bool {
    // https://doc.rust-lang.org/reference/whitespace.html
706
    matches!(
1468
        ch,
        '\t' | '\n'
            | '\u{b}'
            | '\u{c}'
            | '\r'
            | ' '
            | '\u{85}'
            | '\u{200e}'
            | '\u{200f}'
            | '\u{2028}'
            | '\u{2029}'
    )
1468
}

            
/// An error returned from a tokenizer.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Error {
    /// The byte range of the error.
    pub location: Range<usize>,
    /// The kind of error that occurred.
    pub kind: ErrorKind,
}

            
impl Error {
    #[must_use]
    const fn new(location: Range<usize>, kind: ErrorKind) -> Self {
        Self { location, kind }
    }
}

            
impl Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{} at {:?}", self.kind, self.location)
    }
}

            
#[cfg(feature = "std")]
impl std::error::Error for Error {}

            
/// The kind of an error returned from a tokenizer.
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
    /// The end of input was encountered when additional data was still expected.
    UnexpectedEof,
    /// An unexpected character was found.
    Unexpected(char),
    /// A digit (0-9) was expected.
    ExpectedDigit,
    /// The integer literal is too large to represent.
    IntegerTooLarge,
    /// An invalid unicode code point was encountered.
    InvalidUnicode,
    /// An invalid ASCII character was encountered.
    InvalidAscii,
    /// An invalid floating point number was found.
    InvalidFloat,
    /// A carriage return without a corresponding `\n` was encountered.
    IsolatedCarriageReturn,
}

            
impl Display for ErrorKind {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            ErrorKind::UnexpectedEof => f.write_str("unexpected eof"),
            ErrorKind::Unexpected(ch) => write!(f, "unexpected `{ch}`"),
            ErrorKind::ExpectedDigit => f.write_str("expected digit"),
            ErrorKind::InvalidUnicode => f.write_str("invalid unicode escape sequence"),
            ErrorKind::InvalidAscii => f.write_str("invalid ascii escape sequence"),
            ErrorKind::IsolatedCarriageReturn => f.write_str("unexpected isolated carriage return"),
            ErrorKind::IntegerTooLarge => f.write_str("value overflowed the maximum size"),
            ErrorKind::InvalidFloat => f.write_str("invalid floating point literal"),
        }
    }
}

            
trait Integral: From<u8> + Copy {
    type Larger: Integral;
    fn into_larger(self) -> Self::Larger;
    fn checked_mul(self, other: Self) -> Option<Self>;
    fn checked_add(self, other: Self) -> Option<Self>;
    fn checked_sub(self, other: Self) -> Option<Self>;
}

            
impl Integral for usize {
    type Larger = UnsignedLarge;

            
    fn into_larger(self) -> Self::Larger {
        self as UnsignedLarge
    }

            
606
    fn checked_mul(self, other: Self) -> Option<Self> {
606
        self.checked_mul(other)
606
    }

            
606
    fn checked_add(self, other: Self) -> Option<Self> {
606
        self.checked_add(other)
606
    }

            
    fn checked_sub(self, other: Self) -> Option<Self> {
        self.checked_sub(other)
    }
}

            
impl Integral for UnsignedLarge {
    type Larger = Self;

            
    fn into_larger(self) -> Self::Larger {
        self
    }

            
    fn checked_mul(self, other: Self) -> Option<Self> {
        self.checked_mul(other)
    }

            
    fn checked_add(self, other: Self) -> Option<Self> {
        self.checked_add(other)
    }

            
    fn checked_sub(self, other: Self) -> Option<Self> {
        self.checked_sub(other)
    }
}

            
impl Integral for isize {
    type Larger = SignedLarge;

            
    fn into_larger(self) -> Self::Larger {
        self as SignedLarge
    }

            
342
    fn checked_mul(self, other: Self) -> Option<Self> {
342
        self.checked_mul(other)
342
    }

            
32
    fn checked_add(self, other: Self) -> Option<Self> {
32
        self.checked_add(other)
32
    }

            
310
    fn checked_sub(self, other: Self) -> Option<Self> {
310
        self.checked_sub(other)
310
    }
}

            
impl Integral for SignedLarge {
    type Larger = Self;

            
    fn into_larger(self) -> Self::Larger {
        self
    }

            
    fn checked_mul(self, other: Self) -> Option<Self> {
        self.checked_mul(other)
    }

            
    fn checked_add(self, other: Self) -> Option<Self> {
        self.checked_add(other)
    }

            
    fn checked_sub(self, other: Self) -> Option<Self> {
        self.checked_sub(other)
    }
}

            
#[cfg(test)]
#[allow(clippy::too_many_lines)]
mod tests {
    use alloc::vec::Vec;

            
    use super::*;

            
    #[track_caller]
119
    fn test_tokens(source: &str, tokens: &[Token<'_>]) {
119
        assert_eq!(
119
            &Tokenizer::minified(source)
119
                .collect::<Result<Vec<_>, _>>()
119
                .unwrap(),
119
            tokens
119
        );
119
    }

            
    #[track_caller]
4
    fn test_tokens_full(source: &str, tokens: &[Token<'_>]) {
4
        assert_eq!(
4
            &Tokenizer::full(source)
4
                .collect::<Result<Vec<_>, _>>()
4
                .unwrap(),
4
            tokens
4
        );
4
    }

            
    #[track_caller]
1
    fn test_tokens_err(source: &str, location: Range<usize>, kind: &ErrorKind) {
1
        let err = Tokenizer::minified(source)
1
            .collect::<Result<Vec<_>, _>>()
1
            .expect_err("source did not error");
1
        assert_eq!(&err.kind, kind);
1
        assert_eq!(err.location, location);
1
    }

            
    #[test]
1
    fn identifiers() {
1
        test_tokens("true", &[Token::new(0..4, TokenKind::Bool(true))]);
1
        test_tokens("false", &[Token::new(0..5, TokenKind::Bool(false))]);
1

            
1
        test_tokens("r#true", &[Token::new(0..6, TokenKind::Identifier("true"))]);
1
        test_tokens(
1
            "r#false",
1
            &[Token::new(0..7, TokenKind::Identifier("false"))],
1
        );
1

            
1
        test_tokens("_", &[Token::new(0..1, TokenKind::Identifier("_"))]);
1

            
1
        test_tokens("_0", &[Token::new(0..2, TokenKind::Identifier("_0"))]);
1

            
1
        test_tokens_err("=", 0..1, &ErrorKind::Unexpected('='));
1
    }

            
    #[test]
1
    fn integers() {
1
        test_tokens(
1
            "0",
1
            &[Token::new(0..1, TokenKind::Integer(Integer::Usize(0)))],
1
        );
1
        test_tokens(
1
            "9",
1
            &[Token::new(0..1, TokenKind::Integer(Integer::Usize(9)))],
1
        );
1
        test_tokens(
1
            "10",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Usize(10)))],
1
        );
1
        test_tokens(
1
            "99",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Usize(99)))],
1
        );
1
        test_tokens(
1
            "+0",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Isize(0)))],
1
        );
1
        test_tokens(
1
            "+9",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Isize(9)))],
1
        );
1
        test_tokens(
1
            "-0",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Isize(0)))],
1
        );
1
        test_tokens(
1
            "-9",
1
            &[Token::new(0..2, TokenKind::Integer(Integer::Isize(-9)))],
1
        );
1
        test_tokens(
1
            "-10",
1
            &[Token::new(0..3, TokenKind::Integer(Integer::Isize(-10)))],
1
        );
1
        test_tokens(
1
            "-99",
1
            &[Token::new(0..3, TokenKind::Integer(Integer::Isize(-99)))],
1
        );
1

            
1
        // Test 16-bit integer maximums
1
        test_tokens(
1
            "+32_767",
1
            &[Token::new(0..7, TokenKind::Integer(Integer::Isize(32_767)))],
1
        );
1
        test_tokens(
1
            "-32_768",
1
            &[Token::new(
1
                0..7,
1
                TokenKind::Integer(Integer::Isize(-32_768)),
1
            )],
1
        );
1
        test_tokens(
1
            "65_535",
1
            &[Token::new(0..6, TokenKind::Integer(Integer::Usize(65_535)))],
1
        );
1

            
1
        // Test 32-bit integer maximums
1
        test_tokens(
1
            "+2_147_483_647",
1
            &[Token::new(
1
                0..14,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Isize(2_147_483_647)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::SignedLarge(2_147_483_647)),
1
            )],
1
        );
1
        test_tokens(
1
            "-2_147_483_648",
1
            &[Token::new(
1
                0..14,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-2_147_483_648)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-2_147_483_648)),
1
            )],
1
        );
1
        test_tokens(
1
            "4_294_967_295",
1
            &[Token::new(
1
                0..13,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Usize(4_294_967_295)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::UnsignedLarge(4_294_967_295)),
1
            )],
1
        );
1

            
1
        // Test 64-bit integer maximums
1
        test_tokens(
1
            "+9_223_372_036_854_775_807",
1
            &[Token::new(
1
                0..26,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(9_223_372_036_854_775_807)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(9_223_372_036_854_775_807)),
1
            )],
1
        );
1
        test_tokens(
1
            "-9_223_372_036_854_775_808",
1
            &[Token::new(
1
                0..26,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-9_223_372_036_854_775_808)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-9_223_372_036_854_775_808)),
1
            )],
1
        );
1
        test_tokens(
1
            "18_446_744_073_709_551_615",
1
            &[Token::new(
1
                0..26,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Usize(18_446_744_073_709_551_615)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::UnsignedLarge(18_446_744_073_709_551_615)),
1
            )],
1
        );
1

            
1
        #[cfg(feature = "integer128")]
1
        {
1
            test_tokens(
1
                "+9_223_372_036_854_775_808",
1
                &[Token::new(
1
                    0..26,
1
                    TokenKind::Integer(Integer::SignedLarge(9_223_372_036_854_775_808)),
1
                )],
1
            );
1
            test_tokens(
1
                "-9_223_372_036_854_775_809",
1
                &[Token::new(
1
                    0..26,
1
                    TokenKind::Integer(Integer::SignedLarge(-9_223_372_036_854_775_809)),
1
                )],
1
            );
1
            test_tokens(
1
                "18_446_744_073_709_551_616",
1
                &[Token::new(
1
                    0..26,
1
                    TokenKind::Integer(Integer::UnsignedLarge(18_446_744_073_709_551_616)),
1
                )],
1
            );
1
        }
1
    }

            
    #[test]
1
    fn hex_integers() {
1
        test_tokens(
1
            "0x1",
1
            &[Token::new(0..3, TokenKind::Integer(Integer::Usize(1)))],
1
        );
1
        test_tokens(
1
            "0X12",
1
            &[Token::new(0..4, TokenKind::Integer(Integer::Usize(0x12)))],
1
        );
1
        test_tokens(
1
            "0x12_3",
1
            &[Token::new(0..6, TokenKind::Integer(Integer::Usize(0x123)))],
1
        );
1
        test_tokens(
1
            "0xaBc",
1
            &[Token::new(0..5, TokenKind::Integer(Integer::Usize(0xabc)))],
1
        );
1

            
1
        // Test 16-bit integer maximums
1
        test_tokens(
1
            "+0xFFFF",
1
            &[Token::new(0..7, TokenKind::Integer(Integer::Isize(0xFFFF)))],
1
        );
1
        test_tokens(
1
            "-0xFFFF",
1
            &[Token::new(
1
                0..7,
1
                TokenKind::Integer(Integer::Isize(-0xFFFF)),
1
            )],
1
        );
1
        test_tokens(
1
            "0xFFFF",
1
            &[Token::new(0..6, TokenKind::Integer(Integer::Usize(0xFFFF)))],
1
        );
1

            
1
        // Test 32-bit integer maximums
1
        test_tokens(
1
            "+0xFFFF_FFFF",
1
            &[Token::new(
1
                0..12,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Isize(0xFFFF_FFFF)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::SignedLarge(0xFFFF_FFFF)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0xFFFF_FFFF",
1
            &[Token::new(
1
                0..12,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0xFFFF_FFFF)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-0xFFFF_FFFF)),
1
            )],
1
        );
1
        test_tokens(
1
            "0xFFFF_FFFF",
1
            &[Token::new(
1
                0..11,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Usize(0xFFFF_FFFF)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::UnsignedLarge(0xFFFF_FFFF)),
1
            )],
1
        );
1

            
1
        // Test 64-bit integer maximums
1
        #[allow(overflowing_literals)]
1
        test_tokens(
1
            "+0xFFFF_FFFF_FFFF_FFFF",
1
            &[Token::new(
1
                0..22,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(0xFFFF_FFFF_FFFF_FFFF)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(0xFFFF_FFFF_FFFF_FFFF)),
1
            )],
1
        );
1
        #[allow(overflowing_literals)]
1
        test_tokens(
1
            "-0xFFFF_FFFF_FFFF_FFFF",
1
            &[Token::new(
1
                0..22,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0xFFFF_FFFF_FFFF_FFFF)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-0xFFFF_FFFF_FFFF_FFFF)),
1
            )],
1
        );
1
        test_tokens(
1
            "0xFFFF_FFFF_FFFF_FFFF",
1
            &[Token::new(
1
                0..21,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Usize(0xFFFF_FFFF_FFFF_FFFF)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::UnsignedLarge(0xFFFF_FFFF_FFFF_FFFF)),
1
            )],
1
        );
1

            
1
        #[cfg(feature = "integer128")]
1
        {
1
            #[allow(overflowing_literals)]
1
            test_tokens(
1
                "+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF",
1
                &[Token::new(
1
                    0..42,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF,
1
                    )),
1
                )],
1
            );
1
            #[allow(overflowing_literals)]
1
            test_tokens(
1
                "-0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF",
1
                &[Token::new(
1
                    0..42,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF,
1
                    )),
1
                )],
1
            );
1
            test_tokens(
1
                "0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF",
1
                &[Token::new(
1
                    0..41,
1
                    TokenKind::Integer(Integer::UnsignedLarge(
1
                        0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF,
1
                    )),
1
                )],
1
            );
1
        }
1
    }

            
    #[test]
    #[allow(overflowing_literals)]
1
    fn octal_integers() {
1
        test_tokens(
1
            "0o1",
1
            &[Token::new(0..3, TokenKind::Integer(Integer::Usize(1)))],
1
        );
1
        test_tokens(
1
            "0O12",
1
            &[Token::new(0..4, TokenKind::Integer(Integer::Usize(0o12)))],
1
        );
1
        test_tokens(
1
            "0o12_3",
1
            &[Token::new(0..6, TokenKind::Integer(Integer::Usize(0o123)))],
1
        );
1

            
1
        // Test 16-bit integer maximums
1
        test_tokens(
1
            "+0o177_777",
1
            &[Token::new(
1
                0..10,
1
                TokenKind::Integer(Integer::Isize(0o177_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0o177_777",
1
            &[Token::new(
1
                0..10,
1
                TokenKind::Integer(Integer::Isize(-0o177_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "0o177_777",
1
            &[Token::new(
1
                0..9,
1
                TokenKind::Integer(Integer::Usize(0o177_777)),
1
            )],
1
        );
1

            
1
        // Test 32-bit integer maximums
1
        test_tokens(
1
            "+0o37_777_777_777",
1
            &[Token::new(
1
                0..17,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Isize(0o37_777_777_777)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::SignedLarge(0o37_777_777_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0o37_777_777_777",
1
            &[Token::new(
1
                0..17,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0o37_777_777_777)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-0o37_777_777_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "0o37_777_777_777",
1
            &[Token::new(
1
                0..16,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Usize(0o37_777_777_777)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::UnsignedLarge(0o37_777_777_777)),
1
            )],
1
        );
1

            
1
        // Test 64-bit integer maximums
1
        test_tokens(
1
            "+0o1_777_777_777_777_777_777_777",
1
            &[Token::new(
1
                0..32,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(0o1_777_777_777_777_777_777_777)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(0o1_777_777_777_777_777_777_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0o1_777_777_777_777_777_777_777",
1
            &[Token::new(
1
                0..32,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0o1_777_777_777_777_777_777_777)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-0o1_777_777_777_777_777_777_777)),
1
            )],
1
        );
1
        test_tokens(
1
            "0o1_777_777_777_777_777_777_777",
1
            &[Token::new(
1
                0..31,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Usize(0o1_777_777_777_777_777_777_777)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::UnsignedLarge(0o1_777_777_777_777_777_777_777)),
1
            )],
1
        );
1

            
1
        #[cfg(feature = "integer128")]
1
        {
1
            test_tokens(
1
                "+0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777",
1
                &[Token::new(
1
                    0..60,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777,
1
                    )),
1
                )],
1
            );
1
            test_tokens(
1
                "-0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777",
1
                &[Token::new(
1
                    0..60,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        -0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777,
1
                    )),
1
                )],
1
            );
1
            test_tokens(
1
                "0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777",
1
                &[Token::new(
1
                    0..59,
1
                    TokenKind::Integer(Integer::UnsignedLarge(
1
                        0o3_777_777_777_777_777_777_777_777_777_777_777_777_777_777,
1
                    )),
1
                )],
1
            );
1
        }
1
    }

            
    #[test]
    #[allow(overflowing_literals)]
1
    fn binary_integers() {
1
        test_tokens(
1
            "0b1",
1
            &[Token::new(0..3, TokenKind::Integer(Integer::Usize(1)))],
1
        );
1
        test_tokens(
1
            "0B10",
1
            &[Token::new(0..4, TokenKind::Integer(Integer::Usize(0b10)))],
1
        );
1
        test_tokens(
1
            "0b10_1",
1
            &[Token::new(0..6, TokenKind::Integer(Integer::Usize(0b101)))],
1
        );
1

            
1
        // Test 16-bit integer maximums
1
        test_tokens(
1
            "+0b1111_1111_1111_1111",
1
            &[Token::new(
1
                0..22,
1
                TokenKind::Integer(Integer::Isize(0b1111_1111_1111_1111)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0b1111_1111_1111_1111",
1
            &[Token::new(
1
                0..22,
1
                TokenKind::Integer(Integer::Isize(-0b1111_1111_1111_1111)),
1
            )],
1
        );
1
        test_tokens(
1
            "0b1111_1111_1111_1111",
1
            &[Token::new(
1
                0..21,
1
                TokenKind::Integer(Integer::Usize(0b1111_1111_1111_1111)),
1
            )],
1
        );
1

            
1
        // Test 32-bit integer maximums
1
        test_tokens(
1
            "+0b1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..42,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Isize(0b1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::SignedLarge(
1
                    0b1111_1111_1111_1111_1111_1111_1111_1111,
1
                )),
1
            )],
1
        );
1
        test_tokens(
1
            "-0b1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..42,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0b1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(
1
                    -0b1111_1111_1111_1111_1111_1111_1111_1111,
1
                )),
1
            )],
1
        );
1
        test_tokens(
1
            "0b1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..41,
1
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::Usize(0b1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(target_pointer_width = "16")]
1
                TokenKind::Integer(Integer::UnsignedLarge(
1
                    0b1111_1111_1111_1111_1111_1111_1111_1111,
1
                )),
1
            )],
1
        );
1

            
1
        // Test 64-bit integer maximums
1
        test_tokens(
1
            "+0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..82,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
            )],
1
        );
1
        test_tokens(
1
            "-0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..82,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Isize(-0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::SignedLarge(-0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
            )],
1
        );
1
        test_tokens(
1
            "0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
            &[Token::new(
1
                0..81,
1
                #[cfg(target_pointer_width = "64")]
1
                TokenKind::Integer(Integer::Usize(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
                #[cfg(not(target_pointer_width = "64"))]
1
                TokenKind::Integer(Integer::UnsignedLarge(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)),
1
            )],
1
        );
1

            
1
        #[cfg(feature = "integer128")]
1
        {
1
            test_tokens(
1
                "+0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
                &[Token::new(
1
                    0..162,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111,
1
                    )),
1
                )],
1
            );
1
            test_tokens(
1
                "-0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
                &[Token::new(
1
                    0..162,
1
                    TokenKind::Integer(Integer::SignedLarge(
1
                        -0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111,
1
                    )),
1
                )],
1
            );
1
            test_tokens(
1
                "0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111",
1
                &[Token::new(
1
                    0..161,
1
                    TokenKind::Integer(Integer::UnsignedLarge(
1
                        0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111,
1
                    )),
1
                )],
1
            );
1
        }
1
    }

            
    #[test]
1
    fn floats() {
1
        test_tokens("0.", &[Token::new(0..2, TokenKind::Float(0.))]);
1
        test_tokens("1.0", &[Token::new(0..3, TokenKind::Float(1.))]);
1
        test_tokens("-1.0", &[Token::new(0..4, TokenKind::Float(-1.))]);
1
        test_tokens("+1.0", &[Token::new(0..4, TokenKind::Float(1.))]);
1
        test_tokens("-1.0e1", &[Token::new(0..6, TokenKind::Float(-10.))]);
1
        test_tokens("+1.0e1", &[Token::new(0..6, TokenKind::Float(10.))]);
1
        test_tokens("-1.0e+1", &[Token::new(0..7, TokenKind::Float(-10.))]);
1
        test_tokens("+1.0e+1", &[Token::new(0..7, TokenKind::Float(10.))]);
1
        test_tokens("-10.0e-1", &[Token::new(0..8, TokenKind::Float(-1.))]);
1
        test_tokens("+10.0e-1", &[Token::new(0..8, TokenKind::Float(1.))]);
1
        test_tokens("-1.0e10", &[Token::new(0..7, TokenKind::Float(-1e10))]);
1
        test_tokens("+1.0e10", &[Token::new(0..7, TokenKind::Float(1.0e10))]);
1
        test_tokens("-1e10", &[Token::new(0..5, TokenKind::Float(-1e10))]);
1
        test_tokens("+1e10", &[Token::new(0..5, TokenKind::Float(1e10))]);
1
        test_tokens("inf", &[Token::new(0..3, TokenKind::Float(f64::INFINITY))]);
1
        test_tokens("NaN", &[Token::new(0..3, TokenKind::Float(f64::NAN))]);
1
        test_tokens(
1
            "-inf",
1
            &[Token::new(0..4, TokenKind::Float(-f64::INFINITY))],
1
        );
1
        test_tokens("-NaN", &[Token::new(0..4, TokenKind::Float(-f64::NAN))]);
1
        test_tokens("+inf", &[Token::new(0..4, TokenKind::Float(f64::INFINITY))]);
1
        test_tokens("+NaN", &[Token::new(0..4, TokenKind::Float(f64::NAN))]);
1
    }

            
    #[test]
1
    fn maps() {
1
        test_tokens(
1
            "{a:1,b:2}",
1
            &[
1
                Token::new(0..1, TokenKind::Open(Balanced::Brace)),
1
                Token::new(1..2, TokenKind::Identifier("a")),
1
                Token::new(2..3, TokenKind::Colon),
1
                Token::new(3..4, TokenKind::Integer(Integer::Usize(1))),
1
                Token::new(4..5, TokenKind::Comma),
1
                Token::new(5..6, TokenKind::Identifier("b")),
1
                Token::new(6..7, TokenKind::Colon),
1
                Token::new(7..8, TokenKind::Integer(Integer::Usize(2))),
1
                Token::new(8..9, TokenKind::Close(Balanced::Brace)),
1
            ],
1
        );
1
    }

            
    #[test]
1
    fn strings() {
        macro_rules! test_string {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(
                    ch,
                    &[Token::new(
                        0..ch.len(),
                        TokenKind::String(Cow::Borrowed($char)),
                    )],
                );
            };
        }
1
        test_string!("");
1
        test_string!("abc");
1
        test_string!("\r\t\n\0\x41\u{1_F980}\\\"");
1
        // string-continue, better tested with an escaped literal than trust the
1
        // line endings being preserved in git.
1
        test_tokens(
1
            "\"a\\\n \t \r \n  b\"",
1
            &[Token::new(0..14, TokenKind::String(Cow::Borrowed("ab")))],
1
        );
1
    }

            
    #[test]
1
    fn raw_strings() {
        macro_rules! test_string {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(
                    ch,
                    &[Token::new(
                        0..ch.len(),
                        TokenKind::String(Cow::Borrowed($char)),
                    )],
                );
            };
        }
1
        test_string!(r###""##"###);
1
        test_string!(r"abc");
1
        test_string!(r#"abc"#);
1
        test_string!(r#""\r\t\n\0\x41\u{1_F980}\\\"""#);
1
    }

            
    #[test]
1
    fn chars() {
        macro_rules! test_char {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(ch, &[Token::new(0..ch.len(), TokenKind::Character($char))]);
            };
        }

            
1
        test_char!('\0');
1
        test_char!('\r');
1
        test_char!('\t');
1
        test_char!('\\');
1
        test_char!('\'');
1
        test_char!('\"');
1
        test_char!('\x42');
1
        test_char!('\u{1_F980}');
1
    }

            
    #[test]
1
    fn bytes() {
        macro_rules! test_byte {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(ch, &[Token::new(0..ch.len(), TokenKind::Byte($char))]);
            };
        }

            
1
        test_byte!(b'\0');
1
        test_byte!(b'\r');
1
        test_byte!(b'\t');
1
        test_byte!(b'\\');
1
        test_byte!(b'\'');
1
        test_byte!(b'\"');
1
        test_byte!(b'\x42');
1
    }

            
    #[test]
1
    fn byte_strings() {
        macro_rules! test_byte_string {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(
                    ch,
                    &[Token::new(
                        0..ch.len(),
                        TokenKind::Bytes(Cow::Borrowed($char)),
                    )],
                );
            };
        }

            
1
        test_byte_string!(b"hello world");
1
        test_byte_string!(b"\0");
1
        test_byte_string!(b"\r");
1
        test_byte_string!(b"\t");
1
        test_byte_string!(b"\\");
1
        test_byte_string!(b"\'");
1
        test_byte_string!(b"\"");
1
        test_byte_string!(b"\x42");
1
        // string-continue, better tested with an escaped literal than trust the
1
        // line endings being preserved in git.
1
        test_tokens(
1
            "b\"a\\\n \t \r \n  b\"",
1
            &[Token::new(0..15, TokenKind::Bytes(Cow::Borrowed(b"ab")))],
1
        );
1
    }

            
    #[test]
1
    fn raw_byte_strings() {
        macro_rules! test_string {
            ($char:tt) => {
                let ch = core::stringify!($char);
                test_tokens(
                    ch,
                    &[Token::new(
                        0..ch.len(),
                        TokenKind::Bytes(Cow::Borrowed($char)),
                    )],
                );
            };
        }
1
        test_string!(br"abc");
1
        test_string!(br###""##"###);
1
        test_string!(br#"abc"#);
1
        test_string!(br#""\r\t\n\0\x41\u{1_F980}\\\"""#);
1
    }

            
    #[test]
1
    fn block_comments() {
        macro_rules! test_comment {
            ($comment:tt) => {
                test_tokens_full(
                    $comment,
                    &[Token::new(0..$comment.len(), TokenKind::Comment($comment))],
                );
            };
        }
1
        test_comment!("/* hello */");
1
        test_comment!("/*** /* hello */ **/");
1
    }

            
    #[test]
1
    fn single_line_comments() {
1
        test_tokens_full(
1
            "// test",
1
            &[Token::new(0..7, TokenKind::Comment("// test"))],
1
        );
1
        test_tokens_full(
1
            "// test\n",
1
            &[
1
                Token::new(0..7, TokenKind::Comment("// test")),
1
                Token::new(7..8, TokenKind::Whitespace("\n")),
1
            ],
1
        );
1
    }
}