blob: 81be2852c3a427b49ac13616bc9322e25e351940 [file] [log] [blame]
David Tolnayf4bbbd92016-09-23 14:41:55 -07001/// Literal kind.
2///
3/// E.g. `"foo"`, `42`, `12.34` or `bool`
David Tolnay9bf4af82017-01-07 11:17:46 -08004#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayf4bbbd92016-09-23 14:41:55 -07005pub enum Lit {
6 /// A string literal (`"foo"`)
7 Str(String, StrStyle),
8 /// A byte string (`b"foo"`)
David Tolnay4a658402016-10-24 00:21:41 -07009 ByteStr(Vec<u8>, StrStyle),
David Tolnayf4bbbd92016-09-23 14:41:55 -070010 /// A byte char (`b'f'`)
11 Byte(u8),
12 /// A character literal (`'a'`)
13 Char(char),
14 /// An integer literal (`1`)
15 Int(u64, IntTy),
16 /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
17 Float(String, FloatTy),
18 /// A boolean literal
19 Bool(bool),
20}
21
David Tolnay9bf4af82017-01-07 11:17:46 -080022#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
David Tolnayf4bbbd92016-09-23 14:41:55 -070023pub enum StrStyle {
24 /// A regular string, like `"foo"`
25 Cooked,
26 /// A raw string, like `r##"foo"##`
27 ///
28 /// The uint is the number of `#` symbols used
David Tolnaydaaf7742016-10-03 11:11:43 -070029 Raw(usize),
David Tolnayf4bbbd92016-09-23 14:41:55 -070030}
31
Pascal Hertleif36342c52016-10-19 10:31:42 +020032impl From<String> for Lit {
33 fn from(input: String) -> Lit {
34 Lit::Str(input, StrStyle::Cooked)
35 }
36}
37
38impl<'a> From<&'a str> for Lit {
39 fn from(input: &str) -> Lit {
40 Lit::Str(input.into(), StrStyle::Cooked)
41 }
42}
43
44impl From<Vec<u8>> for Lit {
45 fn from(input: Vec<u8>) -> Lit {
David Tolnay4a658402016-10-24 00:21:41 -070046 Lit::ByteStr(input, StrStyle::Cooked)
Pascal Hertleif36342c52016-10-19 10:31:42 +020047 }
48}
49
50impl<'a> From<&'a [u8]> for Lit {
51 fn from(input: &[u8]) -> Lit {
David Tolnay4a658402016-10-24 00:21:41 -070052 Lit::ByteStr(input.into(), StrStyle::Cooked)
Pascal Hertleif36342c52016-10-19 10:31:42 +020053 }
54}
55
56impl From<char> for Lit {
57 fn from(input: char) -> Lit {
58 Lit::Char(input)
59 }
60}
61
62impl From<bool> for Lit {
63 fn from(input: bool) -> Lit {
64 Lit::Bool(input)
65 }
66}
67
David Tolnay9bf4af82017-01-07 11:17:46 -080068#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
David Tolnayf4bbbd92016-09-23 14:41:55 -070069pub enum IntTy {
70 Isize,
71 I8,
72 I16,
73 I32,
74 I64,
75 Usize,
76 U8,
77 U16,
78 U32,
79 U64,
David Tolnaydaaf7742016-10-03 11:11:43 -070080 Unsuffixed,
David Tolnayf4bbbd92016-09-23 14:41:55 -070081}
82
David Tolnay9bf4af82017-01-07 11:17:46 -080083#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
David Tolnayf4bbbd92016-09-23 14:41:55 -070084pub enum FloatTy {
85 F32,
86 F64,
87 Unsuffixed,
88}
89
Pascal Hertleif36342c52016-10-19 10:31:42 +020090macro_rules! impl_from_for_lit {
91 (Int, [$($rust_type:ty => $syn_type:expr),+]) => {
92 $(
93 impl From<$rust_type> for Lit {
94 fn from(input: $rust_type) -> Lit {
95 Lit::Int(input as u64, $syn_type)
96 }
97 }
98 )+
99 };
100 (Float, [$($rust_type:ty => $syn_type:expr),+]) => {
101 $(
102 impl From<$rust_type> for Lit {
103 fn from(input: $rust_type) -> Lit {
104 Lit::Float(format!("{}", input), $syn_type)
105 }
106 }
107 )+
108 };
109}
110
111impl_from_for_lit! {Int, [
112 isize => IntTy::Isize,
113 i8 => IntTy::I8,
114 i16 => IntTy::I16,
115 i32 => IntTy::I32,
116 i64 => IntTy::I64,
117 usize => IntTy::Usize,
118 u8 => IntTy::U8,
119 u16 => IntTy::U16,
120 u32 => IntTy::U32,
121 u64 => IntTy::U64
122]}
123
124impl_from_for_lit! {Float, [
125 f32 => FloatTy::F32,
126 f64 => FloatTy::F64
127]}
128
David Tolnayf4bbbd92016-09-23 14:41:55 -0700129#[cfg(feature = "parsing")]
David Tolnay5fe14fc2017-01-27 16:22:08 -0800130#[derive(Debug, Clone)]
131pub struct StrLit {
Michael Layzell93336812017-01-28 16:16:15 -0500132 pub value: String,
133 pub style: StrStyle,
David Tolnay5fe14fc2017-01-27 16:22:08 -0800134}
135
136#[cfg(feature = "parsing")]
137#[derive(Debug, Clone)]
138pub struct ByteStrLit {
Michael Layzell93336812017-01-28 16:16:15 -0500139 pub value: Vec<u8>,
140 pub style: StrStyle,
David Tolnay5fe14fc2017-01-27 16:22:08 -0800141}
142
143#[cfg(feature = "parsing")]
144#[derive(Debug, Clone)]
145pub struct IntLit {
Michael Layzell93336812017-01-28 16:16:15 -0500146 pub value: u64,
147 pub suffix: IntTy,
David Tolnay5fe14fc2017-01-27 16:22:08 -0800148}
149
150#[cfg(feature = "parsing")]
151#[derive(Debug, Clone)]
152pub struct FloatLit {
Michael Layzell93336812017-01-28 16:16:15 -0500153 pub value: String,
154 pub suffix: FloatTy,
David Tolnay5fe14fc2017-01-27 16:22:08 -0800155}
156
157#[cfg(feature = "parsing")]
David Tolnayf4bbbd92016-09-23 14:41:55 -0700158pub mod parsing {
159 use super::*;
David Tolnayfe373a32016-10-26 23:51:19 -0700160 use escape::{cooked_byte, cooked_byte_string, cooked_char, cooked_string, raw_string};
David Tolnay5fe14fc2017-01-27 16:22:08 -0800161 use synom::space::skip_whitespace;
162 use synom::IResult;
David Tolnay4f5f60f2016-10-24 00:52:58 -0700163 use unicode_xid::UnicodeXID;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700164
165 named!(pub lit -> Lit, alt!(
David Tolnay5fe14fc2017-01-27 16:22:08 -0800166 string => { |StrLit { value, style }| Lit::Str(value, style) }
David Tolnay56d62132016-10-01 16:14:54 -0700167 |
David Tolnay5fe14fc2017-01-27 16:22:08 -0800168 byte_string => { |ByteStrLit { value, style }| Lit::ByteStr(value, style) }
David Tolnay615cf6a2016-10-08 23:07:02 -0700169 |
Michael Layzell5e107ff2017-01-24 19:58:39 -0500170 byte => { |b| Lit::Byte(b) }
David Tolnay615cf6a2016-10-08 23:07:02 -0700171 |
Michael Layzell5e107ff2017-01-24 19:58:39 -0500172 character => { |ch| Lit::Char(ch) }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700173 |
David Tolnay5fe14fc2017-01-27 16:22:08 -0800174 float => { |FloatLit { value, suffix }| Lit::Float(value, suffix) } // must be before int
David Tolnay4f5f60f2016-10-24 00:52:58 -0700175 |
David Tolnay5fe14fc2017-01-27 16:22:08 -0800176 int => { |IntLit { value, suffix }| Lit::Int(value, suffix) }
David Tolnay759d2ff2016-10-01 16:18:15 -0700177 |
Michael Layzell5e107ff2017-01-24 19:58:39 -0500178 boolean => { |value| Lit::Bool(value) }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700179 ));
180
David Tolnay5fe14fc2017-01-27 16:22:08 -0800181 named!(pub string -> StrLit, alt!(
182 quoted_string => { |s| StrLit { value: s, style: StrStyle::Cooked } }
David Tolnay210884d2016-10-01 08:18:42 -0700183 |
184 preceded!(
185 punct!("r"),
186 raw_string
David Tolnay5fe14fc2017-01-27 16:22:08 -0800187 ) => { |(s, n)| StrLit { value: s, style: StrStyle::Raw(n) }}
David Tolnayf4bbbd92016-09-23 14:41:55 -0700188 ));
189
David Tolnay42602292016-10-01 22:25:45 -0700190 named!(pub quoted_string -> String, delimited!(
191 punct!("\""),
192 cooked_string,
193 tag!("\"")
194 ));
195
David Tolnay5fe14fc2017-01-27 16:22:08 -0800196 named!(pub byte_string -> ByteStrLit, alt!(
David Tolnay56d62132016-10-01 16:14:54 -0700197 delimited!(
198 punct!("b\""),
David Tolnaya73e0f02016-10-26 23:25:49 -0700199 cooked_byte_string,
David Tolnay56d62132016-10-01 16:14:54 -0700200 tag!("\"")
David Tolnay5fe14fc2017-01-27 16:22:08 -0800201 ) => { |vec| ByteStrLit { value: vec, style: StrStyle::Cooked } }
David Tolnay56d62132016-10-01 16:14:54 -0700202 |
203 preceded!(
204 punct!("br"),
205 raw_string
David Tolnay5fe14fc2017-01-27 16:22:08 -0800206 ) => { |(s, n): (String, _)| ByteStrLit { value: s.into_bytes(), style: StrStyle::Raw(n) } }
David Tolnay56d62132016-10-01 16:14:54 -0700207 ));
208
Michael Layzell5e107ff2017-01-24 19:58:39 -0500209 named!(pub byte -> u8, do_parse!(
David Tolnay615cf6a2016-10-08 23:07:02 -0700210 punct!("b") >>
211 tag!("'") >>
David Tolnayfe373a32016-10-26 23:51:19 -0700212 b: cooked_byte >>
David Tolnay615cf6a2016-10-08 23:07:02 -0700213 tag!("'") >>
Michael Layzell5e107ff2017-01-24 19:58:39 -0500214 (b)
David Tolnay615cf6a2016-10-08 23:07:02 -0700215 ));
216
Michael Layzell5e107ff2017-01-24 19:58:39 -0500217 named!(pub character -> char, do_parse!(
David Tolnay615cf6a2016-10-08 23:07:02 -0700218 punct!("'") >>
219 ch: cooked_char >>
220 tag!("'") >>
Michael Layzell5e107ff2017-01-24 19:58:39 -0500221 (ch)
David Tolnay615cf6a2016-10-08 23:07:02 -0700222 ));
223
David Tolnay5fe14fc2017-01-27 16:22:08 -0800224 named!(pub float -> FloatLit, do_parse!(
225 value: float_string >>
226 suffix: alt!(
David Tolnay4f5f60f2016-10-24 00:52:58 -0700227 tag!("f32") => { |_| FloatTy::F32 }
228 |
229 tag!("f64") => { |_| FloatTy::F64 }
230 |
231 epsilon!() => { |_| FloatTy::Unsuffixed }
David Tolnay5fe14fc2017-01-27 16:22:08 -0800232 ) >>
233 (FloatLit { value: value, suffix: suffix })
David Tolnay4f5f60f2016-10-24 00:52:58 -0700234 ));
235
David Tolnay5fe14fc2017-01-27 16:22:08 -0800236 named!(pub int -> IntLit, do_parse!(
237 value: digits >>
238 suffix: alt!(
David Tolnayde206222016-09-30 11:47:01 -0700239 tag!("isize") => { |_| IntTy::Isize }
240 |
241 tag!("i8") => { |_| IntTy::I8 }
242 |
243 tag!("i16") => { |_| IntTy::I16 }
244 |
245 tag!("i32") => { |_| IntTy::I32 }
246 |
247 tag!("i64") => { |_| IntTy::I64 }
248 |
249 tag!("usize") => { |_| IntTy::Usize }
250 |
251 tag!("u8") => { |_| IntTy::U8 }
252 |
253 tag!("u16") => { |_| IntTy::U16 }
254 |
255 tag!("u32") => { |_| IntTy::U32 }
256 |
257 tag!("u64") => { |_| IntTy::U64 }
258 |
259 epsilon!() => { |_| IntTy::Unsuffixed }
David Tolnay5fe14fc2017-01-27 16:22:08 -0800260 ) >>
261 (IntLit { value: value, suffix: suffix })
David Tolnayf4bbbd92016-09-23 14:41:55 -0700262 ));
263
Michael Layzell5e107ff2017-01-24 19:58:39 -0500264 named!(pub boolean -> bool, alt!(
265 keyword!("true") => { |_| true }
David Tolnay3ce49d02016-10-23 22:29:19 -0700266 |
Michael Layzell5e107ff2017-01-24 19:58:39 -0500267 keyword!("false") => { |_| false }
David Tolnay3ce49d02016-10-23 22:29:19 -0700268 ));
269
David Tolnaydef66372016-10-24 21:51:32 -0700270 fn float_string(mut input: &str) -> IResult<&str, String> {
271 input = skip_whitespace(input);
272
David Tolnay4f5f60f2016-10-24 00:52:58 -0700273 let mut chars = input.chars().peekable();
274 match chars.next() {
275 Some(ch) if ch >= '0' && ch <= '9' => {}
276 _ => return IResult::Error,
277 }
278
279 let mut len = 1;
280 let mut has_dot = false;
281 let mut has_exp = false;
282 while let Some(&ch) = chars.peek() {
283 match ch {
284 '0'...'9' | '_' => {
285 chars.next();
286 len += 1;
287 }
288 '.' => {
289 if has_dot {
290 break;
291 }
292 chars.next();
293 if chars.peek()
David Tolnay05120ef2017-03-12 18:29:26 -0700294 .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
295 .unwrap_or(false) {
David Tolnay4f5f60f2016-10-24 00:52:58 -0700296 return IResult::Error;
297 }
298 len += 1;
299 has_dot = true;
300 }
301 'e' | 'E' => {
302 chars.next();
303 len += 1;
304 has_exp = true;
305 break;
306 }
307 _ => break,
308 }
309 }
310
311 let rest = &input[len..];
312 if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
313 return IResult::Error;
314 }
315
316 if has_exp {
317 let mut has_exp_value = false;
318 while let Some(&ch) = chars.peek() {
319 match ch {
320 '+' | '-' => {
321 if has_exp_value {
322 break;
323 }
324 chars.next();
325 len += 1;
326 }
327 '0'...'9' => {
328 chars.next();
329 len += 1;
330 has_exp_value = true;
331 }
332 '_' => {
333 chars.next();
334 len += 1;
335 }
336 _ => break,
337 }
338 }
339 if !has_exp_value {
340 return IResult::Error;
341 }
342 }
343
David Tolnayc7b636a2016-10-24 13:01:08 -0700344 IResult::Done(&input[len..], input[..len].replace("_", ""))
David Tolnay4f5f60f2016-10-24 00:52:58 -0700345 }
346
David Tolnay8a19e6d2016-10-24 01:23:40 -0700347 pub fn digits(mut input: &str) -> IResult<&str, u64> {
David Tolnaydef66372016-10-24 21:51:32 -0700348 input = skip_whitespace(input);
349
David Tolnay8a19e6d2016-10-24 01:23:40 -0700350 let base = if input.starts_with("0x") {
351 input = &input[2..];
352 16
353 } else if input.starts_with("0o") {
354 input = &input[2..];
355 8
356 } else if input.starts_with("0b") {
357 input = &input[2..];
358 2
359 } else {
360 10
361 };
362
David Tolnayf4bbbd92016-09-23 14:41:55 -0700363 let mut value = 0u64;
364 let mut len = 0;
David Tolnay24b5ff72016-10-24 01:05:05 -0700365 let mut empty = true;
366 for b in input.bytes() {
David Tolnay8a19e6d2016-10-24 01:23:40 -0700367 let digit = match b {
368 b'0'...b'9' => (b - b'0') as u64,
369 b'a'...b'f' => 10 + (b - b'a') as u64,
370 b'A'...b'F' => 10 + (b - b'A') as u64,
David Tolnay24b5ff72016-10-24 01:05:05 -0700371 b'_' => {
David Tolnayc814a762016-10-27 23:11:28 -0700372 if empty && base == 10 {
David Tolnay24b5ff72016-10-24 01:05:05 -0700373 return IResult::Error;
374 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700375 len += 1;
David Tolnay8a19e6d2016-10-24 01:23:40 -0700376 continue;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700377 }
David Tolnayfa0edf22016-09-23 22:58:24 -0700378 _ => break,
David Tolnay8a19e6d2016-10-24 01:23:40 -0700379 };
380 if digit >= base {
381 return IResult::Error;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700382 }
David Tolnay8a19e6d2016-10-24 01:23:40 -0700383 value = match value.checked_mul(base) {
384 Some(value) => value,
385 None => return IResult::Error,
386 };
387 value = match value.checked_add(digit) {
388 Some(value) => value,
389 None => return IResult::Error,
390 };
391 len += 1;
392 empty = false;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700393 }
David Tolnay24b5ff72016-10-24 01:05:05 -0700394 if empty {
David Tolnayfa0edf22016-09-23 22:58:24 -0700395 IResult::Error
David Tolnay24b5ff72016-10-24 01:05:05 -0700396 } else {
397 IResult::Done(&input[len..], value)
David Tolnayfa0edf22016-09-23 22:58:24 -0700398 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700399 }
400}
401
402#[cfg(feature = "printing")]
403mod printing {
404 use super::*;
405 use quote::{Tokens, ToTokens};
David Tolnay56d62132016-10-01 16:14:54 -0700406 use std::{ascii, iter};
David Tolnayf4bbbd92016-09-23 14:41:55 -0700407 use std::fmt::{self, Display};
David Tolnay4a658402016-10-24 00:21:41 -0700408 use std::str;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700409
410 impl ToTokens for Lit {
411 fn to_tokens(&self, tokens: &mut Tokens) {
412 match *self {
413 Lit::Str(ref s, StrStyle::Cooked) => s.to_tokens(tokens),
David Tolnay627e3d52016-10-01 08:27:31 -0700414 Lit::Str(ref s, StrStyle::Raw(n)) => {
David Tolnay56d62132016-10-01 16:14:54 -0700415 tokens.append(&format!("r{delim}\"{string}\"{delim}",
David Tolnay05120ef2017-03-12 18:29:26 -0700416 delim = iter::repeat("#").take(n).collect::<String>(),
417 string = s));
David Tolnay56d62132016-10-01 16:14:54 -0700418 }
David Tolnay4a658402016-10-24 00:21:41 -0700419 Lit::ByteStr(ref v, StrStyle::Cooked) => {
David Tolnay56d62132016-10-01 16:14:54 -0700420 let mut escaped = "b\"".to_string();
421 for &ch in v.iter() {
David Tolnay289f4c72016-10-25 00:00:09 -0700422 match ch {
423 0 => escaped.push_str(r"\0"),
424 b'\'' => escaped.push('\''),
425 _ => escaped.extend(ascii::escape_default(ch).map(|c| c as char)),
426 }
David Tolnay627e3d52016-10-01 08:27:31 -0700427 }
David Tolnay56d62132016-10-01 16:14:54 -0700428 escaped.push('"');
429 tokens.append(&escaped);
David Tolnay627e3d52016-10-01 08:27:31 -0700430 }
David Tolnay4a658402016-10-24 00:21:41 -0700431 Lit::ByteStr(ref vec, StrStyle::Raw(n)) => {
432 tokens.append(&format!("br{delim}\"{string}\"{delim}",
David Tolnay05120ef2017-03-12 18:29:26 -0700433 delim = iter::repeat("#").take(n).collect::<String>(),
434 string = str::from_utf8(vec).unwrap()));
David Tolnay4a658402016-10-24 00:21:41 -0700435 }
David Tolnay289f4c72016-10-25 00:00:09 -0700436 Lit::Byte(b) => {
437 match b {
438 0 => tokens.append(r"b'\0'"),
439 b'\"' => tokens.append("b'\"'"),
440 _ => {
441 let mut escaped = "b'".to_string();
442 escaped.extend(ascii::escape_default(b).map(|c| c as char));
443 escaped.push('\'');
444 tokens.append(&escaped);
445 }
446 }
447 }
David Tolnayf17fd2f2016-10-07 23:38:08 -0700448 Lit::Char(ch) => ch.to_tokens(tokens),
David Tolnayf4bbbd92016-09-23 14:41:55 -0700449 Lit::Int(value, ty) => tokens.append(&format!("{}{}", value, ty)),
David Tolnayf17fd2f2016-10-07 23:38:08 -0700450 Lit::Float(ref value, ty) => tokens.append(&format!("{}{}", value, ty)),
David Tolnay759d2ff2016-10-01 16:18:15 -0700451 Lit::Bool(true) => tokens.append("true"),
452 Lit::Bool(false) => tokens.append("false"),
David Tolnayf4bbbd92016-09-23 14:41:55 -0700453 }
454 }
455 }
456
457 impl Display for IntTy {
458 fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
459 match *self {
460 IntTy::Isize => formatter.write_str("isize"),
461 IntTy::I8 => formatter.write_str("i8"),
462 IntTy::I16 => formatter.write_str("i16"),
463 IntTy::I32 => formatter.write_str("i32"),
464 IntTy::I64 => formatter.write_str("i64"),
465 IntTy::Usize => formatter.write_str("usize"),
466 IntTy::U8 => formatter.write_str("u8"),
467 IntTy::U16 => formatter.write_str("u16"),
468 IntTy::U32 => formatter.write_str("u32"),
469 IntTy::U64 => formatter.write_str("u64"),
470 IntTy::Unsuffixed => Ok(()),
471 }
472 }
473 }
David Tolnayf17fd2f2016-10-07 23:38:08 -0700474
475 impl Display for FloatTy {
476 fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
477 match *self {
478 FloatTy::F32 => formatter.write_str("f32"),
479 FloatTy::F64 => formatter.write_str("f64"),
480 FloatTy::Unsuffixed => Ok(()),
481 }
482 }
483 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700484}