blob: c051a8dd94591913fbfbc55c8091ffe10b644c3f [file] [log] [blame]
David Tolnay18c754c2018-08-21 23:26:58 -04001use std::cell::RefCell;
2
David Tolnay6d67c742018-08-24 20:42:39 -04003use buffer::Cursor;
David Tolnay18c754c2018-08-21 23:26:58 -04004use proc_macro2::Span;
David Tolnay18c754c2018-08-21 23:26:58 -04005
David Tolnay776f8e02018-08-24 22:32:10 -04006use span::IntoSpans;
David Tolnay544a90f2018-08-24 20:34:03 -04007use super::error;
8use super::parse::Error;
David Tolnay776f8e02018-08-24 22:32:10 -04009use token::Token;
David Tolnay18c754c2018-08-21 23:26:58 -040010
11/// Support for checking the next token in a stream to decide how to parse.
12///
13/// Use [`ParseStream::lookahead1`] to construct this object.
14///
15/// [`ParseStream::lookahead1`]: struct.ParseBuffer.html#method.lookahead1
16pub struct Lookahead1<'a> {
17 scope: Span,
18 cursor: Cursor<'a>,
19 comparisons: RefCell<Vec<String>>,
20}
21
22impl<'a> Lookahead1<'a> {
23 // Not public API.
24 #[doc(hidden)]
25 pub fn new(scope: Span, cursor: Cursor<'a>) -> Self {
26 Lookahead1 {
27 scope: scope,
28 cursor: cursor,
29 comparisons: RefCell::new(Vec::new()),
30 }
31 }
32
33 pub fn peek<T: Peek>(&self, token: T) -> bool {
34 let _ = token;
35 if T::Token::peek(self) {
36 return true;
37 }
38 self.comparisons.borrow_mut().push(T::Token::display());
39 false
40 }
41
42 pub fn error(self) -> Error {
David Tolnayda1dc7c2018-08-24 11:57:28 -040043 let comparisons = self.comparisons.borrow();
44 match comparisons.len() {
45 0 => if self.cursor.eof() {
46 Error::new(self.scope, "unexpected end of input")
47 } else {
48 Error::new(self.cursor.span(), "unexpected token")
49 },
50 1 => {
51 let message = format!("expected {}", comparisons[0]);
52 error::new_at(self.scope, self.cursor, message)
53 }
54 _ => {
David Tolnayc6e86c72018-08-24 12:28:40 -040055 let join = comparisons.join(", ");
56 let message = format!("expected one of: {}", join);
David Tolnayda1dc7c2018-08-24 11:57:28 -040057 error::new_at(self.scope, self.cursor, message)
58 }
59 }
David Tolnay18c754c2018-08-21 23:26:58 -040060 }
61}
62
63/// Types that can be parsed by looking at just one token.
64///
65/// This trait is sealed and cannot be implemented for types outside of Syn.
66pub trait Peek: private::Sealed {
67 // Not public API.
68 #[doc(hidden)]
69 type Token: Token;
70}
71
David Tolnay2b687b02018-08-24 13:36:36 -040072impl<F: FnOnce(TokenMarker) -> T, T: Token> Peek for F {
David Tolnay18c754c2018-08-21 23:26:58 -040073 type Token = T;
74}
75
David Tolnay2b687b02018-08-24 13:36:36 -040076pub enum TokenMarker {}
77
David Tolnay776f8e02018-08-24 22:32:10 -040078impl<S> IntoSpans<S> for TokenMarker {
79 fn into_spans(self) -> S {
80 match self {}
81 }
82}
83
David Tolnay18c754c2018-08-21 23:26:58 -040084// Not public API.
85#[doc(hidden)]
86pub fn is_token(lookahead: &Lookahead1, repr: &'static str) -> bool {
87 if let Some((token, _rest)) = lookahead.cursor.token_tree() {
88 token.to_string() == repr
89 } else {
90 false
91 }
92}
93
94mod private {
David Tolnay2b687b02018-08-24 13:36:36 -040095 use super::{Token, TokenMarker};
David Tolnay18c754c2018-08-21 23:26:58 -040096 pub trait Sealed {}
David Tolnay2b687b02018-08-24 13:36:36 -040097 impl<F: FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
David Tolnay18c754c2018-08-21 23:26:58 -040098}