blob: 4234d0b4935a1f13f7fff2edffb7ec9d9389384b [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")]
David Tolnay60fafad2018-02-03 09:18:03 -080013use proc_macro2::Term;
David Tolnay7d1d1282018-01-06 16:10:51 -080014
David Tolnayd53ac2b2018-01-27 19:00:06 -080015#[cfg(feature = "parsing")]
16use proc_macro2::TokenStream;
17#[cfg(feature = "parsing")]
18use {ParseError, Synom};
19
David Tolnay60fafad2018-02-03 09:18:03 -080020#[cfg(any(feature = "printing", feature = "parsing"))]
21use proc_macro2::TokenTree;
22
David Tolnay360efd22018-01-04 23:35:26 -080023#[cfg(feature = "extra-traits")]
Alex Crichtonccbb45d2017-05-23 10:58:24 -070024use std::hash::{Hash, Hasher};
25
David Tolnay360efd22018-01-04 23:35:26 -080026ast_enum_of_structs! {
David Tolnayabf5c2e2018-01-06 23:30:04 -080027 /// A Rust literal such as a string or integer or boolean.
David Tolnay614a0142018-01-07 10:25:43 -080028 ///
David Tolnay461d98e2018-01-07 11:07:19 -080029 /// *This type is available if Syn is built with the `"derive"` or `"full"`
30 /// feature.*
31 ///
David Tolnay614a0142018-01-07 10:25:43 -080032 /// # Syntax tree enum
33 ///
34 /// This type is a [syntax tree enum].
35 ///
36 /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
David Tolnay360efd22018-01-04 23:35:26 -080037 pub enum Lit {
David Tolnayabf5c2e2018-01-06 23:30:04 -080038 /// A UTF-8 string literal: `"foo"`.
David Tolnay461d98e2018-01-07 11:07:19 -080039 ///
40 /// *This type is available if Syn is built with the `"derive"` or
41 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080042 pub Str(LitStr #manual_extra_traits {
43 token: Literal,
44 pub span: Span,
45 }),
Alex Crichtonccbb45d2017-05-23 10:58:24 -070046
David Tolnayabf5c2e2018-01-06 23:30:04 -080047 /// A byte string literal: `b"foo"`.
David Tolnay461d98e2018-01-07 11:07:19 -080048 ///
49 /// *This type is available if Syn is built with the `"derive"` or
50 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080051 pub ByteStr(LitByteStr #manual_extra_traits {
52 token: Literal,
53 pub span: Span,
54 }),
55
David Tolnayabf5c2e2018-01-06 23:30:04 -080056 /// A byte literal: `b'f'`.
David Tolnay461d98e2018-01-07 11:07:19 -080057 ///
58 /// *This type is available if Syn is built with the `"derive"` or
59 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080060 pub Byte(LitByte #manual_extra_traits {
61 token: Literal,
62 pub span: Span,
63 }),
64
David Tolnayabf5c2e2018-01-06 23:30:04 -080065 /// A character literal: `'a'`.
David Tolnay461d98e2018-01-07 11:07:19 -080066 ///
67 /// *This type is available if Syn is built with the `"derive"` or
68 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080069 pub Char(LitChar #manual_extra_traits {
70 token: Literal,
71 pub span: Span,
72 }),
73
David Tolnayabf5c2e2018-01-06 23:30:04 -080074 /// An integer literal: `1` or `1u16`.
David Tolnay360efd22018-01-04 23:35:26 -080075 ///
76 /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger
77 /// integer literal.
David Tolnay461d98e2018-01-07 11:07:19 -080078 ///
79 /// *This type is available if Syn is built with the `"derive"` or
80 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080081 pub Int(LitInt #manual_extra_traits {
82 token: Literal,
83 pub span: Span,
84 }),
85
David Tolnayabf5c2e2018-01-06 23:30:04 -080086 /// A floating point literal: `1f64` or `1.0e10f64`.
David Tolnay360efd22018-01-04 23:35:26 -080087 ///
88 /// Must be finite. May not be infinte or NaN.
David Tolnay461d98e2018-01-07 11:07:19 -080089 ///
90 /// *This type is available if Syn is built with the `"derive"` or
91 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -080092 pub Float(LitFloat #manual_extra_traits {
93 token: Literal,
94 pub span: Span,
95 }),
96
David Tolnayabf5c2e2018-01-06 23:30:04 -080097 /// A boolean literal: `true` or `false`.
David Tolnay461d98e2018-01-07 11:07:19 -080098 ///
99 /// *This type is available if Syn is built with the `"derive"` or
100 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -0800101 pub Bool(LitBool #manual_extra_traits {
102 pub value: bool,
103 pub span: Span,
104 }),
105
David Tolnayabf5c2e2018-01-06 23:30:04 -0800106 /// A raw token literal not interpreted by Syn, possibly because it
107 /// represents an integer larger than 64 bits.
David Tolnay461d98e2018-01-07 11:07:19 -0800108 ///
109 /// *This type is available if Syn is built with the `"derive"` or
110 /// `"full"` feature.*
David Tolnay360efd22018-01-04 23:35:26 -0800111 pub Verbatim(LitVerbatim #manual_extra_traits {
112 pub token: Literal,
113 pub span: Span,
114 }),
115 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700116}
117
David Tolnay360efd22018-01-04 23:35:26 -0800118impl LitStr {
119 pub fn new(value: &str, span: Span) -> Self {
120 LitStr {
121 token: Literal::string(value),
122 span: span,
123 }
124 }
125
126 pub fn value(&self) -> String {
127 value::parse_lit_str(&self.token.to_string())
128 }
David Tolnayd53ac2b2018-01-27 19:00:06 -0800129
130 /// Parse a syntax tree node from the content of this string literal.
131 ///
132 /// All spans in the syntax tree will point to the span of this `LitStr`.
133 #[cfg(feature = "parsing")]
134 pub fn parse<T: Synom>(&self) -> Result<T, ParseError> {
135 // Parse string literal into a token stream with every span equal to the
136 // original literal's span.
137 fn spanned_tokens(s: &LitStr) -> Result<TokenStream, ParseError> {
138 let stream = ::parse_str(&s.value())?;
139 Ok(respan_token_stream(stream, s.span))
140 }
141
142 // Token stream with every span replaced by the given one.
143 fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
144 stream.into_iter().map(|token| respan_token_tree(token, span)).collect()
145 }
146
147 // Token tree with every span replaced by the given one.
148 fn respan_token_tree(token: TokenTree, span: Span) -> TokenTree {
149 TokenTree {
150 span: span,
151 kind: match token.kind {
152 TokenNode::Group(delimiter, nested) => {
153 TokenNode::Group(delimiter, respan_token_stream(nested, span))
154 }
155 other => other,
156 },
157 }
158 }
159
160 spanned_tokens(self).and_then(::parse2)
161 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700162}
163
David Tolnay360efd22018-01-04 23:35:26 -0800164impl LitByteStr {
165 pub fn new(value: &[u8], span: Span) -> Self {
166 LitByteStr {
167 token: Literal::byte_string(value),
168 span: span,
169 }
170 }
171
172 pub fn value(&self) -> Vec<u8> {
173 value::parse_lit_byte_str(&self.token.to_string())
174 }
175}
176
177impl LitByte {
178 pub fn new(value: u8, span: Span) -> Self {
179 LitByte {
180 token: Literal::byte_char(value),
181 span: span,
182 }
183 }
184
185 pub fn value(&self) -> u8 {
186 value::parse_lit_byte(&self.token.to_string())
187 }
188}
189
190impl LitChar {
191 pub fn new(value: char, span: Span) -> Self {
192 LitChar {
193 token: Literal::character(value),
194 span: span,
195 }
196 }
197
198 pub fn value(&self) -> char {
199 value::parse_lit_char(&self.token.to_string())
200 }
201}
202
203impl LitInt {
204 pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self {
205 LitInt {
206 token: match suffix {
207 IntSuffix::Isize => Literal::isize(value as isize),
208 IntSuffix::I8 => Literal::i8(value as i8),
209 IntSuffix::I16 => Literal::i16(value as i16),
210 IntSuffix::I32 => Literal::i32(value as i32),
211 IntSuffix::I64 => Literal::i64(value as i64),
212 IntSuffix::I128 => value::to_literal(&format!("{}i128", value)),
213 IntSuffix::Usize => Literal::usize(value as usize),
214 IntSuffix::U8 => Literal::u8(value as u8),
215 IntSuffix::U16 => Literal::u16(value as u16),
216 IntSuffix::U32 => Literal::u32(value as u32),
217 IntSuffix::U64 => Literal::u64(value),
218 IntSuffix::U128 => value::to_literal(&format!("{}u128", value)),
219 IntSuffix::None => Literal::integer(value as i64),
220 },
221 span: span,
222 }
223 }
224
225 pub fn value(&self) -> u64 {
226 value::parse_lit_int(&self.token.to_string()).unwrap()
227 }
228
229 pub fn suffix(&self) -> IntSuffix {
230 let value = self.token.to_string();
231 for (s, suffix) in vec![
232 ("i8", IntSuffix::I8),
233 ("i16", IntSuffix::I16),
234 ("i32", IntSuffix::I32),
235 ("i64", IntSuffix::I64),
236 ("i128", IntSuffix::I128),
237 ("isize", IntSuffix::Isize),
238 ("u8", IntSuffix::U8),
239 ("u16", IntSuffix::U16),
240 ("u32", IntSuffix::U32),
241 ("u64", IntSuffix::U64),
242 ("u128", IntSuffix::U128),
243 ("usize", IntSuffix::Usize),
244 ] {
245 if value.ends_with(s) {
246 return suffix;
247 }
248 }
249 IntSuffix::None
250 }
251}
252
253impl LitFloat {
254 pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self {
255 LitFloat {
256 token: match suffix {
257 FloatSuffix::F32 => Literal::f32(value as f32),
258 FloatSuffix::F64 => Literal::f64(value),
259 FloatSuffix::None => Literal::float(value),
260 },
261 span: span,
262 }
263 }
264
265 pub fn value(&self) -> f64 {
266 value::parse_lit_float(&self.token.to_string())
267 }
268
269 pub fn suffix(&self) -> FloatSuffix {
270 let value = self.token.to_string();
David Tolnay61037c62018-01-05 16:21:03 -0800271 for (s, suffix) in vec![("f32", FloatSuffix::F32), ("f64", FloatSuffix::F64)] {
David Tolnay360efd22018-01-04 23:35:26 -0800272 if value.ends_with(s) {
273 return suffix;
274 }
275 }
276 FloatSuffix::None
277 }
278}
279
280macro_rules! lit_extra_traits {
281 ($ty:ident, $field:ident) => {
282 #[cfg(feature = "extra-traits")]
283 impl Eq for $ty {}
284
285 #[cfg(feature = "extra-traits")]
286 impl PartialEq for $ty {
287 fn eq(&self, other: &Self) -> bool {
288 self.$field.to_string() == other.$field.to_string()
289 }
290 }
291
292 #[cfg(feature = "extra-traits")]
293 impl Hash for $ty {
294 fn hash<H>(&self, state: &mut H)
295 where
296 H: Hasher,
297 {
298 self.$field.to_string().hash(state);
299 }
David Tolnay9c76bcb2017-12-26 23:14:59 -0500300 }
Alex Crichton62a0a592017-05-22 13:58:53 -0700301 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700302}
303
David Tolnay360efd22018-01-04 23:35:26 -0800304lit_extra_traits!(LitStr, token);
305lit_extra_traits!(LitByteStr, token);
306lit_extra_traits!(LitByte, token);
307lit_extra_traits!(LitChar, token);
308lit_extra_traits!(LitInt, token);
309lit_extra_traits!(LitFloat, token);
310lit_extra_traits!(LitBool, value);
311lit_extra_traits!(LitVerbatim, token);
312
313ast_enum! {
David Tolnay05658502018-01-07 09:56:37 -0800314 /// The style of a string literal, either plain quoted or a raw string like
David Tolnayabf5c2e2018-01-06 23:30:04 -0800315 /// `r##"data"##`.
David Tolnay461d98e2018-01-07 11:07:19 -0800316 ///
317 /// *This type is available if Syn is built with the `"derive"` or `"full"`
318 /// feature.*
David Tolnay360efd22018-01-04 23:35:26 -0800319 pub enum StrStyle #no_visit {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800320 /// An ordinary string like `"data"`.
David Tolnay360efd22018-01-04 23:35:26 -0800321 Cooked,
David Tolnayabf5c2e2018-01-06 23:30:04 -0800322 /// A raw string like `r##"data"##`.
David Tolnay360efd22018-01-04 23:35:26 -0800323 ///
324 /// The unsigned integer is the number of `#` symbols used.
325 Raw(usize),
Alex Crichton62a0a592017-05-22 13:58:53 -0700326 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700327}
328
David Tolnay360efd22018-01-04 23:35:26 -0800329ast_enum! {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800330 /// The suffix on an integer literal if any, like the `u8` in `127u8`.
David Tolnay461d98e2018-01-07 11:07:19 -0800331 ///
332 /// *This type is available if Syn is built with the `"derive"` or `"full"`
333 /// feature.*
David Tolnay360efd22018-01-04 23:35:26 -0800334 pub enum IntSuffix #no_visit {
335 I8,
336 I16,
337 I32,
338 I64,
339 I128,
340 Isize,
341 U8,
342 U16,
343 U32,
344 U64,
345 U128,
346 Usize,
347 None,
Pascal Hertleif36342c52016-10-19 10:31:42 +0200348 }
349}
350
David Tolnay360efd22018-01-04 23:35:26 -0800351ast_enum! {
David Tolnayabf5c2e2018-01-06 23:30:04 -0800352 /// The suffix on a floating point literal if any, like the `f32` in
353 /// `1.0f32`.
David Tolnay461d98e2018-01-07 11:07:19 -0800354 ///
355 /// *This type is available if Syn is built with the `"derive"` or `"full"`
356 /// feature.*
David Tolnay360efd22018-01-04 23:35:26 -0800357 pub enum FloatSuffix #no_visit {
358 F32,
359 F64,
360 None,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700361 }
David Tolnay5fe14fc2017-01-27 16:22:08 -0800362}
363
364#[cfg(feature = "parsing")]
David Tolnayf4bbbd92016-09-23 14:41:55 -0700365pub mod parsing {
366 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500367 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800368 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500369 use parse_error;
370 use synom::PResult;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700371
Alex Crichton954046c2017-05-30 21:49:42 -0700372 impl Synom for Lit {
Michael Layzell92639a52017-06-01 00:07:44 -0400373 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400374 match input.literal() {
David Tolnay7037c9b2018-01-23 09:34:09 -0800375 Some((span, lit, rest)) => {
376 if lit.to_string().starts_with('/') {
377 // Doc comment literal which is not a Syn literal
378 parse_error()
379 } else {
380 Ok((Lit::new(lit, span), rest))
381 }
382 }
David Tolnay73c98de2017-12-31 15:56:56 -0500383 _ => match input.term() {
David Tolnay360efd22018-01-04 23:35:26 -0800384 Some((span, term, rest)) => Ok((
385 Lit::Bool(LitBool {
386 value: if term.as_str() == "true" {
387 true
388 } else if term.as_str() == "false" {
389 false
390 } else {
391 return parse_error();
David Tolnay51382052017-12-27 13:46:21 -0500392 },
David Tolnay360efd22018-01-04 23:35:26 -0800393 span: span,
394 }),
395 rest,
396 )),
Michael Layzell589a8f42017-06-02 19:47:01 -0400397 _ => parse_error(),
David Tolnay51382052017-12-27 13:46:21 -0500398 },
Michael Layzell589a8f42017-06-02 19:47:01 -0400399 }
David Tolnayfa0edf22016-09-23 22:58:24 -0700400 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800401
402 fn description() -> Option<&'static str> {
403 Some("literal")
404 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700405 }
David Tolnay360efd22018-01-04 23:35:26 -0800406
407 impl_synom!(LitStr "string literal" switch!(
408 syn!(Lit),
409 Lit::Str(lit) => value!(lit)
410 |
411 _ => reject!()
412 ));
413
414 impl_synom!(LitByteStr "byte string literal" switch!(
415 syn!(Lit),
416 Lit::ByteStr(lit) => value!(lit)
417 |
418 _ => reject!()
419 ));
420
421 impl_synom!(LitByte "byte literal" switch!(
422 syn!(Lit),
423 Lit::Byte(lit) => value!(lit)
424 |
425 _ => reject!()
426 ));
427
428 impl_synom!(LitChar "character literal" switch!(
429 syn!(Lit),
430 Lit::Char(lit) => value!(lit)
431 |
432 _ => reject!()
433 ));
434
435 impl_synom!(LitInt "integer literal" switch!(
436 syn!(Lit),
437 Lit::Int(lit) => value!(lit)
438 |
439 _ => reject!()
440 ));
441
442 impl_synom!(LitFloat "floating point literal" switch!(
443 syn!(Lit),
444 Lit::Float(lit) => value!(lit)
445 |
446 _ => reject!()
447 ));
448
449 impl_synom!(LitBool "boolean literal" switch!(
450 syn!(Lit),
451 Lit::Bool(lit) => value!(lit)
452 |
453 _ => reject!()
454 ));
David Tolnayf4bbbd92016-09-23 14:41:55 -0700455}
456
457#[cfg(feature = "printing")]
458mod printing {
459 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500460 use quote::{ToTokens, Tokens};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700461
David Tolnay360efd22018-01-04 23:35:26 -0800462 impl ToTokens for LitStr {
David Tolnayf4bbbd92016-09-23 14:41:55 -0700463 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnay360efd22018-01-04 23:35:26 -0800464 tokens.append(TokenTree {
465 span: self.span,
466 kind: TokenNode::Literal(self.token.clone()),
467 });
468 }
469 }
470
471 impl ToTokens for LitByteStr {
472 fn to_tokens(&self, tokens: &mut Tokens) {
473 tokens.append(TokenTree {
474 span: self.span,
475 kind: TokenNode::Literal(self.token.clone()),
476 });
477 }
478 }
479
480 impl ToTokens for LitByte {
481 fn to_tokens(&self, tokens: &mut Tokens) {
482 tokens.append(TokenTree {
483 span: self.span,
484 kind: TokenNode::Literal(self.token.clone()),
485 });
486 }
487 }
488
489 impl ToTokens for LitChar {
490 fn to_tokens(&self, tokens: &mut Tokens) {
491 tokens.append(TokenTree {
492 span: self.span,
493 kind: TokenNode::Literal(self.token.clone()),
494 });
495 }
496 }
497
498 impl ToTokens for LitInt {
499 fn to_tokens(&self, tokens: &mut Tokens) {
500 tokens.append(TokenTree {
501 span: self.span,
502 kind: TokenNode::Literal(self.token.clone()),
503 });
504 }
505 }
506
507 impl ToTokens for LitFloat {
508 fn to_tokens(&self, tokens: &mut Tokens) {
509 tokens.append(TokenTree {
510 span: self.span,
511 kind: TokenNode::Literal(self.token.clone()),
512 });
513 }
514 }
515
516 impl ToTokens for LitBool {
517 fn to_tokens(&self, tokens: &mut Tokens) {
518 tokens.append(TokenTree {
519 span: self.span,
David Tolnay61037c62018-01-05 16:21:03 -0800520 kind: TokenNode::Term(Term::intern(if self.value { "true" } else { "false" })),
David Tolnay360efd22018-01-04 23:35:26 -0800521 });
522 }
523 }
524
525 impl ToTokens for LitVerbatim {
526 fn to_tokens(&self, tokens: &mut Tokens) {
527 tokens.append(TokenTree {
528 span: self.span,
529 kind: TokenNode::Literal(self.token.clone()),
530 });
531 }
532 }
533}
534
535mod value {
536 use super::*;
537 use std::char;
538 use std::ops::{Index, RangeFrom};
539 use proc_macro2::TokenStream;
540
David Tolnay7d1d1282018-01-06 16:10:51 -0800541 impl Lit {
David Tolnay780292d2018-01-22 23:26:44 -0800542 /// Interpret a Syn literal from a proc-macro2 literal.
543 ///
544 /// Not all proc-macro2 literals are valid Syn literals. In particular,
545 /// doc comments are considered by proc-macro2 to be literals but in Syn
546 /// they are [`Attribute`].
547 ///
548 /// [`Attribute`]: struct.Attribute.html
549 ///
550 /// # Panics
551 ///
552 /// Panics if the input is a doc comment literal.
David Tolnay7d1d1282018-01-06 16:10:51 -0800553 pub fn new(token: Literal, span: Span) -> Self {
554 let value = token.to_string();
555
556 match value::byte(&value, 0) {
557 b'"' | b'r' => {
558 return Lit::Str(LitStr {
559 token: token,
560 span: span,
561 })
562 }
563 b'b' => match value::byte(&value, 1) {
564 b'"' | b'r' => {
565 return Lit::ByteStr(LitByteStr {
566 token: token,
567 span: span,
568 })
569 }
570 b'\'' => {
571 return Lit::Byte(LitByte {
572 token: token,
573 span: span,
574 })
575 }
576 _ => {}
577 },
578 b'\'' => {
579 return Lit::Char(LitChar {
580 token: token,
581 span: span,
582 })
583 }
584 b'0'...b'9' => if number_is_int(&value) {
585 return Lit::Int(LitInt {
586 token: token,
587 span: span,
588 });
589 } else if number_is_float(&value) {
590 return Lit::Float(LitFloat {
591 token: token,
592 span: span,
593 });
594 } else {
595 // number overflow
596 return Lit::Verbatim(LitVerbatim {
597 token: token,
598 span: span,
599 });
600 },
601 _ => if value == "true" || value == "false" {
602 return Lit::Bool(LitBool {
603 value: value == "true",
604 span: span,
605 });
606 },
607 }
608
609 panic!("Unrecognized literal: {}", value);
610 }
611 }
612
613 fn number_is_int(value: &str) -> bool {
614 if number_is_float(value) {
615 false
616 } else {
617 value::parse_lit_int(value).is_some()
618 }
619 }
620
621 fn number_is_float(value: &str) -> bool {
622 if value.contains('.') {
623 true
624 } else if value.starts_with("0x") || value.ends_with("size") {
625 false
626 } else {
627 value.contains('e') || value.contains('E')
628 }
629 }
630
David Tolnay360efd22018-01-04 23:35:26 -0800631 /// Get the byte at offset idx, or a default of `b'\0'` if we're looking
632 /// past the end of the input buffer.
633 pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
634 let s = s.as_ref();
635 if idx < s.len() {
636 s[idx]
637 } else {
638 0
639 }
640 }
641
642 fn next_chr(s: &str) -> char {
643 s.chars().next().unwrap_or('\0')
644 }
645
646 pub fn parse_lit_str(s: &str) -> String {
647 match byte(s, 0) {
648 b'"' => parse_lit_str_cooked(s),
649 b'r' => parse_lit_str_raw(s),
650 _ => unreachable!(),
651 }
652 }
653
David Tolnay76ebcdd2018-01-05 17:07:26 -0800654 // Clippy false positive
655 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
656 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800657 fn parse_lit_str_cooked(mut s: &str) -> String {
658 assert_eq!(byte(s, 0), b'"');
659 s = &s[1..];
660
661 let mut out = String::new();
662 'outer: loop {
663 let ch = match byte(s, 0) {
664 b'"' => break,
665 b'\\' => {
666 let b = byte(s, 1);
667 s = &s[2..];
668 match b {
669 b'x' => {
670 let (byte, rest) = backslash_x(s);
671 s = rest;
672 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800673 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800674 }
675 b'u' => {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800676 let (chr, rest) = backslash_u(s);
David Tolnay360efd22018-01-04 23:35:26 -0800677 s = rest;
678 chr
679 }
680 b'n' => '\n',
681 b'r' => '\r',
682 b't' => '\t',
683 b'\\' => '\\',
684 b'0' => '\0',
685 b'\'' => '\'',
686 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800687 b'\r' | b'\n' => loop {
688 let ch = next_chr(s);
689 if ch.is_whitespace() {
690 s = &s[ch.len_utf8()..];
691 } else {
692 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800693 }
David Tolnay61037c62018-01-05 16:21:03 -0800694 },
695 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800696 }
697 }
698 b'\r' => {
699 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
700 s = &s[2..];
701 '\n'
702 }
703 _ => {
704 let ch = next_chr(s);
705 s = &s[ch.len_utf8()..];
706 ch
707 }
708 };
709 out.push(ch);
710 }
711
712 assert_eq!(s, "\"");
713 out
714 }
715
716 fn parse_lit_str_raw(mut s: &str) -> String {
717 assert_eq!(byte(s, 0), b'r');
718 s = &s[1..];
719
720 let mut pounds = 0;
721 while byte(s, pounds) == b'#' {
722 pounds += 1;
723 }
724 assert_eq!(byte(s, pounds), b'"');
725 assert_eq!(byte(s, s.len() - pounds - 1), b'"');
726 for end in s[s.len() - pounds..].bytes() {
727 assert_eq!(end, b'#');
728 }
729
730 s[pounds + 1..s.len() - pounds - 1].to_owned()
731 }
732
733 pub fn parse_lit_byte_str(s: &str) -> Vec<u8> {
734 assert_eq!(byte(s, 0), b'b');
735 match byte(s, 1) {
736 b'"' => parse_lit_byte_str_cooked(s),
737 b'r' => parse_lit_byte_str_raw(s),
738 _ => unreachable!(),
739 }
740 }
741
David Tolnay76ebcdd2018-01-05 17:07:26 -0800742 // Clippy false positive
743 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
744 #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
David Tolnay360efd22018-01-04 23:35:26 -0800745 fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> {
746 assert_eq!(byte(s, 0), b'b');
747 assert_eq!(byte(s, 1), b'"');
748 s = &s[2..];
749
750 // We're going to want to have slices which don't respect codepoint boundaries.
751 let mut s = s.as_bytes();
752
753 let mut out = Vec::new();
754 'outer: loop {
755 let byte = match byte(s, 0) {
756 b'"' => break,
757 b'\\' => {
758 let b = byte(s, 1);
759 s = &s[2..];
760 match b {
761 b'x' => {
762 let (b, rest) = backslash_x(s);
763 s = rest;
764 b
765 }
766 b'n' => b'\n',
767 b'r' => b'\r',
768 b't' => b'\t',
769 b'\\' => b'\\',
770 b'0' => b'\0',
771 b'\'' => b'\'',
772 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800773 b'\r' | b'\n' => loop {
774 let byte = byte(s, 0);
David Tolnay76ebcdd2018-01-05 17:07:26 -0800775 let ch = char::from_u32(u32::from(byte)).unwrap();
David Tolnay61037c62018-01-05 16:21:03 -0800776 if ch.is_whitespace() {
777 s = &s[1..];
778 } else {
779 continue 'outer;
David Tolnay360efd22018-01-04 23:35:26 -0800780 }
David Tolnay61037c62018-01-05 16:21:03 -0800781 },
782 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800783 }
784 }
785 b'\r' => {
786 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
787 s = &s[2..];
788 b'\n'
789 }
790 b => {
791 s = &s[1..];
792 b
793 }
794 };
795 out.push(byte);
796 }
797
798 assert_eq!(s, b"\"");
799 out
800 }
801
802 fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> {
803 assert_eq!(byte(s, 0), b'b');
804 parse_lit_str_raw(&s[1..]).into_bytes()
805 }
806
807 pub fn parse_lit_byte(s: &str) -> u8 {
808 assert_eq!(byte(s, 0), b'b');
809 assert_eq!(byte(s, 1), b'\'');
810
811 // We're going to want to have slices which don't respect codepoint boundaries.
812 let mut s = s[2..].as_bytes();
813
814 let b = match byte(s, 0) {
815 b'\\' => {
816 let b = byte(s, 1);
817 s = &s[2..];
818 match b {
819 b'x' => {
820 let (b, rest) = backslash_x(s);
821 s = rest;
822 b
823 }
824 b'n' => b'\n',
825 b'r' => b'\r',
826 b't' => b'\t',
827 b'\\' => b'\\',
828 b'0' => b'\0',
829 b'\'' => b'\'',
830 b'"' => b'"',
David Tolnay61037c62018-01-05 16:21:03 -0800831 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800832 }
833 }
834 b => {
835 s = &s[1..];
836 b
837 }
838 };
839
840 assert_eq!(byte(s, 0), b'\'');
841 b
842 }
843
844 pub fn parse_lit_char(mut s: &str) -> char {
845 assert_eq!(byte(s, 0), b'\'');
846 s = &s[1..];
847
848 let ch = match byte(s, 0) {
849 b'\\' => {
850 let b = byte(s, 1);
851 s = &s[2..];
852 match b {
853 b'x' => {
854 let (byte, rest) = backslash_x(s);
855 s = rest;
856 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
David Tolnay76ebcdd2018-01-05 17:07:26 -0800857 char::from_u32(u32::from(byte)).unwrap()
David Tolnay360efd22018-01-04 23:35:26 -0800858 }
859 b'u' => {
860 let (chr, rest) = backslash_u(s);
861 s = rest;
862 chr
863 }
864 b'n' => '\n',
865 b'r' => '\r',
866 b't' => '\t',
867 b'\\' => '\\',
868 b'0' => '\0',
869 b'\'' => '\'',
870 b'"' => '"',
David Tolnay61037c62018-01-05 16:21:03 -0800871 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
David Tolnay360efd22018-01-04 23:35:26 -0800872 }
873 }
874 _ => {
875 let ch = next_chr(s);
876 s = &s[ch.len_utf8()..];
877 ch
878 }
879 };
880 assert_eq!(s, "\'", "Expected end of char literal");
881 ch
882 }
883
884 fn backslash_x<S>(s: &S) -> (u8, &S)
David Tolnay61037c62018-01-05 16:21:03 -0800885 where
886 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
David Tolnay360efd22018-01-04 23:35:26 -0800887 {
888 let mut ch = 0;
889 let b0 = byte(s, 0);
890 let b1 = byte(s, 1);
891 ch += 0x10 * match b0 {
892 b'0'...b'9' => b0 - b'0',
893 b'a'...b'f' => 10 + (b0 - b'a'),
894 b'A'...b'F' => 10 + (b0 - b'A'),
895 _ => panic!("unexpected non-hex character after \\x"),
896 };
David Tolnay76ebcdd2018-01-05 17:07:26 -0800897 ch += match b1 {
David Tolnay360efd22018-01-04 23:35:26 -0800898 b'0'...b'9' => b1 - b'0',
899 b'a'...b'f' => 10 + (b1 - b'a'),
900 b'A'...b'F' => 10 + (b1 - b'A'),
901 _ => panic!("unexpected non-hex character after \\x"),
902 };
903 (ch, &s[2..])
904 }
905
906 fn backslash_u(mut s: &str) -> (char, &str) {
907 if byte(s, 0) != b'{' {
908 panic!("expected {{ after \\u");
909 }
910 s = &s[1..];
911
912 let mut ch = 0;
913 for _ in 0..6 {
914 let b = byte(s, 0);
915 match b {
916 b'0'...b'9' => {
917 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800918 ch += u32::from(b - b'0');
David Tolnay360efd22018-01-04 23:35:26 -0800919 s = &s[1..];
920 }
921 b'a'...b'f' => {
922 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800923 ch += u32::from(10 + b - b'a');
David Tolnay360efd22018-01-04 23:35:26 -0800924 s = &s[1..];
925 }
926 b'A'...b'F' => {
927 ch *= 0x10;
David Tolnay76ebcdd2018-01-05 17:07:26 -0800928 ch += u32::from(10 + b - b'A');
David Tolnay360efd22018-01-04 23:35:26 -0800929 s = &s[1..];
930 }
931 b'}' => break,
932 _ => panic!("unexpected non-hex character after \\u"),
933 }
934 }
935 assert!(byte(s, 0) == b'}');
936 s = &s[1..];
937
938 if let Some(ch) = char::from_u32(ch) {
939 (ch, s)
940 } else {
941 panic!("character code {:x} is not a valid unicode character", ch);
942 }
943 }
944
945 pub fn parse_lit_int(mut s: &str) -> Option<u64> {
946 let base = match (byte(s, 0), byte(s, 1)) {
947 (b'0', b'x') => {
948 s = &s[2..];
949 16
950 }
951 (b'0', b'o') => {
952 s = &s[2..];
953 8
954 }
955 (b'0', b'b') => {
956 s = &s[2..];
957 2
958 }
959 (b'0'...b'9', _) => 10,
960 _ => unreachable!(),
961 };
962
963 let mut value = 0u64;
964 loop {
965 let b = byte(s, 0);
966 let digit = match b {
David Tolnay76ebcdd2018-01-05 17:07:26 -0800967 b'0'...b'9' => u64::from(b - b'0'),
968 b'a'...b'f' if base > 10 => 10 + u64::from(b - b'a'),
969 b'A'...b'F' if base > 10 => 10 + u64::from(b - b'A'),
David Tolnay360efd22018-01-04 23:35:26 -0800970 b'_' => {
971 s = &s[1..];
972 continue;
973 }
974 // NOTE: Looking at a floating point literal, we don't want to
975 // consider these integers.
976 b'.' if base == 10 => return None,
977 b'e' | b'E' if base == 10 => return None,
978 _ => break,
979 };
980
981 if digit >= base {
982 panic!("Unexpected digit {:x} out of base range", digit);
983 }
984
985 value = match value.checked_mul(base) {
986 Some(value) => value,
987 None => return None,
988 };
989 value = match value.checked_add(digit) {
990 Some(value) => value,
991 None => return None,
992 };
993 s = &s[1..];
994 }
995
996 Some(value)
997 }
998
999 pub fn parse_lit_float(input: &str) -> f64 {
1000 // Rust's floating point literals are very similar to the ones parsed by
1001 // the standard library, except that rust's literals can contain
1002 // ignorable underscores. Let's remove those underscores.
1003 let mut bytes = input.to_owned().into_bytes();
1004 let mut write = 0;
1005 for read in 0..bytes.len() {
1006 if bytes[read] == b'_' {
1007 continue; // Don't increase write
David Tolnay76ebcdd2018-01-05 17:07:26 -08001008 }
1009 if write != read {
David Tolnay360efd22018-01-04 23:35:26 -08001010 let x = bytes[read];
1011 bytes[write] = x;
1012 }
1013 write += 1;
1014 }
1015 bytes.truncate(write);
1016 let input = String::from_utf8(bytes).unwrap();
David Tolnay76ebcdd2018-01-05 17:07:26 -08001017 let end = input.find('f').unwrap_or_else(|| input.len());
David Tolnay360efd22018-01-04 23:35:26 -08001018 input[..end].parse().unwrap()
1019 }
1020
1021 pub fn to_literal(s: &str) -> Literal {
1022 let stream = s.parse::<TokenStream>().unwrap();
1023 match stream.into_iter().next().unwrap().kind {
1024 TokenNode::Literal(l) => l,
1025 _ => unreachable!(),
David Tolnayf17fd2f2016-10-07 23:38:08 -07001026 }
1027 }
David Tolnayf4bbbd92016-09-23 14:41:55 -07001028}