]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/token.rs
Replace Rc with Lrc for shared data
[rust.git] / src / libsyntax / parse / token.rs
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.
4 //
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.
10
11 pub use self::BinOpToken::*;
12 pub use self::Nonterminal::*;
13 pub use self::DelimToken::*;
14 pub use self::Lit::*;
15 pub use self::Token::*;
16
17 use ast::{self};
18 use parse::ParseSess;
19 use print::pprust;
20 use ptr::P;
21 use serialize::{Decodable, Decoder, Encodable, Encoder};
22 use symbol::keywords;
23 use syntax::parse::parse_stream_from_source_str;
24 use syntax_pos::{self, Span, FileName};
25 use tokenstream::{TokenStream, TokenTree};
26 use tokenstream;
27
28 use std::cell::Cell;
29 use std::{cmp, fmt};
30 use rustc_data_structures::sync::Lrc;
31
32 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
33 pub enum BinOpToken {
34     Plus,
35     Minus,
36     Star,
37     Slash,
38     Percent,
39     Caret,
40     And,
41     Or,
42     Shl,
43     Shr,
44 }
45
46 /// A delimiter token
47 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
48 pub enum DelimToken {
49     /// A round parenthesis: `(` or `)`
50     Paren,
51     /// A square bracket: `[` or `]`
52     Bracket,
53     /// A curly brace: `{` or `}`
54     Brace,
55     /// An empty delimiter
56     NoDelim,
57 }
58
59 impl DelimToken {
60     pub fn len(self) -> usize {
61         if self == NoDelim { 0 } else { 1 }
62     }
63
64     pub fn is_empty(self) -> bool {
65         self == NoDelim
66     }
67 }
68
69 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
70 pub enum Lit {
71     Byte(ast::Name),
72     Char(ast::Name),
73     Integer(ast::Name),
74     Float(ast::Name),
75     Str_(ast::Name),
76     StrRaw(ast::Name, usize), /* raw str delimited by n hash symbols */
77     ByteStr(ast::Name),
78     ByteStrRaw(ast::Name, usize), /* raw byte str delimited by n hash symbols */
79 }
80
81 impl Lit {
82     pub fn short_name(&self) -> &'static str {
83         match *self {
84             Byte(_) => "byte",
85             Char(_) => "char",
86             Integer(_) => "integer",
87             Float(_) => "float",
88             Str_(_) | StrRaw(..) => "string",
89             ByteStr(_) | ByteStrRaw(..) => "byte string"
90         }
91     }
92 }
93
94 fn ident_can_begin_expr(ident: ast::Ident) -> bool {
95     let ident_token: Token = Ident(ident);
96
97     !ident_token.is_reserved_ident() ||
98     ident_token.is_path_segment_keyword() ||
99     [
100         keywords::Do.name(),
101         keywords::Box.name(),
102         keywords::Break.name(),
103         keywords::Continue.name(),
104         keywords::False.name(),
105         keywords::For.name(),
106         keywords::If.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)
117 }
118
119 fn ident_can_begin_type(ident: ast::Ident) -> bool {
120     let ident_token: Token = Ident(ident);
121
122     !ident_token.is_reserved_ident() ||
123     ident_token.is_path_segment_keyword() ||
124     [
125         keywords::For.name(),
126         keywords::Impl.name(),
127         keywords::Fn.name(),
128         keywords::Unsafe.name(),
129         keywords::Extern.name(),
130         keywords::Typeof.name(),
131     ].contains(&ident.name)
132 }
133
134 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
135 pub enum Token {
136     /* Expression-operator symbols. */
137     Eq,
138     Lt,
139     Le,
140     EqEq,
141     Ne,
142     Ge,
143     Gt,
144     AndAnd,
145     OrOr,
146     Not,
147     Tilde,
148     BinOp(BinOpToken),
149     BinOpEq(BinOpToken),
150
151     /* Structural symbols */
152     At,
153     Dot,
154     DotDot,
155     DotDotDot,
156     DotDotEq,
157     DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
158     Comma,
159     Semi,
160     Colon,
161     ModSep,
162     RArrow,
163     LArrow,
164     FatArrow,
165     Pound,
166     Dollar,
167     Question,
168     /// An opening delimiter, eg. `{`
169     OpenDelim(DelimToken),
170     /// A closing delimiter, eg. `}`
171     CloseDelim(DelimToken),
172
173     /* Literals */
174     Literal(Lit, Option<ast::Name>),
175
176     /* Name components */
177     Ident(ast::Ident),
178     Underscore,
179     Lifetime(ast::Ident),
180
181     // The `LazyTokenStream` is a pure function of the `Nonterminal`,
182     // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc.
183     Interpolated(Lrc<(Nonterminal, LazyTokenStream)>),
184     // Can be expanded into several tokens.
185     /// Doc comment
186     DocComment(ast::Name),
187
188     // Junk. These carry no data because we don't really care about the data
189     // they *would* carry, and don't really want to allocate a new ident for
190     // them. Instead, users could extract that from the associated span.
191
192     /// Whitespace
193     Whitespace,
194     /// Comment
195     Comment,
196     Shebang(ast::Name),
197
198     Eof,
199 }
200
201 impl Token {
202     pub fn interpolated(nt: Nonterminal) -> Token {
203         Token::Interpolated(Lrc::new((nt, LazyTokenStream::new())))
204     }
205
206     /// Returns `true` if the token starts with '>'.
207     pub fn is_like_gt(&self) -> bool {
208         match *self {
209             BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true,
210             _ => false,
211         }
212     }
213
214     /// Returns `true` if the token can appear at the start of an expression.
215     pub fn can_begin_expr(&self) -> bool {
216         match *self {
217             Ident(ident)                => ident_can_begin_expr(ident), // value name or keyword
218             OpenDelim(..)                     | // tuple, array or block
219             Literal(..)                       | // literal
220             Not                               | // operator not
221             BinOp(Minus)                      | // unary minus
222             BinOp(Star)                       | // dereference
223             BinOp(Or) | OrOr                  | // closure
224             BinOp(And)                        | // reference
225             AndAnd                            | // double reference
226             // DotDotDot is no longer supported, but we need some way to display the error
227             DotDot | DotDotDot | DotDotEq     | // range notation
228             Lt | BinOp(Shl)                   | // associated path
229             ModSep                            | // global path
230             Pound                             => true, // expression attributes
231             Interpolated(ref nt) => match nt.0 {
232                 NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
233                 _ => false,
234             },
235             _ => false,
236         }
237     }
238
239     /// Returns `true` if the token can appear at the start of a type.
240     pub fn can_begin_type(&self) -> bool {
241         match *self {
242             Ident(ident)                => ident_can_begin_type(ident), // type name or keyword
243             OpenDelim(Paren)            | // tuple
244             OpenDelim(Bracket)          | // array
245             Underscore                  | // placeholder
246             Not                         | // never
247             BinOp(Star)                 | // raw pointer
248             BinOp(And)                  | // reference
249             AndAnd                      | // double reference
250             Question                    | // maybe bound in trait object
251             Lifetime(..)                | // lifetime bound in trait object
252             Lt | BinOp(Shl)             | // associated path
253             ModSep                      => true, // global path
254             Interpolated(ref nt) => match nt.0 {
255                 NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true,
256                 _ => false,
257             },
258             _ => false,
259         }
260     }
261
262     /// Returns `true` if the token can appear at the start of a generic bound.
263     pub fn can_begin_bound(&self) -> bool {
264         self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
265         self == &Question || self == &OpenDelim(Paren)
266     }
267
268     /// Returns `true` if the token is any literal
269     pub fn is_lit(&self) -> bool {
270         match *self {
271             Literal(..) => true,
272             _           => false,
273         }
274     }
275
276     pub fn ident(&self) -> Option<ast::Ident> {
277         match *self {
278             Ident(ident) => Some(ident),
279             Interpolated(ref nt) => match nt.0 {
280                 NtIdent(ident) => Some(ident.node),
281                 _ => None,
282             },
283             _ => None,
284         }
285     }
286
287     /// Returns `true` if the token is an identifier.
288     pub fn is_ident(&self) -> bool {
289         self.ident().is_some()
290     }
291
292     /// Returns `true` if the token is a documentation comment.
293     pub fn is_doc_comment(&self) -> bool {
294         match *self {
295             DocComment(..)   => true,
296             _                => false,
297         }
298     }
299
300     /// Returns `true` if the token is interpolated.
301     pub fn is_interpolated(&self) -> bool {
302         match *self {
303             Interpolated(..) => true,
304             _                => false,
305         }
306     }
307
308     /// Returns `true` if the token is an interpolated path.
309     pub fn is_path(&self) -> bool {
310         if let Interpolated(ref nt) = *self {
311             if let NtPath(..) = nt.0 {
312                 return true;
313             }
314         }
315         false
316     }
317
318     /// Returns a lifetime with the span and a dummy id if it is a lifetime,
319     /// or the original lifetime if it is an interpolated lifetime, ignoring
320     /// the span.
321     pub fn lifetime(&self, span: Span) -> Option<ast::Lifetime> {
322         match *self {
323             Lifetime(ident) =>
324                 Some(ast::Lifetime { ident: ident, span: span, id: ast::DUMMY_NODE_ID }),
325             Interpolated(ref nt) => match nt.0 {
326                 NtLifetime(lifetime) => Some(lifetime),
327                 _ => None,
328             },
329             _ => None,
330         }
331     }
332
333     /// Returns `true` if the token is a lifetime.
334     pub fn is_lifetime(&self) -> bool {
335         self.lifetime(syntax_pos::DUMMY_SP).is_some()
336     }
337
338     /// Returns `true` if the token is either the `mut` or `const` keyword.
339     pub fn is_mutability(&self) -> bool {
340         self.is_keyword(keywords::Mut) ||
341         self.is_keyword(keywords::Const)
342     }
343
344     pub fn is_qpath_start(&self) -> bool {
345         self == &Lt || self == &BinOp(Shl)
346     }
347
348     pub fn is_path_start(&self) -> bool {
349         self == &ModSep || self.is_qpath_start() || self.is_path() ||
350         self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
351     }
352
353     /// Returns `true` if the token is a given keyword, `kw`.
354     pub fn is_keyword(&self, kw: keywords::Keyword) -> bool {
355         self.ident().map(|ident| ident.name == kw.name()).unwrap_or(false)
356     }
357
358     pub fn is_path_segment_keyword(&self) -> bool {
359         match self.ident() {
360             Some(id) => id.name == keywords::Super.name() ||
361                         id.name == keywords::SelfValue.name() ||
362                         id.name == keywords::SelfType.name() ||
363                         id.name == keywords::Extern.name() ||
364                         id.name == keywords::Crate.name() ||
365                         id.name == keywords::DollarCrate.name(),
366             None => false,
367         }
368     }
369
370     // Returns true for reserved identifiers used internally for elided lifetimes,
371     // unnamed method parameters, crate root module, error recovery etc.
372     pub fn is_special_ident(&self) -> bool {
373         match self.ident() {
374             Some(id) => id.name <= keywords::DollarCrate.name(),
375             _ => false,
376         }
377     }
378
379     /// Returns `true` if the token is a keyword used in the language.
380     pub fn is_used_keyword(&self) -> bool {
381         match self.ident() {
382             Some(id) => id.name >= keywords::As.name() && id.name <= keywords::While.name(),
383             _ => false,
384         }
385     }
386
387     /// Returns `true` if the token is a keyword reserved for possible future use.
388     pub fn is_unused_keyword(&self) -> bool {
389         match self.ident() {
390             Some(id) => id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name(),
391             _ => false,
392         }
393     }
394
395     pub fn glue(self, joint: Token) -> Option<Token> {
396         Some(match self {
397             Eq => match joint {
398                 Eq => EqEq,
399                 Gt => FatArrow,
400                 _ => return None,
401             },
402             Lt => match joint {
403                 Eq => Le,
404                 Lt => BinOp(Shl),
405                 Le => BinOpEq(Shl),
406                 BinOp(Minus) => LArrow,
407                 _ => return None,
408             },
409             Gt => match joint {
410                 Eq => Ge,
411                 Gt => BinOp(Shr),
412                 Ge => BinOpEq(Shr),
413                 _ => return None,
414             },
415             Not => match joint {
416                 Eq => Ne,
417                 _ => return None,
418             },
419             BinOp(op) => match joint {
420                 Eq => BinOpEq(op),
421                 BinOp(And) if op == And => AndAnd,
422                 BinOp(Or) if op == Or => OrOr,
423                 Gt if op == Minus => RArrow,
424                 _ => return None,
425             },
426             Dot => match joint {
427                 Dot => DotDot,
428                 DotDot => DotDotDot,
429                 DotEq => DotDotEq,
430                 _ => return None,
431             },
432             DotDot => match joint {
433                 Dot => DotDotDot,
434                 Eq => DotDotEq,
435                 _ => return None,
436             },
437             Colon => match joint {
438                 Colon => ModSep,
439                 _ => return None,
440             },
441
442             Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
443             DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
444             Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None,
445
446             Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
447             Whitespace | Comment | Shebang(..) | Eof => return None,
448         })
449     }
450
451     /// Returns tokens that are likely to be typed accidentally instead of the current token.
452     /// Enables better error recovery when the wrong token is found.
453     pub fn similar_tokens(&self) -> Option<Vec<Token>> {
454         match *self {
455             Comma => Some(vec![Dot, Lt]),
456             Semi => Some(vec![Colon]),
457             _ => None
458         }
459     }
460
461     /// Returns `true` if the token is either a special identifier or a keyword.
462     pub fn is_reserved_ident(&self) -> bool {
463         self.is_special_ident() || self.is_used_keyword() || self.is_unused_keyword()
464     }
465
466     pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
467         -> TokenStream
468     {
469         let nt = match *self {
470             Token::Interpolated(ref nt) => nt,
471             _ => panic!("only works on interpolated tokens"),
472         };
473
474         // An `Interpolated` token means that we have a `Nonterminal`
475         // which is often a parsed AST item. At this point we now need
476         // to convert the parsed AST to an actual token stream, e.g.
477         // un-parse it basically.
478         //
479         // Unfortunately there's not really a great way to do that in a
480         // guaranteed lossless fashion right now. The fallback here is
481         // to just stringify the AST node and reparse it, but this loses
482         // all span information.
483         //
484         // As a result, some AST nodes are annotated with the token
485         // stream they came from. Attempt to extract these lossless
486         // token streams before we fall back to the stringification.
487         let mut tokens = None;
488
489         match nt.0 {
490             Nonterminal::NtItem(ref item) => {
491                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
492             }
493             Nonterminal::NtTraitItem(ref item) => {
494                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
495             }
496             Nonterminal::NtImplItem(ref item) => {
497                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
498             }
499             Nonterminal::NtIdent(ident) => {
500                 let token = Token::Ident(ident.node);
501                 tokens = Some(TokenTree::Token(ident.span, token).into());
502             }
503             Nonterminal::NtLifetime(lifetime) => {
504                 let token = Token::Lifetime(lifetime.ident);
505                 tokens = Some(TokenTree::Token(lifetime.span, token).into());
506             }
507             Nonterminal::NtTT(ref tt) => {
508                 tokens = Some(tt.clone().into());
509             }
510             _ => {}
511         }
512
513         tokens.unwrap_or_else(|| {
514             nt.1.force(|| {
515                 // FIXME(jseyfried): Avoid this pretty-print + reparse hack
516                 let source = pprust::token_to_string(self);
517                 parse_stream_from_source_str(FileName::MacroExpansion, source, sess, Some(span))
518             })
519         })
520     }
521 }
522
523 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
524 /// For interpolation during macro expansion.
525 pub enum Nonterminal {
526     NtItem(P<ast::Item>),
527     NtBlock(P<ast::Block>),
528     NtStmt(ast::Stmt),
529     NtPat(P<ast::Pat>),
530     NtExpr(P<ast::Expr>),
531     NtTy(P<ast::Ty>),
532     NtIdent(ast::SpannedIdent),
533     /// Stuff inside brackets for attributes
534     NtMeta(ast::MetaItem),
535     NtPath(ast::Path),
536     NtVis(ast::Visibility),
537     NtTT(TokenTree),
538     // These are not exposed to macros, but are used by quasiquote.
539     NtArm(ast::Arm),
540     NtImplItem(ast::ImplItem),
541     NtTraitItem(ast::TraitItem),
542     NtGenerics(ast::Generics),
543     NtWhereClause(ast::WhereClause),
544     NtArg(ast::Arg),
545     NtLifetime(ast::Lifetime),
546 }
547
548 impl fmt::Debug for Nonterminal {
549     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550         match *self {
551             NtItem(..) => f.pad("NtItem(..)"),
552             NtBlock(..) => f.pad("NtBlock(..)"),
553             NtStmt(..) => f.pad("NtStmt(..)"),
554             NtPat(..) => f.pad("NtPat(..)"),
555             NtExpr(..) => f.pad("NtExpr(..)"),
556             NtTy(..) => f.pad("NtTy(..)"),
557             NtIdent(..) => f.pad("NtIdent(..)"),
558             NtMeta(..) => f.pad("NtMeta(..)"),
559             NtPath(..) => f.pad("NtPath(..)"),
560             NtTT(..) => f.pad("NtTT(..)"),
561             NtArm(..) => f.pad("NtArm(..)"),
562             NtImplItem(..) => f.pad("NtImplItem(..)"),
563             NtTraitItem(..) => f.pad("NtTraitItem(..)"),
564             NtGenerics(..) => f.pad("NtGenerics(..)"),
565             NtWhereClause(..) => f.pad("NtWhereClause(..)"),
566             NtArg(..) => f.pad("NtArg(..)"),
567             NtVis(..) => f.pad("NtVis(..)"),
568             NtLifetime(..) => f.pad("NtLifetime(..)"),
569         }
570     }
571 }
572
573 pub fn is_op(tok: &Token) -> bool {
574     match *tok {
575         OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
576         Ident(..) | Underscore | Lifetime(..) | Interpolated(..) |
577         Whitespace | Comment | Shebang(..) | Eof => false,
578         _ => true,
579     }
580 }
581
582 pub struct LazyTokenStream(Cell<Option<TokenStream>>);
583
584 impl Clone for LazyTokenStream {
585     fn clone(&self) -> Self {
586         let opt_stream = self.0.take();
587         self.0.set(opt_stream.clone());
588         LazyTokenStream(Cell::new(opt_stream))
589     }
590 }
591
592 impl cmp::Eq for LazyTokenStream {}
593 impl PartialEq for LazyTokenStream {
594     fn eq(&self, _other: &LazyTokenStream) -> bool {
595         true
596     }
597 }
598
599 impl fmt::Debug for LazyTokenStream {
600     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
601         fmt::Debug::fmt(&self.clone().0.into_inner(), f)
602     }
603 }
604
605 impl LazyTokenStream {
606     pub fn new() -> Self {
607         LazyTokenStream(Cell::new(None))
608     }
609
610     pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream {
611         let mut opt_stream = self.0.take();
612         if opt_stream.is_none() {
613             opt_stream = Some(f());
614         }
615         self.0.set(opt_stream.clone());
616         opt_stream.clone().unwrap()
617     }
618 }
619
620 impl Encodable for LazyTokenStream {
621     fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
622         Ok(())
623     }
624 }
625
626 impl Decodable for LazyTokenStream {
627     fn decode<D: Decoder>(_: &mut D) -> Result<LazyTokenStream, D::Error> {
628         Ok(LazyTokenStream::new())
629     }
630 }
631
632 impl ::std::hash::Hash for LazyTokenStream {
633     fn hash<H: ::std::hash::Hasher>(&self, _hasher: &mut H) {}
634 }
635
636 fn prepend_attrs(sess: &ParseSess,
637                  attrs: &[ast::Attribute],
638                  tokens: Option<&tokenstream::TokenStream>,
639                  span: syntax_pos::Span)
640     -> Option<tokenstream::TokenStream>
641 {
642     let tokens = tokens?;
643     if attrs.len() == 0 {
644         return Some(tokens.clone())
645     }
646     let mut builder = tokenstream::TokenStreamBuilder::new();
647     for attr in attrs {
648         assert_eq!(attr.style, ast::AttrStyle::Outer,
649                    "inner attributes should prevent cached tokens from existing");
650         // FIXME: Avoid this pretty-print + reparse hack as bove
651         let name = FileName::MacroExpansion;
652         let source = pprust::attr_to_string(attr);
653         let stream = parse_stream_from_source_str(name, source, sess, Some(span));
654         builder.push(stream);
655     }
656     builder.push(tokens.clone());
657     Some(builder.build())
658 }