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 // Returns true for reserved identifiers used internally for elided lifetimes,
146 // unnamed method parameters, crate root module, error recovery etc.
147 pub fn is_special_ident(id: ast::Ident) -> bool {
148 id.name <= keywords::Underscore.name()
151 /// Returns `true` if the token is a keyword used in the language.
152 pub fn is_used_keyword(id: ast::Ident) -> bool {
153 id.name >= keywords::As.name() && id.name <= keywords::While.name()
156 /// Returns `true` if the token is a keyword reserved for possible future use.
157 pub fn is_unused_keyword(id: ast::Ident) -> bool {
158 id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name()
161 /// Returns `true` if the token is either a special identifier or a keyword.
162 pub fn is_reserved_ident(id: ast::Ident) -> bool {
163 is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id)
166 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
168 /* Expression-operator symbols. */
183 /* Structural symbols */
189 DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
200 /// An opening delimiter, eg. `{`
201 OpenDelim(DelimToken),
202 /// A closing delimiter, eg. `}`
203 CloseDelim(DelimToken),
206 Literal(Lit, Option<ast::Name>),
208 /* Name components */
209 Ident(ast::Ident, /* is_raw */ bool),
210 Lifetime(ast::Ident),
212 // The `LazyTokenStream` is a pure function of the `Nonterminal`,
213 // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc.
214 Interpolated(Lrc<(Nonterminal, LazyTokenStream)>),
215 // Can be expanded into several tokens.
217 DocComment(ast::Name),
219 // Junk. These carry no data because we don't really care about the data
220 // they *would* carry, and don't really want to allocate a new ident for
221 // them. Instead, users could extract that from the associated span.
233 pub fn interpolated(nt: Nonterminal) -> Token {
234 Token::Interpolated(Lrc::new((nt, LazyTokenStream::new())))
237 /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
238 pub fn from_ast_ident(ident: ast::Ident) -> Token {
239 Ident(ident, is_reserved_ident(ident))
242 /// Returns `true` if the token starts with '>'.
243 pub fn is_like_gt(&self) -> bool {
245 BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true,
250 /// Returns `true` if the token can appear at the start of an expression.
251 pub fn can_begin_expr(&self) -> bool {
253 Ident(ident, is_raw) =>
254 ident_can_begin_expr(ident, is_raw), // value name or keyword
255 OpenDelim(..) | // tuple, array or block
256 Literal(..) | // literal
257 Not | // operator not
258 BinOp(Minus) | // unary minus
259 BinOp(Star) | // dereference
260 BinOp(Or) | OrOr | // closure
261 BinOp(And) | // reference
262 AndAnd | // double reference
263 // DotDotDot is no longer supported, but we need some way to display the error
264 DotDot | DotDotDot | DotDotEq | // range notation
265 Lt | BinOp(Shl) | // associated path
266 ModSep | // global path
267 Pound => true, // expression attributes
268 Interpolated(ref nt) => match nt.0 {
269 NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
276 /// Returns `true` if the token can appear at the start of a type.
277 pub fn can_begin_type(&self) -> bool {
279 Ident(ident, is_raw) =>
280 ident_can_begin_type(ident, is_raw), // type name or keyword
281 OpenDelim(Paren) | // tuple
282 OpenDelim(Bracket) | // array
284 BinOp(Star) | // raw pointer
285 BinOp(And) | // reference
286 AndAnd | // double reference
287 Question | // maybe bound in trait object
288 Lifetime(..) | // lifetime bound in trait object
289 Lt | BinOp(Shl) | // associated path
290 ModSep => true, // global path
291 Interpolated(ref nt) => match nt.0 {
292 NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true,
299 /// Returns `true` if the token can appear at the start of a generic bound.
300 pub fn can_begin_bound(&self) -> bool {
301 self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
302 self == &Question || self == &OpenDelim(Paren)
305 /// Returns `true` if the token is any literal
306 pub fn is_lit(&self) -> bool {
313 pub fn ident(&self) -> Option<(ast::Ident, bool)> {
315 Ident(ident, is_raw) => Some((ident, is_raw)),
316 Interpolated(ref nt) => match nt.0 {
317 NtIdent(ident, is_raw) => Some((ident.node, is_raw)),
324 /// Returns `true` if the token is an identifier.
325 pub fn is_ident(&self) -> bool {
326 self.ident().is_some()
329 /// Returns `true` if the token is a documentation comment.
330 pub fn is_doc_comment(&self) -> bool {
332 DocComment(..) => true,
337 /// Returns `true` if the token is interpolated.
338 pub fn is_interpolated(&self) -> bool {
340 Interpolated(..) => true,
345 /// Returns `true` if the token is an interpolated path.
346 pub fn is_path(&self) -> bool {
347 if let Interpolated(ref nt) = *self {
348 if let NtPath(..) = nt.0 {
355 /// Returns a lifetime with the span and a dummy id if it is a lifetime,
356 /// or the original lifetime if it is an interpolated lifetime, ignoring
358 pub fn lifetime(&self, span: Span) -> Option<ast::Lifetime> {
361 Some(ast::Lifetime { ident: ident, span: span, id: ast::DUMMY_NODE_ID }),
362 Interpolated(ref nt) => match nt.0 {
363 NtLifetime(lifetime) => Some(lifetime),
370 /// Returns `true` if the token is a lifetime.
371 pub fn is_lifetime(&self) -> bool {
372 self.lifetime(syntax_pos::DUMMY_SP).is_some()
375 /// Returns `true` if the token is either the `mut` or `const` keyword.
376 pub fn is_mutability(&self) -> bool {
377 self.is_keyword(keywords::Mut) ||
378 self.is_keyword(keywords::Const)
381 pub fn is_qpath_start(&self) -> bool {
382 self == &Lt || self == &BinOp(Shl)
385 pub fn is_path_start(&self) -> bool {
386 self == &ModSep || self.is_qpath_start() || self.is_path() ||
387 self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
390 /// Returns `true` if the token is a given keyword, `kw`.
391 pub fn is_keyword(&self, kw: keywords::Keyword) -> bool {
392 self.ident().map(|(ident, is_raw)| ident.name == kw.name() && !is_raw).unwrap_or(false)
395 pub fn is_path_segment_keyword(&self) -> bool {
397 Some((id, false)) => is_path_segment_keyword(id),
402 // Returns true for reserved identifiers used internally for elided lifetimes,
403 // unnamed method parameters, crate root module, error recovery etc.
404 pub fn is_special_ident(&self) -> bool {
406 Some((id, false)) => is_special_ident(id),
411 /// Returns `true` if the token is a keyword used in the language.
412 pub fn is_used_keyword(&self) -> bool {
414 Some((id, false)) => is_used_keyword(id),
419 /// Returns `true` if the token is a keyword reserved for possible future use.
420 pub fn is_unused_keyword(&self) -> bool {
422 Some((id, false)) => is_unused_keyword(id),
427 pub fn glue(self, joint: Token) -> Option<Token> {
438 BinOp(Minus) => LArrow,
451 BinOp(op) => match joint {
453 BinOp(And) if op == And => AndAnd,
454 BinOp(Or) if op == Or => OrOr,
455 Gt if op == Minus => RArrow,
464 DotDot => match joint {
469 Colon => match joint {
474 Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
475 DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
476 Question | OpenDelim(..) | CloseDelim(..) => return None,
478 Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
479 Whitespace | Comment | Shebang(..) | Eof => return None,
483 /// Returns tokens that are likely to be typed accidentally instead of the current token.
484 /// Enables better error recovery when the wrong token is found.
485 pub fn similar_tokens(&self) -> Option<Vec<Token>> {
487 Comma => Some(vec![Dot, Lt]),
488 Semi => Some(vec![Colon]),
493 /// Returns `true` if the token is either a special identifier or a keyword.
494 pub fn is_reserved_ident(&self) -> bool {
496 Some((id, false)) => is_reserved_ident(id),
501 pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
504 let nt = match *self {
505 Token::Interpolated(ref nt) => nt,
506 _ => panic!("only works on interpolated tokens"),
509 // An `Interpolated` token means that we have a `Nonterminal`
510 // which is often a parsed AST item. At this point we now need
511 // to convert the parsed AST to an actual token stream, e.g.
512 // un-parse it basically.
514 // Unfortunately there's not really a great way to do that in a
515 // guaranteed lossless fashion right now. The fallback here is
516 // to just stringify the AST node and reparse it, but this loses
517 // all span information.
519 // As a result, some AST nodes are annotated with the token
520 // stream they came from. Attempt to extract these lossless
521 // token streams before we fall back to the stringification.
522 let mut tokens = None;
525 Nonterminal::NtItem(ref item) => {
526 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
528 Nonterminal::NtTraitItem(ref item) => {
529 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
531 Nonterminal::NtImplItem(ref item) => {
532 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
534 Nonterminal::NtIdent(ident, is_raw) => {
535 let token = Token::Ident(ident.node, is_raw);
536 tokens = Some(TokenTree::Token(ident.span, token).into());
538 Nonterminal::NtLifetime(lifetime) => {
539 let token = Token::Lifetime(lifetime.ident);
540 tokens = Some(TokenTree::Token(lifetime.span, token).into());
542 Nonterminal::NtTT(ref tt) => {
543 tokens = Some(tt.clone().into());
548 tokens.unwrap_or_else(|| {
550 // FIXME(jseyfried): Avoid this pretty-print + reparse hack
551 let source = pprust::token_to_string(self);
552 parse_stream_from_source_str(FileName::MacroExpansion, source, sess, Some(span))
558 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
559 /// For interpolation during macro expansion.
560 pub enum Nonterminal {
561 NtItem(P<ast::Item>),
562 NtBlock(P<ast::Block>),
565 NtExpr(P<ast::Expr>),
567 NtIdent(ast::SpannedIdent, /* is_raw */ bool),
568 /// Stuff inside brackets for attributes
569 NtMeta(ast::MetaItem),
571 NtVis(ast::Visibility),
573 // These are not exposed to macros, but are used by quasiquote.
575 NtImplItem(ast::ImplItem),
576 NtTraitItem(ast::TraitItem),
577 NtGenerics(ast::Generics),
578 NtWhereClause(ast::WhereClause),
580 NtLifetime(ast::Lifetime),
583 impl fmt::Debug for Nonterminal {
584 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586 NtItem(..) => f.pad("NtItem(..)"),
587 NtBlock(..) => f.pad("NtBlock(..)"),
588 NtStmt(..) => f.pad("NtStmt(..)"),
589 NtPat(..) => f.pad("NtPat(..)"),
590 NtExpr(..) => f.pad("NtExpr(..)"),
591 NtTy(..) => f.pad("NtTy(..)"),
592 NtIdent(..) => f.pad("NtIdent(..)"),
593 NtMeta(..) => f.pad("NtMeta(..)"),
594 NtPath(..) => f.pad("NtPath(..)"),
595 NtTT(..) => f.pad("NtTT(..)"),
596 NtArm(..) => f.pad("NtArm(..)"),
597 NtImplItem(..) => f.pad("NtImplItem(..)"),
598 NtTraitItem(..) => f.pad("NtTraitItem(..)"),
599 NtGenerics(..) => f.pad("NtGenerics(..)"),
600 NtWhereClause(..) => f.pad("NtWhereClause(..)"),
601 NtArg(..) => f.pad("NtArg(..)"),
602 NtVis(..) => f.pad("NtVis(..)"),
603 NtLifetime(..) => f.pad("NtLifetime(..)"),
608 pub fn is_op(tok: &Token) -> bool {
610 OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
611 Ident(..) | Lifetime(..) | Interpolated(..) |
612 Whitespace | Comment | Shebang(..) | Eof => false,
617 pub struct LazyTokenStream(Cell<Option<TokenStream>>);
619 impl Clone for LazyTokenStream {
620 fn clone(&self) -> Self {
621 let opt_stream = self.0.take();
622 self.0.set(opt_stream.clone());
623 LazyTokenStream(Cell::new(opt_stream))
627 impl cmp::Eq for LazyTokenStream {}
628 impl PartialEq for LazyTokenStream {
629 fn eq(&self, _other: &LazyTokenStream) -> bool {
634 impl fmt::Debug for LazyTokenStream {
635 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
636 fmt::Debug::fmt(&self.clone().0.into_inner(), f)
640 impl LazyTokenStream {
641 pub fn new() -> Self {
642 LazyTokenStream(Cell::new(None))
645 pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream {
646 let mut opt_stream = self.0.take();
647 if opt_stream.is_none() {
648 opt_stream = Some(f());
650 self.0.set(opt_stream.clone());
651 opt_stream.clone().unwrap()
655 impl Encodable for LazyTokenStream {
656 fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
661 impl Decodable for LazyTokenStream {
662 fn decode<D: Decoder>(_: &mut D) -> Result<LazyTokenStream, D::Error> {
663 Ok(LazyTokenStream::new())
667 impl ::std::hash::Hash for LazyTokenStream {
668 fn hash<H: ::std::hash::Hasher>(&self, _hasher: &mut H) {}
671 fn prepend_attrs(sess: &ParseSess,
672 attrs: &[ast::Attribute],
673 tokens: Option<&tokenstream::TokenStream>,
674 span: syntax_pos::Span)
675 -> Option<tokenstream::TokenStream>
677 let tokens = tokens?;
678 if attrs.len() == 0 {
679 return Some(tokens.clone())
681 let mut builder = tokenstream::TokenStreamBuilder::new();
683 assert_eq!(attr.style, ast::AttrStyle::Outer,
684 "inner attributes should prevent cached tokens from existing");
685 // FIXME: Avoid this pretty-print + reparse hack as bove
686 let name = FileName::MacroExpansion;
687 let source = pprust::attr_to_string(attr);
688 let stream = parse_stream_from_source_str(name, source, sess, Some(span));
689 builder.push(stream);
691 builder.push(tokens.clone());
692 Some(builder.build())