blob: 22648b817e8aa6aa9a3671728b07f2e735affb3f [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnay7d1d1282018-01-06 16:10:51 -08009use proc_macro2::{Literal, Span, TokenNode};
David Tolnay360efd22018-01-04 23:35:26 -080010use std::str;
11
David Tolnay7d1d1282018-01-06 16:10:51 -080012#[cfg(feature = "printing")]
13use proc_macro2::{Term, TokenTree};
14
David Tolnay360efd22018-01-04 23:35:26 -080015#[cfg(feature = "extra-traits")]
Alex Crichtonccbb45d2017-05-23 10:58:24 -070016use std::hash::{Hash, Hasher};
17
David Tolnay360efd22018-01-04 23:35:26 -080018ast_enum_of_structs! {
19 pub enum Lit {
20 /// A string literal (`"foo"`)
21 pub Str(LitStr #manual_extra_traits {
22 token: Literal,
23 pub span: Span,
24 }),
Alex Crichtonccbb45d2017-05-23 10:58:24 -070025
David Tolnay360efd22018-01-04 23:35:26 -080026 /// A byte string (`b"foo"`)
27 pub ByteStr(LitByteStr #manual_extra_traits {
28 token: Literal,
29 pub span: Span,
30 }),
31
32 /// A byte char (`b'f'`)
33 pub Byte(LitByte #manual_extra_traits {
34 token: Literal,
35 pub span: Span,
36 }),
37
38 /// A character literal (`'a'`)
39 pub Char(LitChar #manual_extra_traits {
40 token: Literal,
41 pub span: Span,
42 }),
43
44 /// An integer literal (`1`)
45 ///
46 /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger
47 /// integer literal.
48 pub Int(LitInt #manual_extra_traits {
49 token: Literal,
50 pub span: Span,
51 }),
52
53 /// A float literal (`1f64` or `1E10f64`)
54 ///
55 /// Must be finite. May not be infinte or NaN.
56 pub Float(LitFloat #manual_extra_traits {
57 token: Literal,
58 pub span: Span,
59 }),
60
61 /// A boolean literal
62 pub Bool(LitBool #manual_extra_traits {
63 pub value: bool,
64 pub span: Span,
65 }),
66
67 pub Verbatim(LitVerbatim #manual_extra_traits {
68 pub token: Literal,
69 pub span: Span,
70 }),
71 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -070072}
73
David Tolnay360efd22018-01-04 23:35:26 -080074impl LitStr {
75 pub fn new(value: &str, span: Span) -> Self {
76 LitStr {
77 token: Literal::string(value),
78 span: span,
79 }
80 }
81
82 pub fn value(&self) -> String {
83 value::parse_lit_str(&self.token.to_string())
84 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -070085}
86
David Tolnay360efd22018-01-04 23:35:26 -080087impl LitByteStr {
88 pub fn new(value: &[u8], span: Span) -> Self {
89 LitByteStr {
90 token: Literal::byte_string(value),
91 span: span,
92 }
93 }
94
95 pub fn value(&self) -> Vec<u8> {
96 value::parse_lit_byte_str(&self.token.to_string())
97 }
98}
99
100impl LitByte {
101 pub fn new(value: u8, span: Span) -> Self {
102 LitByte {
103 token: Literal::byte_char(value),
104 span: span,
105 }
106 }
107
108 pub fn value(&self) -> u8 {
109 value::parse_lit_byte(&self.token.to_string())
110 }
111}
112
113impl LitChar {
114 pub fn new(value: char, span: Span) -> Self {
115 LitChar {
116 token: Literal::character(value),
117 span: span,
118 }
119 }
120
121 pub fn value(&self) -> char {
122 value::parse_lit_char(&self.token.to_string())
123 }
124}
125
126impl LitInt {
127 pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self {
128 LitInt {
129 token: match suffix {
130 IntSuffix::Isize => Literal::isize(value as isize),
131 IntSuffix::I8 => Literal::i8(value as i8),
132 IntSuffix::I16 => Literal::i16(value as i16),
133 IntSuffix::I32 => Literal::i32(value as i32),
134 IntSuffix::I64 => Literal::i64(value as i64),
135 IntSuffix::I128 => value::to_literal(&format!("{}i128", value)),
136 IntSuffix::Usize => Literal::usize(value as usize),
137 IntSuffix::U8 => Literal::u8(value as u8),
138 IntSuffix::U16 => Literal::u16(value as u16),
139 IntSuffix::U32 => Literal::u32(value as u32),
140 IntSuffix::U64 => Literal::u64(value),
141 IntSuffix::U128 => value::to_literal(&format!("{}u128", value)),
142 IntSuffix::None => Literal::integer(value as i64),
143 },
144 span: span,
145 }
146 }
147
148 pub fn value(&self) -> u64 {
149 value::parse_lit_int(&self.token.to_string()).unwrap()
150 }
151
152 pub fn suffix(&self) -> IntSuffix {
153 let value = self.token.to_string();
154 for (s, suffix) in vec![
155 ("i8", IntSuffix::I8),
156 ("i16", IntSuffix::I16),
157 ("i32", IntSuffix::I32),
158 ("i64", IntSuffix::I64),
159 ("i128", IntSuffix::I128),
160 ("isize", IntSuffix::Isize),
161 ("u8", IntSuffix::U8),
162 ("u16", IntSuffix::U16),
163 ("u32", IntSuffix::U32),
164 ("u64", IntSuffix::U64),
165 ("u128", IntSuffix::U128),
166 ("usize", IntSuffix::Usize),
167 ] {
168 if value.ends_with(s) {
169 return suffix;
170 }
171 }
172 IntSuffix::None
173 }
174}
175
176impl LitFloat {
177 pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self {
178 LitFloat {
179 token: match suffix {
180 FloatSuffix::F32 => Literal::f32(value as f32),
181 FloatSuffix::F64 => Literal::f64(value),
182 FloatSuffix::None => Literal::float(value),
183 },
184 span: span,
185 }
186 }
187
188 pub fn value(&self) -> f64 {
189 value::parse_lit_float(&self.token.to_string())
190 }
191
192 pub fn suffix(&self) -> FloatSuffix {
193 let value = self.token.to_string();
David Tolnay61037c62018-01-05 16:21:03 -0800194 for (s, suffix) in vec![("f32", FloatSuffix::F32), ("f64", FloatSuffix::F64)] {
David Tolnay360efd22018-01-04 23:35:26 -0800195 if value.ends_with(s) {
196 return suffix;
197 }
198 }
199 FloatSuffix::None
200 }
201}
202
203macro_rules! lit_extra_traits {
204 ($ty:ident, $field:ident) => {
205 #[cfg(feature = "extra-traits")]
206 impl Eq for $ty {}
207
208 #[cfg(feature = "extra-traits")]
209 impl PartialEq for $ty {
210 fn eq(&self, other: &Self) -> bool {
211 self.$field.to_string() == other.$field.to_string()
212 }
213 }
214
215 #[cfg(feature = "extra-traits")]
216 impl Hash for $ty {
217 fn hash<H>(&self, state: &mut H)
218 where
219 H: Hasher,
220 {
221 self.$field.to_string().hash(state);
222 }
David Tolnay9c76bcb2017-12-26 23:14:59 -0500223 }
Alex Crichton62a0a592017-05-22 13:58:53 -0700224 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700225}
226
David Tolnay360efd22018-01-04 23:35:26 -0800227lit_extra_traits!(LitStr, token);
228lit_extra_traits!(LitByteStr, token);
229lit_extra_traits!(LitByte, token);
230lit_extra_traits!(LitChar, token);
231lit_extra_traits!(LitInt, token);
232lit_extra_traits!(LitFloat, token);
233lit_extra_traits!(LitBool, value);
234lit_extra_traits!(LitVerbatim, token);
235
236ast_enum! {
237 pub enum StrStyle #no_visit {
238 /// A regular string, like `"foo"`
239 Cooked,
240 /// A raw string, like `r##"foo"##`
241 ///
242 /// The unsigned integer is the number of `#` symbols used.
243 Raw(usize),
Alex Crichton62a0a592017-05-22 13:58:53 -0700244 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700245}
246
David Tolnay360efd22018-01-04 23:35:26 -0800247ast_enum! {
248 pub enum IntSuffix #no_visit {
249 I8,
250 I16,
251 I32,
252 I64,
253 I128,
254 Isize,
255 U8,
256 U16,
257 U32,
258 U64,
259 U128,
260 Usize,
261 None,
Pascal Hertleif36342c52016-10-19 10:31:42 +0200262 }
263}
264
David Tolnay360efd22018-01-04 23:35:26 -0800265ast_enum! {
266 pub enum FloatSuffix #no_visit {
267 F32,
268 F64,
269 None,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700270 }
David Tolnay5fe14fc2017-01-27 16:22:08 -0800271}
272
273#[cfg(feature = "parsing")]
David Tolnayf4bbbd92016-09-23 14:41:55 -0700274pub mod parsing {
275 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500276 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800277 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500278 use parse_error;
279 use synom::PResult;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700280
Alex Crichton954046c2017-05-30 21:49:42 -0700281 impl Synom for Lit {
Michael Layzell92639a52017-06-01 00:07:44 -0400282 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400283 match input.literal() {
David Tolnay360efd22018-01-04 23:35:26 -0800284 Some((span, lit, rest)) => Ok((Lit::new(lit, span), rest)),
David Tolnay73c98de2017-12-31 15:56:56 -0500285 _ => match input.term() {
David Tolnay360efd22018-01-04 23:35:26 -0800286 Some((span, term, rest)) => Ok((
287 Lit::Bool(LitBool {
288 value: if term.as_str() == "true" {
289 true
290 } else if term.as_str() == "false" {
291 false
292 } else {
293 return parse_error();
David Tolnay51382052017-12-27 13:46:21 -0500294 },
David Tolnay360efd22018-01-04 23:35:26 -0800295 span: span,
296 }),
297 rest,
298 )),
Michael Layzell589a8f42017-06-02 19:47:01 -0400299 _ => parse_error(),
David Tolnay51382052017-12-27 13:46:21 -0500300 },
Michael Layzell589a8f42017-06-02 19:47:01 -0400301 }
David Tolnayfa0edf22016-09-23 22:58:24 -0700302 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800303
304 fn description() -> Option<&'static str> {
305 Some("literal")
306 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700307 }
David Tolnay360efd22018-01-04 23:35:26 -0800308
309 impl_synom!(LitStr "string literal" switch!(
310 syn!(Lit),
311 Lit::Str(lit) => value!(lit)
312 |
313 _ => reject!()
314 ));
315
316 impl_synom!(LitByteStr "byte string literal" switch!(
317 syn!(Lit),
318 Lit::ByteStr(lit) => value!(lit)
319 |
320 _ => reject!()
321 ));
322
323 impl_synom!(LitByte "byte literal" switch!(
324 syn!(Lit),
325 Lit::Byte(lit) => value!(lit)
326 |
327 _ => reject!()
328 ));
329
330 impl_synom!(LitChar "character literal" switch!(
331 syn!(Lit),
332 Lit::Char(lit) => value!(lit)
333 |
334 _ => reject!()
335 ));
336
337 impl_synom!(LitInt "integer literal" switch!(
338 syn!(Lit),
339 Lit::Int(lit) => value!(lit)
340 |
341 _ => reject!()
342 ));
343
344 impl_synom!(LitFloat "floating point literal" switch!(
345 syn!(Lit),
346 Lit::Float(lit) => value!(lit)
347 |
348 _ => reject!()
349 ));
350
351 impl_synom!(LitBool "boolean literal" switch!(
352 syn!(Lit),
353 Lit::Bool(lit) => value!(lit)
354 |
355 _ => reject!()
356 ));
David Tolnayf4bbbd92016-09-23 14:41:55 -0700357}
358
359#[cfg(feature = "printing")]
360mod printing {
361 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500362 use quote::{ToTokens, Tokens};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700363
David Tolnay360efd22018-01-04 23:35:26 -0800364 impl ToTokens for LitStr {
David Tolnayf4bbbd92016-09-23 14:41:55 -0700365 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnay360efd22018-01-04 23:35:26 -0800366 tokens.append(TokenTree {
367 span: self.span,
368 kind: TokenNode::Literal(self.token.clone()),
369 });
370 }
371 }
372
373 impl ToTokens for LitByteStr {
374 fn to_tokens(&self, tokens: &mut Tokens) {
375 tokens.append(TokenTree {
376 span: self.span,
377 kind: TokenNode::Literal(self.token.clone()),
378 });
379 }
380 }
381
382 impl ToTokens for LitByte {
383 fn to_tokens(&self, tokens: &mut Tokens) {
384 tokens.append(TokenTree {
385 span: self.span,
386 kind: TokenNode::Literal(self.token.clone()),
387 });
388 }
389 }
390
391 impl ToTokens for LitChar {
392 fn to_tokens(&self, tokens: &mut Tokens) {
393 tokens.append(TokenTree {
394 span: self.span,
395 kind: TokenNode::Literal(self.token.clone()),
396 });
397 }
398 }
399
400 impl ToTokens for LitInt {
401 fn to_tokens(&self, tokens: &mut Tokens) {
402 tokens.append(TokenTree {
403 span: self.span,
404 kind: TokenNode::Literal(self.token.clone()),
405 });
406 }
407 }
408
409 impl ToTokens for LitFloat {
410 fn to_tokens(&self, tokens: &mut Tokens) {
411 tokens.append(TokenTree {
412 span: self.span,
413 kind: TokenNode::Literal(self.token.clone()),
414 });
415 }
416 }
417
418 impl ToTokens for LitBool {
419 fn to_tokens(&self, tokens: &mut Tokens) {
420 tokens.append(TokenTree {
421 span: self.span,
David Tolnay61037c62018-01-05 16:21:03 -0800422 kind: TokenNode::Term(Term::intern(if self.value { "true" } else { "false" })),
David Tolnay360efd22018-01-04 23:35:26 -0800423 });
424 }
425 }
426
427 impl ToTokens for LitVerbatim {
428 fn to_tokens(&self, tokens: &mut Tokens) {
429 tokens.append(TokenTree {
430 span: self.span,
431 kind: TokenNode::Literal(self.token.clone()),
432 });
433 }
434 }
435}
436
437mod value {
438 use super::*;
439 use std::char;
440 use std::ops::{Index, RangeFrom};
441 use proc_macro2::TokenStream;
442
David Tolnay7d1d1282018-01-06 16:10:51 -0800443 impl Lit {
444 pub fn new(token: Literal, span: Span) -> Self {
445 let value = token.to_string();
446
447 match value::byte(&value, 0) {
448 b'"' | b'r' => {
449 return Lit::Str(LitStr {
450 token: token,
451 span: span,
452 })
453 }
454 b'b' => match value::byte(&value, 1) {
455 b'"' | b'r' => {
456 return Lit::ByteStr(LitByteStr {
457 token: token,
458 span: span,
459 })
460 }
461 b'\'' => {
462 return Lit::Byte(LitByte {
463 token: token,
464 span: span,
465 })
466 }
467 _ => {}
468 },
469 b'\'' => {
470 return Lit::Char(LitChar {
471 token: token,
472 span: span,
473 })
474 }
475 b'0'...b'9' => if number_is_int(&value) {
476 return Lit::Int(LitInt {
477 token: token,
478 span: span,
479 });
480 } else if number_is_float(&value) {
481 return Lit::Float(LitFloat {
482 token: token,
483 span: span,
484 });
485 } else {
486 // number overflow
487 return Lit::Verbatim(LitVerbatim {
488 token: token,
489 span: span,
490 });
491 },
492 _ => if value == "true" || value == "false" {
493 return Lit::Bool(LitBool {
494 value: value == "true",
495 span: span,
496 });
497 },
498 }
499
500 panic!("Unrecognized literal: {}", value);
501 }
502 }
503
504 fn number_is_int(value: &str) -> bool {
505 if number_is_float(value) {
506 false
507 } else {
508 value::parse_lit_int(value).is_some()
509 }
510 }
511
512 fn number_is_float(value: &str) -> bool {
513 if value.contains('.') {
514 true
515 } else if value.starts_with("0x") || value.ends_with("size") {
516 false
517 } else {
518 value.contains('e') || value.contains('E')
519 }
520 }
521
David Tolnay360efd22018-01-04 23:35:26 -0800522 /// Get the byte at offset idx, or a default of `b'\0'` if we're looking
523 /// past the end of the input buffer.
524 pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
525 let s = s.as_ref();
526 if idx < s.len() {
527 s[idx]
528 } else {
529 0
530 }
531 }
532
533 fn next_chr(s: &str) -> char {
534 s.chars().next().unwrap_or('\0')
535 }
536
537 pub fn parse_lit_str(s: &str) -> String {
538 match byte(s, 0) {
539 b'"' => parse_lit_str_cooked(s),
540 b'r' => parse_lit_str_raw(s),
541 _ => unreachable!(),
542 }
543 }
544
David Tolnay76ebcdd2018-01-05 17:07:26 -0800545 // Clippy false positive
546 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
547 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800548 fn parse_lit_str_cooked(mut s: &str) -> String {
549 assert_eq!(byte(s, 0), b'"');
550 s = &s[1..];
551
552 let mut out = String::new();
553 'outer: loop {
554 let ch = match byte(s, 0) {
555 b'"' => break,
556 b'\\' => {
557 let b = byte(s, 1);
558 s = &s[2..];
559 match b {
560 b'x' => {
561 let (byte, rest) = backslash_x(s);
562 s = rest;
563 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800564 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800565 }
566 b'u' => {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800567 let (chr, rest) = backslash_u(s);
David Tolnay360efd22018-01-04 23:35:26 -0800568 s = rest;
569 chr
570 }
571 b'n' => '\n',
572 b'r' => '\r',
573 b't' => '\t',
574 b'\\' => '\\',
575 b'0' => '\0',
576 b'\'' => '\'',
577 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800578 b'\r' | b'\n' => loop {
579 let ch = next_chr(s);
580 if ch.is_whitespace() {
581 s = &s[ch.len_utf8()..];
582 } else {
583 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800584 }
David Tolnay61037c62018-01-05 16:21:03 -0800585 },
586 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800587 }
588 }
589 b'\r' => {
590 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
591 s = &s[2..];
592 '\n'
593 }
594 _ => {
595 let ch = next_chr(s);
596 s = &s[ch.len_utf8()..];
597 ch
598 }
599 };
600 out.push(ch);
601 }
602
603 assert_eq!(s, "\"");
604 out
605 }
606
607 fn parse_lit_str_raw(mut s: &str) -> String {
608 assert_eq!(byte(s, 0), b'r');
609 s = &s[1..];
610
611 let mut pounds = 0;
612 while byte(s, pounds) == b'#' {
613 pounds += 1;
614 }
615 assert_eq!(byte(s, pounds), b'"');
616 assert_eq!(byte(s, s.len() - pounds - 1), b'"');
617 for end in s[s.len() - pounds..].bytes() {
618 assert_eq!(end, b'#');
619 }
620
621 s[pounds + 1..s.len() - pounds - 1].to_owned()
622 }
623
624 pub fn parse_lit_byte_str(s: &str) -> Vec<u8> {
625 assert_eq!(byte(s, 0), b'b');
626 match byte(s, 1) {
627 b'"' => parse_lit_byte_str_cooked(s),
628 b'r' => parse_lit_byte_str_raw(s),
629 _ => unreachable!(),
630 }
631 }
632
David Tolnay76ebcdd2018-01-05 17:07:26 -0800633 // Clippy false positive
634 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
635 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800636 fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> {
637 assert_eq!(byte(s, 0), b'b');
638 assert_eq!(byte(s, 1), b'"');
639 s = &s[2..];
640
641 // We're going to want to have slices which don't respect codepoint boundaries.
642 let mut s = s.as_bytes();
643
644 let mut out = Vec::new();
645 'outer: loop {
646 let byte = match byte(s, 0) {
647 b'"' => break,
648 b'\\' => {
649 let b = byte(s, 1);
650 s = &s[2..];
651 match b {
652 b'x' => {
653 let (b, rest) = backslash_x(s);
654 s = rest;
655 b
656 }
657 b'n' => b'\n',
658 b'r' => b'\r',
659 b't' => b'\t',
660 b'\\' => b'\\',
661 b'0' => b'\0',
662 b'\'' => b'\'',
663 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800664 b'\r' | b'\n' => loop {
665 let byte = byte(s, 0);
David Tolnay76ebcdd2018-01-05 17:07:26 -0800666 let ch = char::from_u32(u32::from(byte)).unwrap();
David Tolnay61037c62018-01-05 16:21:03 -0800667 if ch.is_whitespace() {
668 s = &s[1..];
669 } else {
670 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800671 }
David Tolnay61037c62018-01-05 16:21:03 -0800672 },
673 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800674 }
675 }
676 b'\r' => {
677 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
678 s = &s[2..];
679 b'\n'
680 }
681 b => {
682 s = &s[1..];
683 b
684 }
685 };
686 out.push(byte);
687 }
688
689 assert_eq!(s, b"\"");
690 out
691 }
692
693 fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> {
694 assert_eq!(byte(s, 0), b'b');
695 parse_lit_str_raw(&s[1..]).into_bytes()
696 }
697
698 pub fn parse_lit_byte(s: &str) -> u8 {
699 assert_eq!(byte(s, 0), b'b');
700 assert_eq!(byte(s, 1), b'\'');
701
702 // We're going to want to have slices which don't respect codepoint boundaries.
703 let mut s = s[2..].as_bytes();
704
705 let b = match byte(s, 0) {
706 b'\\' => {
707 let b = byte(s, 1);
708 s = &s[2..];
709 match b {
710 b'x' => {
711 let (b, rest) = backslash_x(s);
712 s = rest;
713 b
714 }
715 b'n' => b'\n',
716 b'r' => b'\r',
717 b't' => b'\t',
718 b'\\' => b'\\',
719 b'0' => b'\0',
720 b'\'' => b'\'',
721 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800722 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800723 }
724 }
725 b => {
726 s = &s[1..];
727 b
728 }
729 };
730
731 assert_eq!(byte(s, 0), b'\'');
732 b
733 }
734
735 pub fn parse_lit_char(mut s: &str) -> char {
736 assert_eq!(byte(s, 0), b'\'');
737 s = &s[1..];
738
739 let ch = match byte(s, 0) {
740 b'\\' => {
741 let b = byte(s, 1);
742 s = &s[2..];
743 match b {
744 b'x' => {
745 let (byte, rest) = backslash_x(s);
746 s = rest;
747 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800748 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800749 }
750 b'u' => {
751 let (chr, rest) = backslash_u(s);
752 s = rest;
753 chr
754 }
755 b'n' => '\n',
756 b'r' => '\r',
757 b't' => '\t',
758 b'\\' => '\\',
759 b'0' => '\0',
760 b'\'' => '\'',
761 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800762 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800763 }
764 }
765 _ => {
766 let ch = next_chr(s);
767 s = &s[ch.len_utf8()..];
768 ch
769 }
770 };
771 assert_eq!(s, "\'", "Expected end of char literal");
772 ch
773 }
774
775 fn backslash_x<S>(s: &S) -> (u8, &S)
David Tolnay61037c62018-01-05 16:21:03 -0800776 where
777 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
David Tolnay360efd22018-01-04 23:35:26 -0800778 {
779 let mut ch = 0;
780 let b0 = byte(s, 0);
781 let b1 = byte(s, 1);
782 ch += 0x10 * match b0 {
783 b'0'...b'9' => b0 - b'0',
784 b'a'...b'f' => 10 + (b0 - b'a'),
785 b'A'...b'F' => 10 + (b0 - b'A'),
786 _ => panic!("unexpected non-hex character after \\x"),
787 };
David Tolnay76ebcdd2018-01-05 17:07:26 -0800788 ch += match b1 {
David Tolnay360efd22018-01-04 23:35:26 -0800789 b'0'...b'9' => b1 - b'0',
790 b'a'...b'f' => 10 + (b1 - b'a'),
791 b'A'...b'F' => 10 + (b1 - b'A'),
792 _ => panic!("unexpected non-hex character after \\x"),
793 };
794 (ch, &s[2..])
795 }
796
797 fn backslash_u(mut s: &str) -> (char, &str) {
798 if byte(s, 0) != b'{' {
799 panic!("expected {{ after \\u");
800 }
801 s = &s[1..];
802
803 let mut ch = 0;
804 for _ in 0..6 {
805 let b = byte(s, 0);
806 match b {
807 b'0'...b'9' => {
808 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800809 ch += u32::from(b - b'0');
David Tolnay360efd22018-01-04 23:35:26 -0800810 s = &s[1..];
811 }
812 b'a'...b'f' => {
813 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800814 ch += u32::from(10 + b - b'a');
David Tolnay360efd22018-01-04 23:35:26 -0800815 s = &s[1..];
816 }
817 b'A'...b'F' => {
818 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800819 ch += u32::from(10 + b - b'A');
David Tolnay360efd22018-01-04 23:35:26 -0800820 s = &s[1..];
821 }
822 b'}' => break,
823 _ => panic!("unexpected non-hex character after \\u"),
824 }
825 }
826 assert!(byte(s, 0) == b'}');
827 s = &s[1..];
828
829 if let Some(ch) = char::from_u32(ch) {
830 (ch, s)
831 } else {
832 panic!("character code {:x} is not a valid unicode character", ch);
833 }
834 }
835
836 pub fn parse_lit_int(mut s: &str) -> Option<u64> {
837 let base = match (byte(s, 0), byte(s, 1)) {
838 (b'0', b'x') => {
839 s = &s[2..];
840 16
841 }
842 (b'0', b'o') => {
843 s = &s[2..];
844 8
845 }
846 (b'0', b'b') => {
847 s = &s[2..];
848 2
849 }
850 (b'0'...b'9', _) => 10,
851 _ => unreachable!(),
852 };
853
854 let mut value = 0u64;
855 loop {
856 let b = byte(s, 0);
857 let digit = match b {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800858 b'0'...b'9' => u64::from(b - b'0'),
859 b'a'...b'f' if base > 10 => 10 + u64::from(b - b'a'),
860 b'A'...b'F' if base > 10 => 10 + u64::from(b - b'A'),
David Tolnay360efd22018-01-04 23:35:26 -0800861 b'_' => {
862 s = &s[1..];
863 continue;
864 }
865 // NOTE: Looking at a floating point literal, we don't want to
866 // consider these integers.
867 b'.' if base == 10 => return None,
868 b'e' | b'E' if base == 10 => return None,
869 _ => break,
870 };
871
872 if digit >= base {
873 panic!("Unexpected digit {:x} out of base range", digit);
874 }
875
876 value = match value.checked_mul(base) {
877 Some(value) => value,
878 None => return None,
879 };
880 value = match value.checked_add(digit) {
881 Some(value) => value,
882 None => return None,
883 };
884 s = &s[1..];
885 }
886
887 Some(value)
888 }
889
890 pub fn parse_lit_float(input: &str) -> f64 {
891 // Rust's floating point literals are very similar to the ones parsed by
892 // the standard library, except that rust's literals can contain
893 // ignorable underscores. Let's remove those underscores.
894 let mut bytes = input.to_owned().into_bytes();
895 let mut write = 0;
896 for read in 0..bytes.len() {
897 if bytes[read] == b'_' {
898 continue; // Don't increase write
David Tolnay76ebcdd2018-01-05 17:07:26 -0800899 }
900 if write != read {
David Tolnay360efd22018-01-04 23:35:26 -0800901 let x = bytes[read];
902 bytes[write] = x;
903 }
904 write += 1;
905 }
906 bytes.truncate(write);
907 let input = String::from_utf8(bytes).unwrap();
David Tolnay76ebcdd2018-01-05 17:07:26 -0800908 let end = input.find('f').unwrap_or_else(|| input.len());
David Tolnay360efd22018-01-04 23:35:26 -0800909 input[..end].parse().unwrap()
910 }
911
912 pub fn to_literal(s: &str) -> Literal {
913 let stream = s.parse::<TokenStream>().unwrap();
914 match stream.into_iter().next().unwrap().kind {
915 TokenNode::Literal(l) => l,
916 _ => unreachable!(),
David Tolnayf17fd2f2016-10-07 23:38:08 -0700917 }
918 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700919}