blob: d38c58d185efeebbbdc5f7dc963c26ff58aeeb35 [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! {
David Tolnayabf5c2e2018-01-06 23:30:04 -080019 /// A Rust literal such as a string or integer or boolean.
David Tolnay360efd22018-01-04 23:35:26 -080020 pub enum Lit {
David Tolnayabf5c2e2018-01-06 23:30:04 -080021 /// A UTF-8 string literal: `"foo"`.
David Tolnay360efd22018-01-04 23:35:26 -080022 pub Str(LitStr #manual_extra_traits {
23 token: Literal,
24 pub span: Span,
25 }),
Alex Crichtonccbb45d2017-05-23 10:58:24 -070026
David Tolnayabf5c2e2018-01-06 23:30:04 -080027 /// A byte string literal: `b"foo"`.
David Tolnay360efd22018-01-04 23:35:26 -080028 pub ByteStr(LitByteStr #manual_extra_traits {
29 token: Literal,
30 pub span: Span,
31 }),
32
David Tolnayabf5c2e2018-01-06 23:30:04 -080033 /// A byte literal: `b'f'`.
David Tolnay360efd22018-01-04 23:35:26 -080034 pub Byte(LitByte #manual_extra_traits {
35 token: Literal,
36 pub span: Span,
37 }),
38
David Tolnayabf5c2e2018-01-06 23:30:04 -080039 /// A character literal: `'a'`.
David Tolnay360efd22018-01-04 23:35:26 -080040 pub Char(LitChar #manual_extra_traits {
41 token: Literal,
42 pub span: Span,
43 }),
44
David Tolnayabf5c2e2018-01-06 23:30:04 -080045 /// An integer literal: `1` or `1u16`.
David Tolnay360efd22018-01-04 23:35:26 -080046 ///
47 /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger
48 /// integer literal.
49 pub Int(LitInt #manual_extra_traits {
50 token: Literal,
51 pub span: Span,
52 }),
53
David Tolnayabf5c2e2018-01-06 23:30:04 -080054 /// A floating point literal: `1f64` or `1.0e10f64`.
David Tolnay360efd22018-01-04 23:35:26 -080055 ///
56 /// Must be finite. May not be infinte or NaN.
57 pub Float(LitFloat #manual_extra_traits {
58 token: Literal,
59 pub span: Span,
60 }),
61
David Tolnayabf5c2e2018-01-06 23:30:04 -080062 /// A boolean literal: `true` or `false`.
David Tolnay360efd22018-01-04 23:35:26 -080063 pub Bool(LitBool #manual_extra_traits {
64 pub value: bool,
65 pub span: Span,
66 }),
67
David Tolnayabf5c2e2018-01-06 23:30:04 -080068 /// A raw token literal not interpreted by Syn, possibly because it
69 /// represents an integer larger than 64 bits.
David Tolnay360efd22018-01-04 23:35:26 -080070 pub Verbatim(LitVerbatim #manual_extra_traits {
71 pub token: Literal,
72 pub span: Span,
73 }),
74 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -070075}
76
David Tolnay360efd22018-01-04 23:35:26 -080077impl LitStr {
78 pub fn new(value: &str, span: Span) -> Self {
79 LitStr {
80 token: Literal::string(value),
81 span: span,
82 }
83 }
84
85 pub fn value(&self) -> String {
86 value::parse_lit_str(&self.token.to_string())
87 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -070088}
89
David Tolnay360efd22018-01-04 23:35:26 -080090impl LitByteStr {
91 pub fn new(value: &[u8], span: Span) -> Self {
92 LitByteStr {
93 token: Literal::byte_string(value),
94 span: span,
95 }
96 }
97
98 pub fn value(&self) -> Vec<u8> {
99 value::parse_lit_byte_str(&self.token.to_string())
100 }
101}
102
103impl LitByte {
104 pub fn new(value: u8, span: Span) -> Self {
105 LitByte {
106 token: Literal::byte_char(value),
107 span: span,
108 }
109 }
110
111 pub fn value(&self) -> u8 {
112 value::parse_lit_byte(&self.token.to_string())
113 }
114}
115
116impl LitChar {
117 pub fn new(value: char, span: Span) -> Self {
118 LitChar {
119 token: Literal::character(value),
120 span: span,
121 }
122 }
123
124 pub fn value(&self) -> char {
125 value::parse_lit_char(&self.token.to_string())
126 }
127}
128
129impl LitInt {
130 pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self {
131 LitInt {
132 token: match suffix {
133 IntSuffix::Isize => Literal::isize(value as isize),
134 IntSuffix::I8 => Literal::i8(value as i8),
135 IntSuffix::I16 => Literal::i16(value as i16),
136 IntSuffix::I32 => Literal::i32(value as i32),
137 IntSuffix::I64 => Literal::i64(value as i64),
138 IntSuffix::I128 => value::to_literal(&format!("{}i128", value)),
139 IntSuffix::Usize => Literal::usize(value as usize),
140 IntSuffix::U8 => Literal::u8(value as u8),
141 IntSuffix::U16 => Literal::u16(value as u16),
142 IntSuffix::U32 => Literal::u32(value as u32),
143 IntSuffix::U64 => Literal::u64(value),
144 IntSuffix::U128 => value::to_literal(&format!("{}u128", value)),
145 IntSuffix::None => Literal::integer(value as i64),
146 },
147 span: span,
148 }
149 }
150
151 pub fn value(&self) -> u64 {
152 value::parse_lit_int(&self.token.to_string()).unwrap()
153 }
154
155 pub fn suffix(&self) -> IntSuffix {
156 let value = self.token.to_string();
157 for (s, suffix) in vec![
158 ("i8", IntSuffix::I8),
159 ("i16", IntSuffix::I16),
160 ("i32", IntSuffix::I32),
161 ("i64", IntSuffix::I64),
162 ("i128", IntSuffix::I128),
163 ("isize", IntSuffix::Isize),
164 ("u8", IntSuffix::U8),
165 ("u16", IntSuffix::U16),
166 ("u32", IntSuffix::U32),
167 ("u64", IntSuffix::U64),
168 ("u128", IntSuffix::U128),
169 ("usize", IntSuffix::Usize),
170 ] {
171 if value.ends_with(s) {
172 return suffix;
173 }
174 }
175 IntSuffix::None
176 }
177}
178
179impl LitFloat {
180 pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self {
181 LitFloat {
182 token: match suffix {
183 FloatSuffix::F32 => Literal::f32(value as f32),
184 FloatSuffix::F64 => Literal::f64(value),
185 FloatSuffix::None => Literal::float(value),
186 },
187 span: span,
188 }
189 }
190
191 pub fn value(&self) -> f64 {
192 value::parse_lit_float(&self.token.to_string())
193 }
194
195 pub fn suffix(&self) -> FloatSuffix {
196 let value = self.token.to_string();
David Tolnay61037c62018-01-05 16:21:03 -0800197 for (s, suffix) in vec![("f32", FloatSuffix::F32), ("f64", FloatSuffix::F64)] {
David Tolnay360efd22018-01-04 23:35:26 -0800198 if value.ends_with(s) {
199 return suffix;
200 }
201 }
202 FloatSuffix::None
203 }
204}
205
206macro_rules! lit_extra_traits {
207 ($ty:ident, $field:ident) => {
208 #[cfg(feature = "extra-traits")]
209 impl Eq for $ty {}
210
211 #[cfg(feature = "extra-traits")]
212 impl PartialEq for $ty {
213 fn eq(&self, other: &Self) -> bool {
214 self.$field.to_string() == other.$field.to_string()
215 }
216 }
217
218 #[cfg(feature = "extra-traits")]
219 impl Hash for $ty {
220 fn hash<H>(&self, state: &mut H)
221 where
222 H: Hasher,
223 {
224 self.$field.to_string().hash(state);
225 }
David Tolnay9c76bcb2017-12-26 23:14:59 -0500226 }
Alex Crichton62a0a592017-05-22 13:58:53 -0700227 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700228}
229
David Tolnay360efd22018-01-04 23:35:26 -0800230lit_extra_traits!(LitStr, token);
231lit_extra_traits!(LitByteStr, token);
232lit_extra_traits!(LitByte, token);
233lit_extra_traits!(LitChar, token);
234lit_extra_traits!(LitInt, token);
235lit_extra_traits!(LitFloat, token);
236lit_extra_traits!(LitBool, value);
237lit_extra_traits!(LitVerbatim, token);
238
239ast_enum! {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800240 /// The style of a string literal, either plain or a raw string like
241 /// `r##"data"##`.
David Tolnay360efd22018-01-04 23:35:26 -0800242 pub enum StrStyle #no_visit {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800243 /// An ordinary string like `"data"`.
David Tolnay360efd22018-01-04 23:35:26 -0800244 Cooked,
David Tolnayabf5c2e2018-01-06 23:30:04 -0800245 /// A raw string like `r##"data"##`.
David Tolnay360efd22018-01-04 23:35:26 -0800246 ///
247 /// The unsigned integer is the number of `#` symbols used.
248 Raw(usize),
Alex Crichton62a0a592017-05-22 13:58:53 -0700249 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700250}
251
David Tolnay360efd22018-01-04 23:35:26 -0800252ast_enum! {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800253 /// The suffix on an integer literal if any, like the `u8` in `127u8`.
David Tolnay360efd22018-01-04 23:35:26 -0800254 pub enum IntSuffix #no_visit {
255 I8,
256 I16,
257 I32,
258 I64,
259 I128,
260 Isize,
261 U8,
262 U16,
263 U32,
264 U64,
265 U128,
266 Usize,
267 None,
Pascal Hertleif36342c52016-10-19 10:31:42 +0200268 }
269}
270
David Tolnay360efd22018-01-04 23:35:26 -0800271ast_enum! {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800272 /// The suffix on a floating point literal if any, like the `f32` in
273 /// `1.0f32`.
David Tolnay360efd22018-01-04 23:35:26 -0800274 pub enum FloatSuffix #no_visit {
275 F32,
276 F64,
277 None,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700278 }
David Tolnay5fe14fc2017-01-27 16:22:08 -0800279}
280
281#[cfg(feature = "parsing")]
David Tolnayf4bbbd92016-09-23 14:41:55 -0700282pub mod parsing {
283 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500284 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800285 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500286 use parse_error;
287 use synom::PResult;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700288
Alex Crichton954046c2017-05-30 21:49:42 -0700289 impl Synom for Lit {
Michael Layzell92639a52017-06-01 00:07:44 -0400290 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400291 match input.literal() {
David Tolnay360efd22018-01-04 23:35:26 -0800292 Some((span, lit, rest)) => Ok((Lit::new(lit, span), rest)),
David Tolnay73c98de2017-12-31 15:56:56 -0500293 _ => match input.term() {
David Tolnay360efd22018-01-04 23:35:26 -0800294 Some((span, term, rest)) => Ok((
295 Lit::Bool(LitBool {
296 value: if term.as_str() == "true" {
297 true
298 } else if term.as_str() == "false" {
299 false
300 } else {
301 return parse_error();
David Tolnay51382052017-12-27 13:46:21 -0500302 },
David Tolnay360efd22018-01-04 23:35:26 -0800303 span: span,
304 }),
305 rest,
306 )),
Michael Layzell589a8f42017-06-02 19:47:01 -0400307 _ => parse_error(),
David Tolnay51382052017-12-27 13:46:21 -0500308 },
Michael Layzell589a8f42017-06-02 19:47:01 -0400309 }
David Tolnayfa0edf22016-09-23 22:58:24 -0700310 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800311
312 fn description() -> Option<&'static str> {
313 Some("literal")
314 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700315 }
David Tolnay360efd22018-01-04 23:35:26 -0800316
317 impl_synom!(LitStr "string literal" switch!(
318 syn!(Lit),
319 Lit::Str(lit) => value!(lit)
320 |
321 _ => reject!()
322 ));
323
324 impl_synom!(LitByteStr "byte string literal" switch!(
325 syn!(Lit),
326 Lit::ByteStr(lit) => value!(lit)
327 |
328 _ => reject!()
329 ));
330
331 impl_synom!(LitByte "byte literal" switch!(
332 syn!(Lit),
333 Lit::Byte(lit) => value!(lit)
334 |
335 _ => reject!()
336 ));
337
338 impl_synom!(LitChar "character literal" switch!(
339 syn!(Lit),
340 Lit::Char(lit) => value!(lit)
341 |
342 _ => reject!()
343 ));
344
345 impl_synom!(LitInt "integer literal" switch!(
346 syn!(Lit),
347 Lit::Int(lit) => value!(lit)
348 |
349 _ => reject!()
350 ));
351
352 impl_synom!(LitFloat "floating point literal" switch!(
353 syn!(Lit),
354 Lit::Float(lit) => value!(lit)
355 |
356 _ => reject!()
357 ));
358
359 impl_synom!(LitBool "boolean literal" switch!(
360 syn!(Lit),
361 Lit::Bool(lit) => value!(lit)
362 |
363 _ => reject!()
364 ));
David Tolnayf4bbbd92016-09-23 14:41:55 -0700365}
366
367#[cfg(feature = "printing")]
368mod printing {
369 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500370 use quote::{ToTokens, Tokens};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700371
David Tolnay360efd22018-01-04 23:35:26 -0800372 impl ToTokens for LitStr {
David Tolnayf4bbbd92016-09-23 14:41:55 -0700373 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnay360efd22018-01-04 23:35:26 -0800374 tokens.append(TokenTree {
375 span: self.span,
376 kind: TokenNode::Literal(self.token.clone()),
377 });
378 }
379 }
380
381 impl ToTokens for LitByteStr {
382 fn to_tokens(&self, tokens: &mut Tokens) {
383 tokens.append(TokenTree {
384 span: self.span,
385 kind: TokenNode::Literal(self.token.clone()),
386 });
387 }
388 }
389
390 impl ToTokens for LitByte {
391 fn to_tokens(&self, tokens: &mut Tokens) {
392 tokens.append(TokenTree {
393 span: self.span,
394 kind: TokenNode::Literal(self.token.clone()),
395 });
396 }
397 }
398
399 impl ToTokens for LitChar {
400 fn to_tokens(&self, tokens: &mut Tokens) {
401 tokens.append(TokenTree {
402 span: self.span,
403 kind: TokenNode::Literal(self.token.clone()),
404 });
405 }
406 }
407
408 impl ToTokens for LitInt {
409 fn to_tokens(&self, tokens: &mut Tokens) {
410 tokens.append(TokenTree {
411 span: self.span,
412 kind: TokenNode::Literal(self.token.clone()),
413 });
414 }
415 }
416
417 impl ToTokens for LitFloat {
418 fn to_tokens(&self, tokens: &mut Tokens) {
419 tokens.append(TokenTree {
420 span: self.span,
421 kind: TokenNode::Literal(self.token.clone()),
422 });
423 }
424 }
425
426 impl ToTokens for LitBool {
427 fn to_tokens(&self, tokens: &mut Tokens) {
428 tokens.append(TokenTree {
429 span: self.span,
David Tolnay61037c62018-01-05 16:21:03 -0800430 kind: TokenNode::Term(Term::intern(if self.value { "true" } else { "false" })),
David Tolnay360efd22018-01-04 23:35:26 -0800431 });
432 }
433 }
434
435 impl ToTokens for LitVerbatim {
436 fn to_tokens(&self, tokens: &mut Tokens) {
437 tokens.append(TokenTree {
438 span: self.span,
439 kind: TokenNode::Literal(self.token.clone()),
440 });
441 }
442 }
443}
444
445mod value {
446 use super::*;
447 use std::char;
448 use std::ops::{Index, RangeFrom};
449 use proc_macro2::TokenStream;
450
David Tolnay7d1d1282018-01-06 16:10:51 -0800451 impl Lit {
452 pub fn new(token: Literal, span: Span) -> Self {
453 let value = token.to_string();
454
455 match value::byte(&value, 0) {
456 b'"' | b'r' => {
457 return Lit::Str(LitStr {
458 token: token,
459 span: span,
460 })
461 }
462 b'b' => match value::byte(&value, 1) {
463 b'"' | b'r' => {
464 return Lit::ByteStr(LitByteStr {
465 token: token,
466 span: span,
467 })
468 }
469 b'\'' => {
470 return Lit::Byte(LitByte {
471 token: token,
472 span: span,
473 })
474 }
475 _ => {}
476 },
477 b'\'' => {
478 return Lit::Char(LitChar {
479 token: token,
480 span: span,
481 })
482 }
483 b'0'...b'9' => if number_is_int(&value) {
484 return Lit::Int(LitInt {
485 token: token,
486 span: span,
487 });
488 } else if number_is_float(&value) {
489 return Lit::Float(LitFloat {
490 token: token,
491 span: span,
492 });
493 } else {
494 // number overflow
495 return Lit::Verbatim(LitVerbatim {
496 token: token,
497 span: span,
498 });
499 },
500 _ => if value == "true" || value == "false" {
501 return Lit::Bool(LitBool {
502 value: value == "true",
503 span: span,
504 });
505 },
506 }
507
508 panic!("Unrecognized literal: {}", value);
509 }
510 }
511
512 fn number_is_int(value: &str) -> bool {
513 if number_is_float(value) {
514 false
515 } else {
516 value::parse_lit_int(value).is_some()
517 }
518 }
519
520 fn number_is_float(value: &str) -> bool {
521 if value.contains('.') {
522 true
523 } else if value.starts_with("0x") || value.ends_with("size") {
524 false
525 } else {
526 value.contains('e') || value.contains('E')
527 }
528 }
529
David Tolnay360efd22018-01-04 23:35:26 -0800530 /// Get the byte at offset idx, or a default of `b'\0'` if we're looking
531 /// past the end of the input buffer.
532 pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
533 let s = s.as_ref();
534 if idx < s.len() {
535 s[idx]
536 } else {
537 0
538 }
539 }
540
541 fn next_chr(s: &str) -> char {
542 s.chars().next().unwrap_or('\0')
543 }
544
545 pub fn parse_lit_str(s: &str) -> String {
546 match byte(s, 0) {
547 b'"' => parse_lit_str_cooked(s),
548 b'r' => parse_lit_str_raw(s),
549 _ => unreachable!(),
550 }
551 }
552
David Tolnay76ebcdd2018-01-05 17:07:26 -0800553 // Clippy false positive
554 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
555 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800556 fn parse_lit_str_cooked(mut s: &str) -> String {
557 assert_eq!(byte(s, 0), b'"');
558 s = &s[1..];
559
560 let mut out = String::new();
561 'outer: loop {
562 let ch = match byte(s, 0) {
563 b'"' => break,
564 b'\\' => {
565 let b = byte(s, 1);
566 s = &s[2..];
567 match b {
568 b'x' => {
569 let (byte, rest) = backslash_x(s);
570 s = rest;
571 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800572 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800573 }
574 b'u' => {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800575 let (chr, rest) = backslash_u(s);
David Tolnay360efd22018-01-04 23:35:26 -0800576 s = rest;
577 chr
578 }
579 b'n' => '\n',
580 b'r' => '\r',
581 b't' => '\t',
582 b'\\' => '\\',
583 b'0' => '\0',
584 b'\'' => '\'',
585 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800586 b'\r' | b'\n' => loop {
587 let ch = next_chr(s);
588 if ch.is_whitespace() {
589 s = &s[ch.len_utf8()..];
590 } else {
591 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800592 }
David Tolnay61037c62018-01-05 16:21:03 -0800593 },
594 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800595 }
596 }
597 b'\r' => {
598 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
599 s = &s[2..];
600 '\n'
601 }
602 _ => {
603 let ch = next_chr(s);
604 s = &s[ch.len_utf8()..];
605 ch
606 }
607 };
608 out.push(ch);
609 }
610
611 assert_eq!(s, "\"");
612 out
613 }
614
615 fn parse_lit_str_raw(mut s: &str) -> String {
616 assert_eq!(byte(s, 0), b'r');
617 s = &s[1..];
618
619 let mut pounds = 0;
620 while byte(s, pounds) == b'#' {
621 pounds += 1;
622 }
623 assert_eq!(byte(s, pounds), b'"');
624 assert_eq!(byte(s, s.len() - pounds - 1), b'"');
625 for end in s[s.len() - pounds..].bytes() {
626 assert_eq!(end, b'#');
627 }
628
629 s[pounds + 1..s.len() - pounds - 1].to_owned()
630 }
631
632 pub fn parse_lit_byte_str(s: &str) -> Vec<u8> {
633 assert_eq!(byte(s, 0), b'b');
634 match byte(s, 1) {
635 b'"' => parse_lit_byte_str_cooked(s),
636 b'r' => parse_lit_byte_str_raw(s),
637 _ => unreachable!(),
638 }
639 }
640
David Tolnay76ebcdd2018-01-05 17:07:26 -0800641 // Clippy false positive
642 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
643 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800644 fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> {
645 assert_eq!(byte(s, 0), b'b');
646 assert_eq!(byte(s, 1), b'"');
647 s = &s[2..];
648
649 // We're going to want to have slices which don't respect codepoint boundaries.
650 let mut s = s.as_bytes();
651
652 let mut out = Vec::new();
653 'outer: loop {
654 let byte = match byte(s, 0) {
655 b'"' => break,
656 b'\\' => {
657 let b = byte(s, 1);
658 s = &s[2..];
659 match b {
660 b'x' => {
661 let (b, rest) = backslash_x(s);
662 s = rest;
663 b
664 }
665 b'n' => b'\n',
666 b'r' => b'\r',
667 b't' => b'\t',
668 b'\\' => b'\\',
669 b'0' => b'\0',
670 b'\'' => b'\'',
671 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800672 b'\r' | b'\n' => loop {
673 let byte = byte(s, 0);
David Tolnay76ebcdd2018-01-05 17:07:26 -0800674 let ch = char::from_u32(u32::from(byte)).unwrap();
David Tolnay61037c62018-01-05 16:21:03 -0800675 if ch.is_whitespace() {
676 s = &s[1..];
677 } else {
678 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800679 }
David Tolnay61037c62018-01-05 16:21:03 -0800680 },
681 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800682 }
683 }
684 b'\r' => {
685 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
686 s = &s[2..];
687 b'\n'
688 }
689 b => {
690 s = &s[1..];
691 b
692 }
693 };
694 out.push(byte);
695 }
696
697 assert_eq!(s, b"\"");
698 out
699 }
700
701 fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> {
702 assert_eq!(byte(s, 0), b'b');
703 parse_lit_str_raw(&s[1..]).into_bytes()
704 }
705
706 pub fn parse_lit_byte(s: &str) -> u8 {
707 assert_eq!(byte(s, 0), b'b');
708 assert_eq!(byte(s, 1), b'\'');
709
710 // We're going to want to have slices which don't respect codepoint boundaries.
711 let mut s = s[2..].as_bytes();
712
713 let b = match byte(s, 0) {
714 b'\\' => {
715 let b = byte(s, 1);
716 s = &s[2..];
717 match b {
718 b'x' => {
719 let (b, rest) = backslash_x(s);
720 s = rest;
721 b
722 }
723 b'n' => b'\n',
724 b'r' => b'\r',
725 b't' => b'\t',
726 b'\\' => b'\\',
727 b'0' => b'\0',
728 b'\'' => b'\'',
729 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800730 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800731 }
732 }
733 b => {
734 s = &s[1..];
735 b
736 }
737 };
738
739 assert_eq!(byte(s, 0), b'\'');
740 b
741 }
742
743 pub fn parse_lit_char(mut s: &str) -> char {
744 assert_eq!(byte(s, 0), b'\'');
745 s = &s[1..];
746
747 let ch = match byte(s, 0) {
748 b'\\' => {
749 let b = byte(s, 1);
750 s = &s[2..];
751 match b {
752 b'x' => {
753 let (byte, rest) = backslash_x(s);
754 s = rest;
755 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800756 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800757 }
758 b'u' => {
759 let (chr, rest) = backslash_u(s);
760 s = rest;
761 chr
762 }
763 b'n' => '\n',
764 b'r' => '\r',
765 b't' => '\t',
766 b'\\' => '\\',
767 b'0' => '\0',
768 b'\'' => '\'',
769 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800770 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800771 }
772 }
773 _ => {
774 let ch = next_chr(s);
775 s = &s[ch.len_utf8()..];
776 ch
777 }
778 };
779 assert_eq!(s, "\'", "Expected end of char literal");
780 ch
781 }
782
783 fn backslash_x<S>(s: &S) -> (u8, &S)
David Tolnay61037c62018-01-05 16:21:03 -0800784 where
785 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
David Tolnay360efd22018-01-04 23:35:26 -0800786 {
787 let mut ch = 0;
788 let b0 = byte(s, 0);
789 let b1 = byte(s, 1);
790 ch += 0x10 * match b0 {
791 b'0'...b'9' => b0 - b'0',
792 b'a'...b'f' => 10 + (b0 - b'a'),
793 b'A'...b'F' => 10 + (b0 - b'A'),
794 _ => panic!("unexpected non-hex character after \\x"),
795 };
David Tolnay76ebcdd2018-01-05 17:07:26 -0800796 ch += match b1 {
David Tolnay360efd22018-01-04 23:35:26 -0800797 b'0'...b'9' => b1 - b'0',
798 b'a'...b'f' => 10 + (b1 - b'a'),
799 b'A'...b'F' => 10 + (b1 - b'A'),
800 _ => panic!("unexpected non-hex character after \\x"),
801 };
802 (ch, &s[2..])
803 }
804
805 fn backslash_u(mut s: &str) -> (char, &str) {
806 if byte(s, 0) != b'{' {
807 panic!("expected {{ after \\u");
808 }
809 s = &s[1..];
810
811 let mut ch = 0;
812 for _ in 0..6 {
813 let b = byte(s, 0);
814 match b {
815 b'0'...b'9' => {
816 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800817 ch += u32::from(b - b'0');
David Tolnay360efd22018-01-04 23:35:26 -0800818 s = &s[1..];
819 }
820 b'a'...b'f' => {
821 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800822 ch += u32::from(10 + b - b'a');
David Tolnay360efd22018-01-04 23:35:26 -0800823 s = &s[1..];
824 }
825 b'A'...b'F' => {
826 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800827 ch += u32::from(10 + b - b'A');
David Tolnay360efd22018-01-04 23:35:26 -0800828 s = &s[1..];
829 }
830 b'}' => break,
831 _ => panic!("unexpected non-hex character after \\u"),
832 }
833 }
834 assert!(byte(s, 0) == b'}');
835 s = &s[1..];
836
837 if let Some(ch) = char::from_u32(ch) {
838 (ch, s)
839 } else {
840 panic!("character code {:x} is not a valid unicode character", ch);
841 }
842 }
843
844 pub fn parse_lit_int(mut s: &str) -> Option<u64> {
845 let base = match (byte(s, 0), byte(s, 1)) {
846 (b'0', b'x') => {
847 s = &s[2..];
848 16
849 }
850 (b'0', b'o') => {
851 s = &s[2..];
852 8
853 }
854 (b'0', b'b') => {
855 s = &s[2..];
856 2
857 }
858 (b'0'...b'9', _) => 10,
859 _ => unreachable!(),
860 };
861
862 let mut value = 0u64;
863 loop {
864 let b = byte(s, 0);
865 let digit = match b {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800866 b'0'...b'9' => u64::from(b - b'0'),
867 b'a'...b'f' if base > 10 => 10 + u64::from(b - b'a'),
868 b'A'...b'F' if base > 10 => 10 + u64::from(b - b'A'),
David Tolnay360efd22018-01-04 23:35:26 -0800869 b'_' => {
870 s = &s[1..];
871 continue;
872 }
873 // NOTE: Looking at a floating point literal, we don't want to
874 // consider these integers.
875 b'.' if base == 10 => return None,
876 b'e' | b'E' if base == 10 => return None,
877 _ => break,
878 };
879
880 if digit >= base {
881 panic!("Unexpected digit {:x} out of base range", digit);
882 }
883
884 value = match value.checked_mul(base) {
885 Some(value) => value,
886 None => return None,
887 };
888 value = match value.checked_add(digit) {
889 Some(value) => value,
890 None => return None,
891 };
892 s = &s[1..];
893 }
894
895 Some(value)
896 }
897
898 pub fn parse_lit_float(input: &str) -> f64 {
899 // Rust's floating point literals are very similar to the ones parsed by
900 // the standard library, except that rust's literals can contain
901 // ignorable underscores. Let's remove those underscores.
902 let mut bytes = input.to_owned().into_bytes();
903 let mut write = 0;
904 for read in 0..bytes.len() {
905 if bytes[read] == b'_' {
906 continue; // Don't increase write
David Tolnay76ebcdd2018-01-05 17:07:26 -0800907 }
908 if write != read {
David Tolnay360efd22018-01-04 23:35:26 -0800909 let x = bytes[read];
910 bytes[write] = x;
911 }
912 write += 1;
913 }
914 bytes.truncate(write);
915 let input = String::from_utf8(bytes).unwrap();
David Tolnay76ebcdd2018-01-05 17:07:26 -0800916 let end = input.find('f').unwrap_or_else(|| input.len());
David Tolnay360efd22018-01-04 23:35:26 -0800917 input[..end].parse().unwrap()
918 }
919
920 pub fn to_literal(s: &str) -> Literal {
921 let stream = s.parse::<TokenStream>().unwrap();
922 match stream.into_iter().next().unwrap().kind {
923 TokenNode::Literal(l) => l,
924 _ => unreachable!(),
David Tolnayf17fd2f2016-10-07 23:38:08 -0700925 }
926 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700927}