blob: 9e69791df4e08e6d053c4365736c7caf7b3e934f [file] [log] [blame]
Alex Crichton76a5cc82017-05-23 07:01:44 -07001use std::ascii;
Alex Crichton44bffbc2017-05-19 17:51:59 -07002use std::borrow::Borrow;
3use std::cell::RefCell;
Nika Layzella9dbc182017-12-30 14:50:13 -05004#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -05005use std::cmp;
Alex Crichton44bffbc2017-05-19 17:51:59 -07006use std::collections::HashMap;
7use std::fmt;
8use std::iter;
David Tolnay041bcd42017-06-03 09:18:04 -07009use std::marker::PhantomData;
Alex Crichton44bffbc2017-05-19 17:51:59 -070010use std::ops;
11use std::rc::Rc;
12use std::str::FromStr;
13use std::vec;
14
15use proc_macro;
David Tolnayb1032662017-05-31 15:52:28 -070016use unicode_xid::UnicodeXID;
Nika Layzellf8d5f212017-12-11 14:07:02 -050017use strnom::{Cursor, PResult, skip_whitespace, block_comment, whitespace, word_break};
Alex Crichton44bffbc2017-05-19 17:51:59 -070018
Alex Crichton1a7f7622017-07-05 17:47:15 -070019use {TokenTree, TokenNode, Delimiter, Spacing};
Alex Crichton44bffbc2017-05-19 17:51:59 -070020
David Tolnay977f8282017-05-31 17:41:33 -070021#[derive(Clone, Debug)]
Alex Crichton44bffbc2017-05-19 17:51:59 -070022pub struct TokenStream {
23 inner: Vec<TokenTree>,
24}
25
26#[derive(Debug)]
27pub struct LexError;
28
29impl TokenStream {
30 pub fn empty() -> TokenStream {
31 TokenStream { inner: Vec::new() }
32 }
33
34 pub fn is_empty(&self) -> bool {
35 self.inner.len() == 0
36 }
37}
38
Nika Layzella9dbc182017-12-30 14:50:13 -050039#[cfg(procmacro2_unstable)]
40fn get_cursor(src: &str) -> Cursor {
41 // Create a dummy file & add it to the codemap
42 CODEMAP.with(|cm| {
43 let mut cm = cm.borrow_mut();
44 let name = format!("<parsed string {}>", cm.files.len());
45 let span = cm.add_file(&name, src);
46 Cursor {
47 rest: src,
48 off: span.lo,
49 }
50 })
51}
52
53#[cfg(not(procmacro2_unstable))]
54fn get_cursor(src: &str) -> Cursor {
55 Cursor {
56 rest: src,
57 off: 0,
58 }
59}
60
Alex Crichton44bffbc2017-05-19 17:51:59 -070061impl FromStr for TokenStream {
62 type Err = LexError;
63
64 fn from_str(src: &str) -> Result<TokenStream, LexError> {
Nika Layzellf8d5f212017-12-11 14:07:02 -050065 // Create a dummy file & add it to the codemap
Nika Layzella9dbc182017-12-30 14:50:13 -050066 let cursor = get_cursor(src);
Nika Layzellf8d5f212017-12-11 14:07:02 -050067
68 match token_stream(cursor) {
David Tolnay1218e122017-06-01 11:13:45 -070069 Ok((input, output)) => {
Alex Crichton44bffbc2017-05-19 17:51:59 -070070 if skip_whitespace(input).len() != 0 {
71 Err(LexError)
72 } else {
David Tolnay8e976c62017-06-01 12:12:29 -070073 Ok(output.0)
Alex Crichton44bffbc2017-05-19 17:51:59 -070074 }
75 }
David Tolnay1218e122017-06-01 11:13:45 -070076 Err(LexError) => Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -070077 }
78 }
79}
80
81impl fmt::Display for TokenStream {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 let mut joint = false;
84 for (i, tt) in self.inner.iter().enumerate() {
85 if i != 0 && !joint {
86 write!(f, " ")?;
87 }
88 joint = false;
89 match tt.kind {
Alex Crichton1a7f7622017-07-05 17:47:15 -070090 TokenNode::Group(delim, ref stream) => {
Alex Crichton44bffbc2017-05-19 17:51:59 -070091 let (start, end) = match delim {
92 Delimiter::Parenthesis => ("(", ")"),
93 Delimiter::Brace => ("{", "}"),
94 Delimiter::Bracket => ("[", "]"),
95 Delimiter::None => ("", ""),
96 };
Alex Crichton852d53d2017-05-19 19:25:08 -070097 if stream.0.inner.len() == 0 {
98 write!(f, "{} {}", start, end)?
99 } else {
100 write!(f, "{} {} {}", start, stream, end)?
101 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700102 }
Alex Crichton1a7f7622017-07-05 17:47:15 -0700103 TokenNode::Term(ref sym) => write!(f, "{}", sym.as_str())?,
104 TokenNode::Op(ch, ref op) => {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700105 write!(f, "{}", ch)?;
106 match *op {
Alex Crichton1a7f7622017-07-05 17:47:15 -0700107 Spacing::Alone => {}
108 Spacing::Joint => joint = true,
Alex Crichton44bffbc2017-05-19 17:51:59 -0700109 }
110 }
Alex Crichton1a7f7622017-07-05 17:47:15 -0700111 TokenNode::Literal(ref literal) => {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700112 write!(f, "{}", literal)?;
113 // handle comments
114 if (literal.0).0.starts_with("/") {
115 write!(f, "\n")?;
116 }
117 }
118 }
119 }
120
121 Ok(())
122 }
123}
124
125impl From<proc_macro::TokenStream> for TokenStream {
126 fn from(inner: proc_macro::TokenStream) -> TokenStream {
127 inner.to_string().parse().expect("compiler token stream parse failed")
128 }
129}
130
131impl From<TokenStream> for proc_macro::TokenStream {
132 fn from(inner: TokenStream) -> proc_macro::TokenStream {
133 inner.to_string().parse().expect("failed to parse to compiler tokens")
134 }
135}
136
137
138impl From<TokenTree> for TokenStream {
139 fn from(tree: TokenTree) -> TokenStream {
140 TokenStream { inner: vec![tree] }
141 }
142}
143
144impl iter::FromIterator<TokenStream> for TokenStream {
145 fn from_iter<I: IntoIterator<Item=TokenStream>>(streams: I) -> Self {
146 let mut v = Vec::new();
147
148 for stream in streams.into_iter() {
149 v.extend(stream.inner);
150 }
151
152 TokenStream { inner: v }
153 }
154}
155
Alex Crichton1a7f7622017-07-05 17:47:15 -0700156pub type TokenTreeIter = vec::IntoIter<TokenTree>;
Alex Crichton44bffbc2017-05-19 17:51:59 -0700157
158impl IntoIterator for TokenStream {
159 type Item = TokenTree;
Alex Crichton1a7f7622017-07-05 17:47:15 -0700160 type IntoIter = TokenTreeIter;
Alex Crichton44bffbc2017-05-19 17:51:59 -0700161
Alex Crichton1a7f7622017-07-05 17:47:15 -0700162 fn into_iter(self) -> TokenTreeIter {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700163 self.inner.into_iter()
164 }
165}
166
Nika Layzella9dbc182017-12-30 14:50:13 -0500167#[cfg(procmacro2_unstable)]
Nika Layzellb35a9a32017-12-30 14:34:35 -0500168#[derive(Clone, PartialEq, Eq, Debug)]
169pub struct FileName(String);
170
Nika Layzella9dbc182017-12-30 14:50:13 -0500171#[cfg(procmacro2_unstable)]
Nika Layzellb35a9a32017-12-30 14:34:35 -0500172impl fmt::Display for FileName {
173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174 self.0.fmt(f)
175 }
176}
177
Nika Layzella9dbc182017-12-30 14:50:13 -0500178#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500179#[derive(Clone, PartialEq, Eq)]
180pub struct SourceFile {
Nika Layzellb35a9a32017-12-30 14:34:35 -0500181 name: FileName,
Nika Layzellf8d5f212017-12-11 14:07:02 -0500182}
183
Nika Layzella9dbc182017-12-30 14:50:13 -0500184#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500185impl SourceFile {
186 /// Get the path to this source file as a string.
Nika Layzellb35a9a32017-12-30 14:34:35 -0500187 pub fn path(&self) -> &FileName {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500188 &self.name
189 }
190
191 pub fn is_real(&self) -> bool {
192 // XXX(nika): Support real files in the future?
193 false
194 }
195}
196
Nika Layzella9dbc182017-12-30 14:50:13 -0500197#[cfg(procmacro2_unstable)]
Nika Layzellb35a9a32017-12-30 14:34:35 -0500198impl AsRef<FileName> for SourceFile {
199 fn as_ref(&self) -> &FileName {
200 self.path()
Nika Layzellf8d5f212017-12-11 14:07:02 -0500201 }
202}
203
Nika Layzella9dbc182017-12-30 14:50:13 -0500204#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500205impl fmt::Debug for SourceFile {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 f.debug_struct("SourceFile")
Nika Layzellb35a9a32017-12-30 14:34:35 -0500208 .field("path", &self.path())
Nika Layzellf8d5f212017-12-11 14:07:02 -0500209 .field("is_real", &self.is_real())
210 .finish()
211 }
212}
213
Nika Layzella9dbc182017-12-30 14:50:13 -0500214#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500215#[derive(Clone, Copy, Debug, PartialEq, Eq)]
216pub struct LineColumn {
217 pub line: usize,
218 pub column: usize,
219}
220
Nika Layzella9dbc182017-12-30 14:50:13 -0500221#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500222thread_local! {
223 static CODEMAP: RefCell<Codemap> = RefCell::new(Codemap {
224 // NOTE: We start with a single dummy file which all call_site() and
225 // def_site() spans reference.
226 files: vec![FileInfo {
227 name: "<unspecified>".to_owned(),
228 span: Span { lo: 0, hi: 0 },
229 lines: vec![0],
230 }],
231 });
232}
233
Nika Layzella9dbc182017-12-30 14:50:13 -0500234#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500235struct FileInfo {
236 name: String,
237 span: Span,
238 lines: Vec<usize>,
239}
240
Nika Layzella9dbc182017-12-30 14:50:13 -0500241#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500242impl FileInfo {
243 fn offset_line_column(&self, offset: usize) -> LineColumn {
244 assert!(self.span_within(Span { lo: offset as u32, hi: offset as u32 }));
245 let offset = offset - self.span.lo as usize;
246 match self.lines.binary_search(&offset) {
247 Ok(found) => LineColumn {
248 line: found + 1,
249 column: 0
250 },
251 Err(idx) => LineColumn {
252 line: idx,
253 column: offset - self.lines[idx - 1]
254 },
255 }
256 }
257
258 fn span_within(&self, span: Span) -> bool {
259 span.lo >= self.span.lo && span.hi <= self.span.hi
260 }
261}
262
263/// Computes the offsets of each line in the given source string.
Nika Layzella9dbc182017-12-30 14:50:13 -0500264#[cfg(procmacro2_unstable)]
Nika Layzella0a7c3d2017-12-30 14:52:39 -0500265fn lines_offsets(s: &str) -> Vec<usize> {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500266 let mut lines = vec![0];
267 let mut prev = 0;
Nika Layzella0a7c3d2017-12-30 14:52:39 -0500268 while let Some(len) = s[prev..].find('\n') {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500269 prev += len + 1;
270 lines.push(prev);
271 }
272 lines
273}
274
Nika Layzella9dbc182017-12-30 14:50:13 -0500275#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500276struct Codemap {
277 files: Vec<FileInfo>,
278}
279
Nika Layzella9dbc182017-12-30 14:50:13 -0500280#[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500281impl Codemap {
282 fn next_start_pos(&self) -> u32 {
283 // Add 1 so there's always space between files.
284 //
285 // We'll always have at least 1 file, as we initialize our files list
286 // with a dummy file.
287 self.files.last().unwrap().span.hi + 1
288 }
289
290 fn add_file(&mut self, name: &str, src: &str) -> Span {
Nika Layzella0a7c3d2017-12-30 14:52:39 -0500291 let lines = lines_offsets(src);
Nika Layzellf8d5f212017-12-11 14:07:02 -0500292 let lo = self.next_start_pos();
293 // XXX(nika): Shouild we bother doing a checked cast or checked add here?
294 let span = Span { lo: lo, hi: lo + (src.len() as u32) };
295
296 self.files.push(FileInfo {
297 name: name.to_owned(),
298 span: span,
299 lines: lines,
300 });
301
302 span
303 }
304
305 fn fileinfo(&self, span: Span) -> &FileInfo {
306 for file in &self.files {
307 if file.span_within(span) {
308 return file;
309 }
310 }
311 panic!("Invalid span with no related FileInfo!");
312 }
313}
314
Alex Crichtone6085b72017-11-21 07:24:25 -0800315#[derive(Clone, Copy, Debug)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500316pub struct Span { lo: u32, hi: u32 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700317
318impl Span {
319 pub fn call_site() -> Span {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500320 Span { lo: 0, hi: 0 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700321 }
Alex Crichtone6085b72017-11-21 07:24:25 -0800322
323 pub fn def_site() -> Span {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500324 Span { lo: 0, hi: 0 }
325 }
326
Nika Layzella9dbc182017-12-30 14:50:13 -0500327 #[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500328 pub fn source_file(&self) -> SourceFile {
329 CODEMAP.with(|cm| {
330 let cm = cm.borrow();
331 let fi = cm.fileinfo(*self);
332 SourceFile {
Nika Layzellb35a9a32017-12-30 14:34:35 -0500333 name: FileName(fi.name.clone()),
Nika Layzellf8d5f212017-12-11 14:07:02 -0500334 }
335 })
336 }
337
Nika Layzella9dbc182017-12-30 14:50:13 -0500338 #[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500339 pub fn start(&self) -> LineColumn {
340 CODEMAP.with(|cm| {
341 let cm = cm.borrow();
342 let fi = cm.fileinfo(*self);
343 fi.offset_line_column(self.lo as usize)
344 })
345 }
346
Nika Layzella9dbc182017-12-30 14:50:13 -0500347 #[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500348 pub fn end(&self) -> LineColumn {
349 CODEMAP.with(|cm| {
350 let cm = cm.borrow();
351 let fi = cm.fileinfo(*self);
352 fi.offset_line_column(self.hi as usize)
353 })
354 }
355
Nika Layzella9dbc182017-12-30 14:50:13 -0500356 #[cfg(procmacro2_unstable)]
Nika Layzellf8d5f212017-12-11 14:07:02 -0500357 pub fn join(&self, other: Span) -> Option<Span> {
358 CODEMAP.with(|cm| {
359 let cm = cm.borrow();
360 // If `other` is not within the same FileInfo as us, return None.
361 if !cm.fileinfo(*self).span_within(other) {
362 return None;
363 }
364 Some(Span {
365 lo: cmp::min(self.lo, other.lo),
366 hi: cmp::max(self.hi, other.hi),
367 })
368 })
Alex Crichtone6085b72017-11-21 07:24:25 -0800369 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700370}
371
David Tolnay8ad3e3e2017-06-03 16:45:00 -0700372#[derive(Copy, Clone)]
Alex Crichton1a7f7622017-07-05 17:47:15 -0700373pub struct Term {
David Tolnay041bcd42017-06-03 09:18:04 -0700374 intern: usize,
375 not_send_sync: PhantomData<*const ()>,
376}
Alex Crichton44bffbc2017-05-19 17:51:59 -0700377
378thread_local!(static SYMBOLS: RefCell<Interner> = RefCell::new(Interner::new()));
379
Alex Crichton1a7f7622017-07-05 17:47:15 -0700380impl<'a> From<&'a str> for Term {
381 fn from(string: &'a str) -> Term {
382 Term {
David Tolnay041bcd42017-06-03 09:18:04 -0700383 intern: SYMBOLS.with(|s| s.borrow_mut().intern(string)),
384 not_send_sync: PhantomData,
385 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700386 }
387}
388
Alex Crichton1a7f7622017-07-05 17:47:15 -0700389impl ops::Deref for Term {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700390 type Target = str;
391
392 fn deref(&self) -> &str {
393 SYMBOLS.with(|interner| {
394 let interner = interner.borrow();
David Tolnay041bcd42017-06-03 09:18:04 -0700395 let s = interner.get(self.intern);
Alex Crichton44bffbc2017-05-19 17:51:59 -0700396 unsafe {
397 &*(s as *const str)
398 }
399 })
400 }
401}
402
Alex Crichton1a7f7622017-07-05 17:47:15 -0700403impl fmt::Debug for Term {
David Tolnay8ad3e3e2017-06-03 16:45:00 -0700404 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Alex Crichton1a7f7622017-07-05 17:47:15 -0700405 f.debug_tuple("Term").field(&&**self).finish()
David Tolnay8ad3e3e2017-06-03 16:45:00 -0700406 }
407}
408
Alex Crichton44bffbc2017-05-19 17:51:59 -0700409struct Interner {
410 string_to_index: HashMap<MyRc, usize>,
411 index_to_string: Vec<Rc<String>>,
412}
413
414#[derive(Hash, Eq, PartialEq)]
415struct MyRc(Rc<String>);
416
417impl Borrow<str> for MyRc {
418 fn borrow(&self) -> &str {
419 &self.0
420 }
421}
422
423impl Interner {
424 fn new() -> Interner {
425 Interner {
426 string_to_index: HashMap::new(),
427 index_to_string: Vec::new(),
428 }
429 }
430
431 fn intern(&mut self, s: &str) -> usize {
432 if let Some(&idx) = self.string_to_index.get(s) {
433 return idx
434 }
435 let s = Rc::new(s.to_string());
436 self.index_to_string.push(s.clone());
437 self.string_to_index.insert(MyRc(s), self.index_to_string.len() - 1);
438 self.index_to_string.len() - 1
439 }
440
441 fn get(&self, idx: usize) -> &str {
442 &self.index_to_string[idx]
443 }
444}
445
David Tolnay977f8282017-05-31 17:41:33 -0700446#[derive(Clone, Debug)]
Alex Crichton44bffbc2017-05-19 17:51:59 -0700447pub struct Literal(String);
448
Alex Crichton852d53d2017-05-19 19:25:08 -0700449impl Literal {
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700450 pub fn byte_char(byte: u8) -> Literal {
Alex Crichton76a5cc82017-05-23 07:01:44 -0700451 match byte {
452 0 => Literal(format!("b'\\0'")),
453 b'\"' => Literal(format!("b'\"'")),
454 n => {
455 let mut escaped = "b'".to_string();
456 escaped.extend(ascii::escape_default(n).map(|c| c as char));
457 escaped.push('\'');
458 Literal(escaped)
459 }
460 }
461 }
462
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700463 pub fn byte_string(bytes: &[u8]) -> Literal {
Alex Crichton852d53d2017-05-19 19:25:08 -0700464 let mut escaped = "b\"".to_string();
465 for b in bytes {
466 match *b {
467 b'\0' => escaped.push_str(r"\0"),
468 b'\t' => escaped.push_str(r"\t"),
469 b'\n' => escaped.push_str(r"\n"),
470 b'\r' => escaped.push_str(r"\r"),
471 b'"' => escaped.push_str("\\\""),
472 b'\\' => escaped.push_str("\\\\"),
473 b'\x20' ... b'\x7E' => escaped.push(*b as char),
474 _ => escaped.push_str(&format!("\\x{:02X}", b)),
475 }
476 }
477 escaped.push('"');
478 Literal(escaped)
479 }
Alex Crichton76a5cc82017-05-23 07:01:44 -0700480
481 pub fn doccomment(s: &str) -> Literal {
482 Literal(s.to_string())
483 }
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700484
Alex Crichton1a7f7622017-07-05 17:47:15 -0700485 pub fn float(s: f64) -> Literal {
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700486 Literal(s.to_string())
487 }
488
Alex Crichton1a7f7622017-07-05 17:47:15 -0700489 pub fn integer(s: i64) -> Literal {
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700490 Literal(s.to_string())
491 }
Alex Crichton31316622017-05-26 12:54:47 -0700492
493 pub fn raw_string(s: &str, pounds: usize) -> Literal {
494 let mut ret = format!("r");
495 ret.extend((0..pounds).map(|_| "#"));
496 ret.push('"');
497 ret.push_str(s);
498 ret.push('"');
499 ret.extend((0..pounds).map(|_| "#"));
500 Literal(ret)
501 }
502
503 pub fn raw_byte_string(s: &str, pounds: usize) -> Literal {
Alex Crichton7ed6d282017-05-26 13:42:50 -0700504 let mut ret = format!("br");
Alex Crichton31316622017-05-26 12:54:47 -0700505 ret.extend((0..pounds).map(|_| "#"));
506 ret.push('"');
507 ret.push_str(s);
508 ret.push('"');
509 ret.extend((0..pounds).map(|_| "#"));
510 Literal(ret)
511 }
Alex Crichton852d53d2017-05-19 19:25:08 -0700512}
513
Alex Crichton44bffbc2017-05-19 17:51:59 -0700514impl fmt::Display for Literal {
515 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516 self.0.fmt(f)
517 }
518}
519
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700520macro_rules! ints {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700521 ($($t:ty,)*) => {$(
522 impl From<$t> for Literal {
523 fn from(t: $t) -> Literal {
Alex Crichton852d53d2017-05-19 19:25:08 -0700524 Literal(format!(concat!("{}", stringify!($t)), t))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700525 }
526 }
527 )*}
528}
529
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700530ints! {
Alex Crichton852d53d2017-05-19 19:25:08 -0700531 u8, u16, u32, u64, usize,
532 i8, i16, i32, i64, isize,
Alex Crichton9c2fb0a2017-05-26 08:49:31 -0700533}
534
535macro_rules! floats {
536 ($($t:ty,)*) => {$(
537 impl From<$t> for Literal {
538 fn from(t: $t) -> Literal {
539 assert!(!t.is_nan());
540 assert!(!t.is_infinite());
541 Literal(format!(concat!("{}", stringify!($t)), t))
542 }
543 }
544 )*}
545}
546
547floats! {
Alex Crichton852d53d2017-05-19 19:25:08 -0700548 f32, f64,
549}
550
Alex Crichton44bffbc2017-05-19 17:51:59 -0700551impl<'a> From<&'a str> for Literal {
552 fn from(t: &'a str) -> Literal {
553 let mut s = t.chars().flat_map(|c| c.escape_default()).collect::<String>();
554 s.push('"');
555 s.insert(0, '"');
556 Literal(s)
557 }
558}
559
560impl From<char> for Literal {
561 fn from(t: char) -> Literal {
Alex Crichton2d0cf0b2017-05-26 14:00:16 -0700562 Literal(format!("'{}'", t.escape_default().collect::<String>()))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700563 }
564}
565
David Tolnay8e976c62017-06-01 12:12:29 -0700566named!(token_stream -> ::TokenStream, map!(
567 many0!(token_tree),
568 |trees| ::TokenStream(TokenStream { inner: trees })
569));
Alex Crichton44bffbc2017-05-19 17:51:59 -0700570
Nika Layzellf8d5f212017-12-11 14:07:02 -0500571fn token_tree(input: Cursor) -> PResult<TokenTree> {
572 let input = skip_whitespace(input);
573 let lo = input.off;
574 let (input, kind) = token_kind(input)?;
575 let hi = input.off;
576 Ok((input, TokenTree {
577 span: ::Span(Span {
578 lo: lo,
579 hi: hi,
580 }),
581 kind: kind,
582 }))
583}
Alex Crichton44bffbc2017-05-19 17:51:59 -0700584
Alex Crichton1a7f7622017-07-05 17:47:15 -0700585named!(token_kind -> TokenNode, alt!(
586 map!(delimited, |(d, s)| TokenNode::Group(d, s))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700587 |
Alex Crichton1a7f7622017-07-05 17:47:15 -0700588 map!(literal, TokenNode::Literal) // must be before symbol
Alex Crichton44bffbc2017-05-19 17:51:59 -0700589 |
Alex Crichton52725f72017-08-28 12:20:58 -0700590 symbol
Alex Crichton44bffbc2017-05-19 17:51:59 -0700591 |
Alex Crichton1a7f7622017-07-05 17:47:15 -0700592 map!(op, |(op, kind)| TokenNode::Op(op, kind))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700593));
594
David Tolnay8e976c62017-06-01 12:12:29 -0700595named!(delimited -> (Delimiter, ::TokenStream), alt!(
Alex Crichton44bffbc2017-05-19 17:51:59 -0700596 delimited!(
597 punct!("("),
598 token_stream,
599 punct!(")")
600 ) => { |ts| (Delimiter::Parenthesis, ts) }
601 |
602 delimited!(
603 punct!("["),
604 token_stream,
605 punct!("]")
606 ) => { |ts| (Delimiter::Bracket, ts) }
607 |
608 delimited!(
609 punct!("{"),
610 token_stream,
611 punct!("}")
612 ) => { |ts| (Delimiter::Brace, ts) }
613));
614
Nika Layzellf8d5f212017-12-11 14:07:02 -0500615fn symbol(mut input: Cursor) -> PResult<TokenNode> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700616 input = skip_whitespace(input);
617
618 let mut chars = input.char_indices();
David Tolnaya202d502017-06-01 12:26:55 -0700619
620 let lifetime = input.starts_with("'");
621 if lifetime {
622 chars.next();
623 }
624
Alex Crichton44bffbc2017-05-19 17:51:59 -0700625 match chars.next() {
626 Some((_, ch)) if UnicodeXID::is_xid_start(ch) || ch == '_' => {}
David Tolnay1218e122017-06-01 11:13:45 -0700627 _ => return Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -0700628 }
629
David Tolnay214c94c2017-06-01 12:42:56 -0700630 let mut end = input.len();
Alex Crichton44bffbc2017-05-19 17:51:59 -0700631 for (i, ch) in chars {
632 if !UnicodeXID::is_xid_continue(ch) {
David Tolnay214c94c2017-06-01 12:42:56 -0700633 end = i;
634 break;
Alex Crichton44bffbc2017-05-19 17:51:59 -0700635 }
636 }
637
Nika Layzellf8d5f212017-12-11 14:07:02 -0500638 if lifetime && &input.rest[..end] != "'static" && KEYWORDS.contains(&&input.rest[1..end]) {
David Tolnay214c94c2017-06-01 12:42:56 -0700639 Err(LexError)
640 } else {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500641 let a = &input.rest[..end];
Alex Crichton52725f72017-08-28 12:20:58 -0700642 if a == "_" {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500643 Ok((input.advance(end), TokenNode::Op('_', Spacing::Alone)))
Alex Crichton52725f72017-08-28 12:20:58 -0700644 } else {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500645 Ok((input.advance(end), TokenNode::Term(::Term::intern(a))))
Alex Crichton52725f72017-08-28 12:20:58 -0700646 }
David Tolnay214c94c2017-06-01 12:42:56 -0700647 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700648}
649
David Tolnay214c94c2017-06-01 12:42:56 -0700650// From https://github.com/rust-lang/rust/blob/master/src/libsyntax_pos/symbol.rs
651static KEYWORDS: &'static [&'static str] = &[
652 "abstract", "alignof", "as", "become", "box", "break", "const", "continue",
653 "crate", "do", "else", "enum", "extern", "false", "final", "fn", "for",
654 "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut",
655 "offsetof", "override", "priv", "proc", "pub", "pure", "ref", "return",
656 "self", "Self", "sizeof", "static", "struct", "super", "trait", "true",
657 "type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while",
658 "yield",
659];
660
Nika Layzellf8d5f212017-12-11 14:07:02 -0500661fn literal(input: Cursor) -> PResult<::Literal> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700662 let input_no_ws = skip_whitespace(input);
663
664 match literal_nocapture(input_no_ws) {
David Tolnay1218e122017-06-01 11:13:45 -0700665 Ok((a, ())) => {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700666 let start = input.len() - input_no_ws.len();
667 let len = input_no_ws.len() - a.len();
668 let end = start + len;
Nika Layzellf8d5f212017-12-11 14:07:02 -0500669 Ok((a, ::Literal(Literal(input.rest[start..end].to_string()))))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700670 }
David Tolnay1218e122017-06-01 11:13:45 -0700671 Err(LexError) => Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -0700672 }
673}
674
675named!(literal_nocapture -> (), alt!(
676 string
677 |
678 byte_string
679 |
680 byte
681 |
682 character
683 |
684 float
685 |
686 int
687 |
688 boolean
689 |
690 doc_comment
691));
692
693named!(string -> (), alt!(
694 quoted_string
695 |
696 preceded!(
697 punct!("r"),
698 raw_string
699 ) => { |_| () }
700));
701
702named!(quoted_string -> (), delimited!(
703 punct!("\""),
704 cooked_string,
705 tag!("\"")
706));
707
Nika Layzellf8d5f212017-12-11 14:07:02 -0500708fn cooked_string(input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700709 let mut chars = input.char_indices().peekable();
710 while let Some((byte_offset, ch)) = chars.next() {
711 match ch {
712 '"' => {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500713 return Ok((input.advance(byte_offset), ()));
Alex Crichton44bffbc2017-05-19 17:51:59 -0700714 }
715 '\r' => {
716 if let Some((_, '\n')) = chars.next() {
717 // ...
718 } else {
719 break;
720 }
721 }
722 '\\' => {
723 match chars.next() {
724 Some((_, 'x')) => {
725 if !backslash_x_char(&mut chars) {
726 break
727 }
728 }
729 Some((_, 'n')) |
730 Some((_, 'r')) |
731 Some((_, 't')) |
732 Some((_, '\\')) |
733 Some((_, '\'')) |
734 Some((_, '"')) |
735 Some((_, '0')) => {}
736 Some((_, 'u')) => {
737 if !backslash_u(&mut chars) {
738 break
739 }
740 }
741 Some((_, '\n')) | Some((_, '\r')) => {
742 while let Some(&(_, ch)) = chars.peek() {
743 if ch.is_whitespace() {
744 chars.next();
745 } else {
746 break;
747 }
748 }
749 }
750 _ => break,
751 }
752 }
753 _ch => {}
754 }
755 }
David Tolnay1218e122017-06-01 11:13:45 -0700756 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -0700757}
758
759named!(byte_string -> (), alt!(
760 delimited!(
761 punct!("b\""),
762 cooked_byte_string,
763 tag!("\"")
764 ) => { |_| () }
765 |
766 preceded!(
767 punct!("br"),
768 raw_string
769 ) => { |_| () }
770));
771
Nika Layzellf8d5f212017-12-11 14:07:02 -0500772fn cooked_byte_string(mut input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700773 let mut bytes = input.bytes().enumerate();
774 'outer: while let Some((offset, b)) = bytes.next() {
775 match b {
776 b'"' => {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500777 return Ok((input.advance(offset), ()));
Alex Crichton44bffbc2017-05-19 17:51:59 -0700778 }
779 b'\r' => {
780 if let Some((_, b'\n')) = bytes.next() {
781 // ...
782 } else {
783 break;
784 }
785 }
786 b'\\' => {
787 match bytes.next() {
788 Some((_, b'x')) => {
789 if !backslash_x_byte(&mut bytes) {
790 break
791 }
792 }
793 Some((_, b'n')) |
794 Some((_, b'r')) |
795 Some((_, b't')) |
796 Some((_, b'\\')) |
797 Some((_, b'0')) |
798 Some((_, b'\'')) |
799 Some((_, b'"')) => {}
800 Some((newline, b'\n')) |
801 Some((newline, b'\r')) => {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500802 let rest = input.advance(newline + 1);
Alex Crichton44bffbc2017-05-19 17:51:59 -0700803 for (offset, ch) in rest.char_indices() {
804 if !ch.is_whitespace() {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500805 input = rest.advance(offset);
Alex Crichton44bffbc2017-05-19 17:51:59 -0700806 bytes = input.bytes().enumerate();
807 continue 'outer;
808 }
809 }
810 break;
811 }
812 _ => break,
813 }
814 }
815 b if b < 0x80 => {}
816 _ => break,
817 }
818 }
David Tolnay1218e122017-06-01 11:13:45 -0700819 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -0700820}
821
Nika Layzellf8d5f212017-12-11 14:07:02 -0500822fn raw_string(input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700823 let mut chars = input.char_indices();
824 let mut n = 0;
825 while let Some((byte_offset, ch)) = chars.next() {
826 match ch {
827 '"' => {
828 n = byte_offset;
829 break;
830 }
831 '#' => {}
David Tolnay1218e122017-06-01 11:13:45 -0700832 _ => return Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -0700833 }
834 }
835 for (byte_offset, ch) in chars {
836 match ch {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500837 '"' if input.advance(byte_offset + 1).starts_with(&input.rest[..n]) => {
838 let rest = input.advance(byte_offset + 1 + n);
David Tolnay1218e122017-06-01 11:13:45 -0700839 return Ok((rest, ()))
Alex Crichton44bffbc2017-05-19 17:51:59 -0700840 }
841 '\r' => {}
842 _ => {}
843 }
844 }
David Tolnay1218e122017-06-01 11:13:45 -0700845 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -0700846}
847
848named!(byte -> (), do_parse!(
849 punct!("b") >>
850 tag!("'") >>
851 cooked_byte >>
852 tag!("'") >>
853 (())
854));
855
Nika Layzellf8d5f212017-12-11 14:07:02 -0500856fn cooked_byte(input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700857 let mut bytes = input.bytes().enumerate();
858 let ok = match bytes.next().map(|(_, b)| b) {
859 Some(b'\\') => {
860 match bytes.next().map(|(_, b)| b) {
861 Some(b'x') => backslash_x_byte(&mut bytes),
862 Some(b'n') |
863 Some(b'r') |
864 Some(b't') |
865 Some(b'\\') |
866 Some(b'0') |
867 Some(b'\'') |
868 Some(b'"') => true,
869 _ => false,
870 }
871 }
872 b => b.is_some(),
873 };
874 if ok {
875 match bytes.next() {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500876 Some((offset, _)) => Ok((input.advance(offset), ())),
877 None => Ok((input.advance(input.len()), ())),
Alex Crichton44bffbc2017-05-19 17:51:59 -0700878 }
879 } else {
David Tolnay1218e122017-06-01 11:13:45 -0700880 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -0700881 }
882}
883
884named!(character -> (), do_parse!(
885 punct!("'") >>
886 cooked_char >>
887 tag!("'") >>
888 (())
889));
890
Nika Layzellf8d5f212017-12-11 14:07:02 -0500891fn cooked_char(input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700892 let mut chars = input.char_indices();
893 let ok = match chars.next().map(|(_, ch)| ch) {
894 Some('\\') => {
895 match chars.next().map(|(_, ch)| ch) {
896 Some('x') => backslash_x_char(&mut chars),
897 Some('u') => backslash_u(&mut chars),
898 Some('n') |
899 Some('r') |
900 Some('t') |
901 Some('\\') |
902 Some('0') |
903 Some('\'') |
904 Some('"') => true,
905 _ => false,
906 }
907 }
908 ch => ch.is_some(),
909 };
910 if ok {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500911 match chars.next() {
912 Some((idx, _)) => Ok((input.advance(idx), ())),
913 None => Ok((input.advance(input.len()), ())),
914 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700915 } else {
David Tolnay1218e122017-06-01 11:13:45 -0700916 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -0700917 }
918}
919
920macro_rules! next_ch {
921 ($chars:ident @ $pat:pat $(| $rest:pat)*) => {
922 match $chars.next() {
923 Some((_, ch)) => match ch {
924 $pat $(| $rest)* => ch,
925 _ => return false,
926 },
927 None => return false
928 }
929 };
930}
931
932fn backslash_x_char<I>(chars: &mut I) -> bool
933 where I: Iterator<Item = (usize, char)>
934{
935 next_ch!(chars @ '0'...'7');
936 next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F');
937 true
938}
939
940fn backslash_x_byte<I>(chars: &mut I) -> bool
941 where I: Iterator<Item = (usize, u8)>
942{
943 next_ch!(chars @ b'0'...b'9' | b'a'...b'f' | b'A'...b'F');
944 next_ch!(chars @ b'0'...b'9' | b'a'...b'f' | b'A'...b'F');
945 true
946}
947
948fn backslash_u<I>(chars: &mut I) -> bool
949 where I: Iterator<Item = (usize, char)>
950{
951 next_ch!(chars @ '{');
952 next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F');
David Tolnay8d109342017-12-25 18:24:45 -0500953 loop {
954 let c = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '_' | '}');
955 if c == '}' {
956 return true;
957 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700958 }
Alex Crichton44bffbc2017-05-19 17:51:59 -0700959}
960
Nika Layzellf8d5f212017-12-11 14:07:02 -0500961fn float(input: Cursor) -> PResult<()> {
David Tolnay744a6b82017-06-01 11:34:29 -0700962 let (rest, ()) = float_digits(input)?;
963 for suffix in &["f32", "f64"] {
964 if rest.starts_with(suffix) {
Nika Layzellf8d5f212017-12-11 14:07:02 -0500965 return word_break(rest.advance(suffix.len()));
David Tolnay744a6b82017-06-01 11:34:29 -0700966 }
967 }
968 word_break(rest)
969}
Alex Crichton44bffbc2017-05-19 17:51:59 -0700970
Nika Layzellf8d5f212017-12-11 14:07:02 -0500971fn float_digits(input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -0700972 let mut chars = input.chars().peekable();
973 match chars.next() {
974 Some(ch) if ch >= '0' && ch <= '9' => {}
David Tolnay1218e122017-06-01 11:13:45 -0700975 _ => return Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -0700976 }
977
978 let mut len = 1;
979 let mut has_dot = false;
980 let mut has_exp = false;
981 while let Some(&ch) = chars.peek() {
982 match ch {
983 '0'...'9' | '_' => {
984 chars.next();
985 len += 1;
986 }
987 '.' => {
988 if has_dot {
989 break;
990 }
991 chars.next();
992 if chars.peek()
993 .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
994 .unwrap_or(false) {
David Tolnay1218e122017-06-01 11:13:45 -0700995 return Err(LexError);
Alex Crichton44bffbc2017-05-19 17:51:59 -0700996 }
997 len += 1;
998 has_dot = true;
999 }
1000 'e' | 'E' => {
1001 chars.next();
1002 len += 1;
1003 has_exp = true;
1004 break;
1005 }
1006 _ => break,
1007 }
1008 }
1009
Nika Layzellf8d5f212017-12-11 14:07:02 -05001010 let rest = input.advance(len);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001011 if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
David Tolnay1218e122017-06-01 11:13:45 -07001012 return Err(LexError);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001013 }
1014
1015 if has_exp {
1016 let mut has_exp_value = false;
1017 while let Some(&ch) = chars.peek() {
1018 match ch {
1019 '+' | '-' => {
1020 if has_exp_value {
1021 break;
1022 }
1023 chars.next();
1024 len += 1;
1025 }
1026 '0'...'9' => {
1027 chars.next();
1028 len += 1;
1029 has_exp_value = true;
1030 }
1031 '_' => {
1032 chars.next();
1033 len += 1;
1034 }
1035 _ => break,
1036 }
1037 }
1038 if !has_exp_value {
David Tolnay1218e122017-06-01 11:13:45 -07001039 return Err(LexError);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001040 }
1041 }
1042
Nika Layzellf8d5f212017-12-11 14:07:02 -05001043 Ok((input.advance(len), ()))
Alex Crichton44bffbc2017-05-19 17:51:59 -07001044}
1045
Nika Layzellf8d5f212017-12-11 14:07:02 -05001046fn int(input: Cursor) -> PResult<()> {
David Tolnay744a6b82017-06-01 11:34:29 -07001047 let (rest, ()) = digits(input)?;
1048 for suffix in &[
1049 "isize",
1050 "i8",
1051 "i16",
1052 "i32",
1053 "i64",
1054 "i128",
1055 "usize",
1056 "u8",
1057 "u16",
1058 "u32",
1059 "u64",
1060 "u128",
1061 ] {
1062 if rest.starts_with(suffix) {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001063 return word_break(rest.advance(suffix.len()));
David Tolnay744a6b82017-06-01 11:34:29 -07001064 }
1065 }
1066 word_break(rest)
1067}
Alex Crichton44bffbc2017-05-19 17:51:59 -07001068
Nika Layzellf8d5f212017-12-11 14:07:02 -05001069fn digits(mut input: Cursor) -> PResult<()> {
Alex Crichton44bffbc2017-05-19 17:51:59 -07001070 let base = if input.starts_with("0x") {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001071 input = input.advance(2);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001072 16
1073 } else if input.starts_with("0o") {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001074 input = input.advance(2);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001075 8
1076 } else if input.starts_with("0b") {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001077 input = input.advance(2);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001078 2
1079 } else {
1080 10
1081 };
1082
Alex Crichton44bffbc2017-05-19 17:51:59 -07001083 let mut len = 0;
1084 let mut empty = true;
1085 for b in input.bytes() {
1086 let digit = match b {
1087 b'0'...b'9' => (b - b'0') as u64,
1088 b'a'...b'f' => 10 + (b - b'a') as u64,
1089 b'A'...b'F' => 10 + (b - b'A') as u64,
1090 b'_' => {
1091 if empty && base == 10 {
David Tolnay1218e122017-06-01 11:13:45 -07001092 return Err(LexError);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001093 }
1094 len += 1;
1095 continue;
1096 }
1097 _ => break,
1098 };
1099 if digit >= base {
David Tolnay1218e122017-06-01 11:13:45 -07001100 return Err(LexError);
Alex Crichton44bffbc2017-05-19 17:51:59 -07001101 }
Alex Crichton44bffbc2017-05-19 17:51:59 -07001102 len += 1;
1103 empty = false;
1104 }
1105 if empty {
David Tolnay1218e122017-06-01 11:13:45 -07001106 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -07001107 } else {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001108 Ok((input.advance(len), ()))
Alex Crichton44bffbc2017-05-19 17:51:59 -07001109 }
1110}
1111
1112named!(boolean -> (), alt!(
1113 keyword!("true") => { |_| () }
1114 |
1115 keyword!("false") => { |_| () }
1116));
1117
Nika Layzellf8d5f212017-12-11 14:07:02 -05001118fn op(input: Cursor) -> PResult<(char, Spacing)> {
David Tolnayea75c5f2017-05-31 23:40:33 -07001119 let input = skip_whitespace(input);
1120 match op_char(input) {
David Tolnay1218e122017-06-01 11:13:45 -07001121 Ok((rest, ch)) => {
David Tolnayea75c5f2017-05-31 23:40:33 -07001122 let kind = match op_char(rest) {
Alex Crichton1a7f7622017-07-05 17:47:15 -07001123 Ok(_) => Spacing::Joint,
1124 Err(LexError) => Spacing::Alone,
David Tolnayea75c5f2017-05-31 23:40:33 -07001125 };
David Tolnay1218e122017-06-01 11:13:45 -07001126 Ok((rest, (ch, kind)))
David Tolnayea75c5f2017-05-31 23:40:33 -07001127 }
David Tolnay1218e122017-06-01 11:13:45 -07001128 Err(LexError) => Err(LexError),
Alex Crichton44bffbc2017-05-19 17:51:59 -07001129 }
1130}
1131
Nika Layzellf8d5f212017-12-11 14:07:02 -05001132fn op_char(input: Cursor) -> PResult<char> {
David Tolnayea75c5f2017-05-31 23:40:33 -07001133 let mut chars = input.chars();
1134 let first = match chars.next() {
1135 Some(ch) => ch,
1136 None => {
David Tolnay1218e122017-06-01 11:13:45 -07001137 return Err(LexError);
David Tolnayea75c5f2017-05-31 23:40:33 -07001138 }
1139 };
1140 let recognized = "~!@#$%^&*-=+|;:,<.>/?";
1141 if recognized.contains(first) {
Nika Layzellf8d5f212017-12-11 14:07:02 -05001142 Ok((input.advance(first.len_utf8()), first))
Alex Crichton44bffbc2017-05-19 17:51:59 -07001143 } else {
David Tolnay1218e122017-06-01 11:13:45 -07001144 Err(LexError)
Alex Crichton44bffbc2017-05-19 17:51:59 -07001145 }
1146}
1147
Alex Crichton44bffbc2017-05-19 17:51:59 -07001148named!(doc_comment -> (), alt!(
1149 do_parse!(
1150 punct!("//!") >>
1151 take_until!("\n") >>
1152 (())
1153 )
1154 |
1155 do_parse!(
1156 option!(whitespace) >>
1157 peek!(tag!("/*!")) >>
1158 block_comment >>
1159 (())
1160 )
1161 |
1162 do_parse!(
1163 punct!("///") >>
1164 not!(tag!("/")) >>
1165 take_until!("\n") >>
1166 (())
1167 )
1168 |
1169 do_parse!(
1170 option!(whitespace) >>
1171 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
1172 block_comment >>
1173 (())
1174 )
1175));