1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 pub use self::BinOpToken::*;
12 pub use self::Nonterminal::*;
13 pub use self::DelimToken::*;
15 pub use self::Token::*;
21 use serialize::{Decodable, Decoder, Encodable, Encoder};
23 use syntax::parse::parse_stream_from_source_str;
24 use syntax_pos::{self, Span, FileName};
25 use tokenstream::{TokenStream, TokenTree};
30 use rustc_data_structures::sync::Lrc;
32 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
47 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
49 /// A round parenthesis: `(` or `)`
51 /// A square bracket: `[` or `]`
53 /// A curly brace: `{` or `}`
55 /// An empty delimiter
60 pub fn len(self) -> usize {
61 if self == NoDelim { 0 } else { 1 }
64 pub fn is_empty(self) -> bool {
69 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
76 StrRaw(ast::Name, usize), /* raw str delimited by n hash symbols */
78 ByteStrRaw(ast::Name, usize), /* raw byte str delimited by n hash symbols */
82 pub fn short_name(&self) -> &'static str {
86 Integer(_) => "integer",
88 Str_(_) | StrRaw(..) => "string",
89 ByteStr(_) | ByteStrRaw(..) => "byte string"
94 fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool {
95 let ident_token: Token = Ident(ident, is_raw);
97 !ident_token.is_reserved_ident() ||
98 ident_token.is_path_segment_keyword() ||
101 keywords::Box.name(),
102 keywords::Break.name(),
103 keywords::Continue.name(),
104 keywords::False.name(),
105 keywords::For.name(),
107 keywords::Loop.name(),
108 keywords::Match.name(),
109 keywords::Move.name(),
110 keywords::Return.name(),
111 keywords::True.name(),
112 keywords::Unsafe.name(),
113 keywords::While.name(),
114 keywords::Yield.name(),
115 keywords::Static.name(),
116 ].contains(&ident.name)
119 fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool {
120 let ident_token: Token = Ident(ident, is_raw);
122 !ident_token.is_reserved_ident() ||
123 ident_token.is_path_segment_keyword() ||
125 keywords::Underscore.name(),
126 keywords::For.name(),
127 keywords::Impl.name(),
129 keywords::Unsafe.name(),
130 keywords::Extern.name(),
131 keywords::Typeof.name(),
132 ].contains(&ident.name)
135 pub fn is_path_segment_keyword(id: ast::Ident) -> bool {
136 id.name == keywords::Super.name() ||
137 id.name == keywords::SelfValue.name() ||
138 id.name == keywords::SelfType.name() ||
139 id.name == keywords::Extern.name() ||
140 id.name == keywords::Crate.name() ||
141 id.name == keywords::CrateRoot.name() ||
142 id.name == keywords::DollarCrate.name()
145 // We see this identifier in a normal identifier position, like variable name or a type.
146 // How was it written originally? Did it use the raw form? Let's try to guess.
147 pub fn is_raw_guess(ident: ast::Ident) -> bool {
148 ident.name != keywords::Invalid.name() &&
149 is_reserved_ident(ident) && !is_path_segment_keyword(ident)
152 // Returns true for reserved identifiers used internally for elided lifetimes,
153 // unnamed method parameters, crate root module, error recovery etc.
154 pub fn is_special_ident(id: ast::Ident) -> bool {
155 id.name <= keywords::Underscore.name()
158 /// Returns `true` if the token is a keyword used in the language.
159 pub fn is_used_keyword(id: ast::Ident) -> bool {
160 id.name >= keywords::As.name() && id.name <= keywords::While.name()
163 /// Returns `true` if the token is a keyword reserved for possible future use.
164 pub fn is_unused_keyword(id: ast::Ident) -> bool {
165 id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name()
168 /// Returns `true` if the token is either a special identifier or a keyword.
169 pub fn is_reserved_ident(id: ast::Ident) -> bool {
170 is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id)
173 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
175 /* Expression-operator symbols. */
190 /* Structural symbols */
196 DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
207 /// An opening delimiter, eg. `{`
208 OpenDelim(DelimToken),
209 /// A closing delimiter, eg. `}`
210 CloseDelim(DelimToken),
213 Literal(Lit, Option<ast::Name>),
215 /* Name components */
216 Ident(ast::Ident, /* is_raw */ bool),
217 Lifetime(ast::Ident),
219 // The `LazyTokenStream` is a pure function of the `Nonterminal`,
220 // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc.
221 Interpolated(Lrc<(Nonterminal, LazyTokenStream)>),
222 // Can be expanded into several tokens.
224 DocComment(ast::Name),
226 // Junk. These carry no data because we don't really care about the data
227 // they *would* carry, and don't really want to allocate a new ident for
228 // them. Instead, users could extract that from the associated span.
240 pub fn interpolated(nt: Nonterminal) -> Token {
241 Token::Interpolated(Lrc::new((nt, LazyTokenStream::new())))
244 /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
245 pub fn from_ast_ident(ident: ast::Ident) -> Token {
246 Ident(ident, is_raw_guess(ident))
249 /// Returns `true` if the token starts with '>'.
250 pub fn is_like_gt(&self) -> bool {
252 BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true,
257 /// Returns `true` if the token can appear at the start of an expression.
258 pub fn can_begin_expr(&self) -> bool {
260 Ident(ident, is_raw) =>
261 ident_can_begin_expr(ident, is_raw), // value name or keyword
262 OpenDelim(..) | // tuple, array or block
263 Literal(..) | // literal
264 Not | // operator not
265 BinOp(Minus) | // unary minus
266 BinOp(Star) | // dereference
267 BinOp(Or) | OrOr | // closure
268 BinOp(And) | // reference
269 AndAnd | // double reference
270 // DotDotDot is no longer supported, but we need some way to display the error
271 DotDot | DotDotDot | DotDotEq | // range notation
272 Lt | BinOp(Shl) | // associated path
273 ModSep | // global path
274 Pound => true, // expression attributes
275 Interpolated(ref nt) => match nt.0 {
276 NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
283 /// Returns `true` if the token can appear at the start of a type.
284 pub fn can_begin_type(&self) -> bool {
286 Ident(ident, is_raw) =>
287 ident_can_begin_type(ident, is_raw), // type name or keyword
288 OpenDelim(Paren) | // tuple
289 OpenDelim(Bracket) | // array
291 BinOp(Star) | // raw pointer
292 BinOp(And) | // reference
293 AndAnd | // double reference
294 Question | // maybe bound in trait object
295 Lifetime(..) | // lifetime bound in trait object
296 Lt | BinOp(Shl) | // associated path
297 ModSep => true, // global path
298 Interpolated(ref nt) => match nt.0 {
299 NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true,
306 /// Returns `true` if the token can appear at the start of a generic bound.
307 pub fn can_begin_bound(&self) -> bool {
308 self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
309 self == &Question || self == &OpenDelim(Paren)
312 /// Returns `true` if the token is any literal
313 pub fn is_lit(&self) -> bool {
320 /// Returns an identifier if this token is an identifier.
321 pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> {
323 Ident(ident, is_raw) => Some((ident, is_raw)),
324 Interpolated(ref nt) => match nt.0 {
325 NtIdent(ident, is_raw) => Some((ident, is_raw)),
331 /// Returns a lifetime identifier if this token is a lifetime.
332 pub fn lifetime(&self) -> Option<ast::Ident> {
334 Lifetime(ident) => Some(ident),
335 Interpolated(ref nt) => match nt.0 {
336 NtLifetime(ident) => Some(ident),
342 /// Returns `true` if the token is an identifier.
343 pub fn is_ident(&self) -> bool {
344 self.ident().is_some()
346 /// Returns `true` if the token is a lifetime.
347 pub fn is_lifetime(&self) -> bool {
348 self.lifetime().is_some()
351 /// Returns `true` if the token is a documentation comment.
352 pub fn is_doc_comment(&self) -> bool {
354 DocComment(..) => true,
359 /// Returns `true` if the token is interpolated.
360 pub fn is_interpolated(&self) -> bool {
362 Interpolated(..) => true,
367 /// Returns `true` if the token is an interpolated path.
368 pub fn is_path(&self) -> bool {
369 if let Interpolated(ref nt) = *self {
370 if let NtPath(..) = nt.0 {
377 /// Returns `true` if the token is either the `mut` or `const` keyword.
378 pub fn is_mutability(&self) -> bool {
379 self.is_keyword(keywords::Mut) ||
380 self.is_keyword(keywords::Const)
383 pub fn is_qpath_start(&self) -> bool {
384 self == &Lt || self == &BinOp(Shl)
387 pub fn is_path_start(&self) -> bool {
388 self == &ModSep || self.is_qpath_start() || self.is_path() ||
389 self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
392 /// Returns `true` if the token is a given keyword, `kw`.
393 pub fn is_keyword(&self, kw: keywords::Keyword) -> bool {
394 self.ident().map(|(ident, is_raw)| ident.name == kw.name() && !is_raw).unwrap_or(false)
397 pub fn is_path_segment_keyword(&self) -> bool {
399 Some((id, false)) => is_path_segment_keyword(id),
404 // Returns true for reserved identifiers used internally for elided lifetimes,
405 // unnamed method parameters, crate root module, error recovery etc.
406 pub fn is_special_ident(&self) -> bool {
408 Some((id, false)) => is_special_ident(id),
413 /// Returns `true` if the token is a keyword used in the language.
414 pub fn is_used_keyword(&self) -> bool {
416 Some((id, false)) => is_used_keyword(id),
421 /// Returns `true` if the token is a keyword reserved for possible future use.
422 pub fn is_unused_keyword(&self) -> bool {
424 Some((id, false)) => is_unused_keyword(id),
429 /// Returns `true` if the token is either a special identifier or a keyword.
430 pub fn is_reserved_ident(&self) -> bool {
432 Some((id, false)) => is_reserved_ident(id),
437 pub fn glue(self, joint: Token) -> Option<Token> {
448 BinOp(Minus) => LArrow,
461 BinOp(op) => match joint {
463 BinOp(And) if op == And => AndAnd,
464 BinOp(Or) if op == Or => OrOr,
465 Gt if op == Minus => RArrow,
474 DotDot => match joint {
479 Colon => match joint {
484 Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
485 DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
486 Question | OpenDelim(..) | CloseDelim(..) => return None,
488 Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
489 Whitespace | Comment | Shebang(..) | Eof => return None,
493 /// Returns tokens that are likely to be typed accidentally instead of the current token.
494 /// Enables better error recovery when the wrong token is found.
495 pub fn similar_tokens(&self) -> Option<Vec<Token>> {
497 Comma => Some(vec![Dot, Lt]),
498 Semi => Some(vec![Colon]),
503 pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
506 let nt = match *self {
507 Token::Interpolated(ref nt) => nt,
508 _ => panic!("only works on interpolated tokens"),
511 // An `Interpolated` token means that we have a `Nonterminal`
512 // which is often a parsed AST item. At this point we now need
513 // to convert the parsed AST to an actual token stream, e.g.
514 // un-parse it basically.
516 // Unfortunately there's not really a great way to do that in a
517 // guaranteed lossless fashion right now. The fallback here is
518 // to just stringify the AST node and reparse it, but this loses
519 // all span information.
521 // As a result, some AST nodes are annotated with the token
522 // stream they came from. Attempt to extract these lossless
523 // token streams before we fall back to the stringification.
524 let mut tokens = None;
527 Nonterminal::NtItem(ref item) => {
528 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
530 Nonterminal::NtTraitItem(ref item) => {
531 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
533 Nonterminal::NtImplItem(ref item) => {
534 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
536 Nonterminal::NtIdent(ident, is_raw) => {
537 let token = Token::Ident(ident, is_raw);
538 tokens = Some(TokenTree::Token(ident.span, token).into());
540 Nonterminal::NtLifetime(ident) => {
541 let token = Token::Lifetime(ident);
542 tokens = Some(TokenTree::Token(ident.span, token).into());
544 Nonterminal::NtTT(ref tt) => {
545 tokens = Some(tt.clone().into());
550 tokens.unwrap_or_else(|| {
552 // FIXME(jseyfried): Avoid this pretty-print + reparse hack
553 let source = pprust::token_to_string(self);
554 parse_stream_from_source_str(FileName::MacroExpansion, source, sess, Some(span))
560 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
561 /// For interpolation during macro expansion.
562 pub enum Nonterminal {
563 NtItem(P<ast::Item>),
564 NtBlock(P<ast::Block>),
567 NtExpr(P<ast::Expr>),
569 NtIdent(ast::Ident, /* is_raw */ bool),
570 NtLifetime(ast::Ident),
571 /// Stuff inside brackets for attributes
572 NtMeta(ast::MetaItem),
574 NtVis(ast::Visibility),
576 // These are not exposed to macros, but are used by quasiquote.
578 NtImplItem(ast::ImplItem),
579 NtTraitItem(ast::TraitItem),
580 NtForeignItem(ast::ForeignItem),
581 NtGenerics(ast::Generics),
582 NtWhereClause(ast::WhereClause),
586 impl fmt::Debug for Nonterminal {
587 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
589 NtItem(..) => f.pad("NtItem(..)"),
590 NtBlock(..) => f.pad("NtBlock(..)"),
591 NtStmt(..) => f.pad("NtStmt(..)"),
592 NtPat(..) => f.pad("NtPat(..)"),
593 NtExpr(..) => f.pad("NtExpr(..)"),
594 NtTy(..) => f.pad("NtTy(..)"),
595 NtIdent(..) => f.pad("NtIdent(..)"),
596 NtMeta(..) => f.pad("NtMeta(..)"),
597 NtPath(..) => f.pad("NtPath(..)"),
598 NtTT(..) => f.pad("NtTT(..)"),
599 NtArm(..) => f.pad("NtArm(..)"),
600 NtImplItem(..) => f.pad("NtImplItem(..)"),
601 NtTraitItem(..) => f.pad("NtTraitItem(..)"),
602 NtForeignItem(..) => f.pad("NtForeignItem(..)"),
603 NtGenerics(..) => f.pad("NtGenerics(..)"),
604 NtWhereClause(..) => f.pad("NtWhereClause(..)"),
605 NtArg(..) => f.pad("NtArg(..)"),
606 NtVis(..) => f.pad("NtVis(..)"),
607 NtLifetime(..) => f.pad("NtLifetime(..)"),
612 pub fn is_op(tok: &Token) -> bool {
614 OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
615 Ident(..) | Lifetime(..) | Interpolated(..) |
616 Whitespace | Comment | Shebang(..) | Eof => false,
621 pub struct LazyTokenStream(Cell<Option<TokenStream>>);
623 impl Clone for LazyTokenStream {
624 fn clone(&self) -> Self {
625 let opt_stream = self.0.take();
626 self.0.set(opt_stream.clone());
627 LazyTokenStream(Cell::new(opt_stream))
631 impl cmp::Eq for LazyTokenStream {}
632 impl PartialEq for LazyTokenStream {
633 fn eq(&self, _other: &LazyTokenStream) -> bool {
638 impl fmt::Debug for LazyTokenStream {
639 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
640 fmt::Debug::fmt(&self.clone().0.into_inner(), f)
644 impl LazyTokenStream {
645 pub fn new() -> Self {
646 LazyTokenStream(Cell::new(None))
649 pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream {
650 let mut opt_stream = self.0.take();
651 if opt_stream.is_none() {
652 opt_stream = Some(f());
654 self.0.set(opt_stream.clone());
655 opt_stream.clone().unwrap()
659 impl Encodable for LazyTokenStream {
660 fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
665 impl Decodable for LazyTokenStream {
666 fn decode<D: Decoder>(_: &mut D) -> Result<LazyTokenStream, D::Error> {
667 Ok(LazyTokenStream::new())
671 impl ::std::hash::Hash for LazyTokenStream {
672 fn hash<H: ::std::hash::Hasher>(&self, _hasher: &mut H) {}
675 fn prepend_attrs(sess: &ParseSess,
676 attrs: &[ast::Attribute],
677 tokens: Option<&tokenstream::TokenStream>,
678 span: syntax_pos::Span)
679 -> Option<tokenstream::TokenStream>
681 let tokens = tokens?;
682 if attrs.len() == 0 {
683 return Some(tokens.clone())
685 let mut builder = tokenstream::TokenStreamBuilder::new();
687 assert_eq!(attr.style, ast::AttrStyle::Outer,
688 "inner attributes should prevent cached tokens from existing");
689 // FIXME: Avoid this pretty-print + reparse hack as bove
690 let name = FileName::MacroExpansion;
691 let source = pprust::attr_to_string(attr);
692 let stream = parse_stream_from_source_str(name, source, sess, Some(span));
693 builder.push(stream);
695 builder.push(tokens.clone());
696 Some(builder.build())