]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/lexer/mod.rs
Update E0253.rs
[rust.git] / src / libsyntax / parse / lexer / mod.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 use ast;
12 use syntax_pos::{self, BytePos, CharPos, Pos, Span};
13 use codemap::CodeMap;
14 use errors::{FatalError, Handler, DiagnosticBuilder};
15 use ext::tt::transcribe::tt_next_token;
16 use parse::token::{self, keywords, str_to_ident};
17 use str::char_at;
18 use rustc_unicode::property::Pattern_White_Space;
19
20 use std::borrow::Cow;
21 use std::char;
22 use std::mem::replace;
23 use std::rc::Rc;
24
25 pub use ext::tt::transcribe::{TtReader, new_tt_reader, new_tt_reader_with_doc_flag};
26
27 pub mod comments;
28 mod unicode_chars;
29
30 pub trait Reader {
31     fn is_eof(&self) -> bool;
32     fn try_next_token(&mut self) -> Result<TokenAndSpan, ()>;
33     fn next_token(&mut self) -> TokenAndSpan where Self: Sized {
34         let res = self.try_next_token();
35         self.unwrap_or_abort(res)
36     }
37     /// Report a fatal error with the current span.
38     fn fatal(&self, &str) -> FatalError;
39     /// Report a non-fatal error with the current span.
40     fn err(&self, &str);
41     fn emit_fatal_errors(&mut self);
42     fn unwrap_or_abort(&mut self, res: Result<TokenAndSpan, ()>) -> TokenAndSpan {
43         match res {
44             Ok(tok) => tok,
45             Err(_) => {
46                 self.emit_fatal_errors();
47                 panic!(FatalError);
48             }
49         }
50     }
51     fn peek(&self) -> TokenAndSpan;
52     /// Get a token the parser cares about.
53     fn try_real_token(&mut self) -> Result<TokenAndSpan, ()> {
54         let mut t = self.try_next_token()?;
55         loop {
56             match t.tok {
57                 token::Whitespace | token::Comment | token::Shebang(_) => {
58                     t = self.try_next_token()?;
59                 }
60                 _ => break,
61             }
62         }
63         Ok(t)
64     }
65     fn real_token(&mut self) -> TokenAndSpan {
66         let res = self.try_real_token();
67         self.unwrap_or_abort(res)
68     }
69 }
70
71 #[derive(Clone, PartialEq, Eq, Debug)]
72 pub struct TokenAndSpan {
73     pub tok: token::Token,
74     pub sp: Span,
75 }
76
77 pub struct StringReader<'a> {
78     pub span_diagnostic: &'a Handler,
79     /// The absolute offset within the codemap of the next character to read
80     pub pos: BytePos,
81     /// The absolute offset within the codemap of the last character read(curr)
82     pub last_pos: BytePos,
83     /// The column of the next character to read
84     pub col: CharPos,
85     /// The last character to be read
86     pub curr: Option<char>,
87     pub filemap: Rc<syntax_pos::FileMap>,
88     // cached:
89     pub peek_tok: token::Token,
90     pub peek_span: Span,
91     pub fatal_errs: Vec<DiagnosticBuilder<'a>>,
92     // cache a direct reference to the source text, so that we don't have to
93     // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
94     source_text: Rc<String>,
95 }
96
97 impl<'a> Reader for StringReader<'a> {
98     fn is_eof(&self) -> bool {
99         self.curr.is_none()
100     }
101     /// Return the next token. EFFECT: advances the string_reader.
102     fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
103         assert!(self.fatal_errs.is_empty());
104         let ret_val = TokenAndSpan {
105             tok: replace(&mut self.peek_tok, token::Underscore),
106             sp: self.peek_span,
107         };
108         self.advance_token()?;
109         Ok(ret_val)
110     }
111     fn fatal(&self, m: &str) -> FatalError {
112         self.fatal_span(self.peek_span, m)
113     }
114     fn err(&self, m: &str) {
115         self.err_span(self.peek_span, m)
116     }
117     fn emit_fatal_errors(&mut self) {
118         for err in &mut self.fatal_errs {
119             err.emit();
120         }
121         self.fatal_errs.clear();
122     }
123     fn peek(&self) -> TokenAndSpan {
124         // FIXME(pcwalton): Bad copy!
125         TokenAndSpan {
126             tok: self.peek_tok.clone(),
127             sp: self.peek_span,
128         }
129     }
130 }
131
132 impl<'a> Reader for TtReader<'a> {
133     fn is_eof(&self) -> bool {
134         self.cur_tok == token::Eof
135     }
136     fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
137         assert!(self.fatal_errs.is_empty());
138         let r = tt_next_token(self);
139         debug!("TtReader: r={:?}", r);
140         Ok(r)
141     }
142     fn fatal(&self, m: &str) -> FatalError {
143         self.sp_diag.span_fatal(self.cur_span, m)
144     }
145     fn err(&self, m: &str) {
146         self.sp_diag.span_err(self.cur_span, m);
147     }
148     fn emit_fatal_errors(&mut self) {
149         for err in &mut self.fatal_errs {
150             err.emit();
151         }
152         self.fatal_errs.clear();
153     }
154     fn peek(&self) -> TokenAndSpan {
155         TokenAndSpan {
156             tok: self.cur_tok.clone(),
157             sp: self.cur_span,
158         }
159     }
160 }
161
162 impl<'a> StringReader<'a> {
163     /// For comments.rs, which hackily pokes into pos and curr
164     pub fn new_raw<'b>(span_diagnostic: &'b Handler,
165                        filemap: Rc<syntax_pos::FileMap>)
166                        -> StringReader<'b> {
167         if filemap.src.is_none() {
168             span_diagnostic.bug(&format!("Cannot lex filemap \
169                                           without source: {}",
170                                          filemap.name)[..]);
171         }
172
173         let source_text = (*filemap.src.as_ref().unwrap()).clone();
174
175         let mut sr = StringReader {
176             span_diagnostic: span_diagnostic,
177             pos: filemap.start_pos,
178             last_pos: filemap.start_pos,
179             col: CharPos(0),
180             curr: Some('\n'),
181             filemap: filemap,
182             // dummy values; not read
183             peek_tok: token::Eof,
184             peek_span: syntax_pos::DUMMY_SP,
185             source_text: source_text,
186             fatal_errs: Vec::new(),
187         };
188         sr.bump();
189         sr
190     }
191
192     pub fn new<'b>(span_diagnostic: &'b Handler,
193                    filemap: Rc<syntax_pos::FileMap>)
194                    -> StringReader<'b> {
195         let mut sr = StringReader::new_raw(span_diagnostic, filemap);
196         if let Err(_) = sr.advance_token() {
197             sr.emit_fatal_errors();
198             panic!(FatalError);
199         }
200         sr
201     }
202
203     pub fn curr_is(&self, c: char) -> bool {
204         self.curr == Some(c)
205     }
206
207     /// Report a fatal lexical error with a given span.
208     pub fn fatal_span(&self, sp: Span, m: &str) -> FatalError {
209         self.span_diagnostic.span_fatal(sp, m)
210     }
211
212     /// Report a lexical error with a given span.
213     pub fn err_span(&self, sp: Span, m: &str) {
214         self.span_diagnostic.span_err(sp, m)
215     }
216
217
218     /// Report a fatal error spanning [`from_pos`, `to_pos`).
219     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
220         self.fatal_span(syntax_pos::mk_sp(from_pos, to_pos), m)
221     }
222
223     /// Report a lexical error spanning [`from_pos`, `to_pos`).
224     fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
225         self.err_span(syntax_pos::mk_sp(from_pos, to_pos), m)
226     }
227
228     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
229     /// escaped character to the error message
230     fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
231         let mut m = m.to_string();
232         m.push_str(": ");
233         for c in c.escape_default() {
234             m.push(c)
235         }
236         self.fatal_span_(from_pos, to_pos, &m[..])
237     }
238     fn struct_fatal_span_char(&self,
239                               from_pos: BytePos,
240                               to_pos: BytePos,
241                               m: &str,
242                               c: char)
243                               -> DiagnosticBuilder<'a> {
244         let mut m = m.to_string();
245         m.push_str(": ");
246         for c in c.escape_default() {
247             m.push(c)
248         }
249         self.span_diagnostic.struct_span_fatal(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
250     }
251
252     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
253     /// escaped character to the error message
254     fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
255         let mut m = m.to_string();
256         m.push_str(": ");
257         for c in c.escape_default() {
258             m.push(c)
259         }
260         self.err_span_(from_pos, to_pos, &m[..]);
261     }
262     fn struct_err_span_char(&self,
263                             from_pos: BytePos,
264                             to_pos: BytePos,
265                             m: &str,
266                             c: char)
267                             -> DiagnosticBuilder<'a> {
268         let mut m = m.to_string();
269         m.push_str(": ");
270         for c in c.escape_default() {
271             m.push(c)
272         }
273         self.span_diagnostic.struct_span_err(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
274     }
275
276     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
277     /// offending string to the error message
278     fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError {
279         m.push_str(": ");
280         let from = self.byte_offset(from_pos).to_usize();
281         let to = self.byte_offset(to_pos).to_usize();
282         m.push_str(&self.source_text[from..to]);
283         self.fatal_span_(from_pos, to_pos, &m[..])
284     }
285
286     /// Advance peek_tok and peek_span to refer to the next token, and
287     /// possibly update the interner.
288     fn advance_token(&mut self) -> Result<(), ()> {
289         match self.scan_whitespace_or_comment() {
290             Some(comment) => {
291                 self.peek_span = comment.sp;
292                 self.peek_tok = comment.tok;
293             }
294             None => {
295                 if self.is_eof() {
296                     self.peek_tok = token::Eof;
297                     self.peek_span = syntax_pos::mk_sp(self.filemap.end_pos, self.filemap.end_pos);
298                 } else {
299                     let start_bytepos = self.last_pos;
300                     self.peek_tok = self.next_token_inner()?;
301                     self.peek_span = syntax_pos::mk_sp(start_bytepos, self.last_pos);
302                 };
303             }
304         }
305         Ok(())
306     }
307
308     fn byte_offset(&self, pos: BytePos) -> BytePos {
309         (pos - self.filemap.start_pos)
310     }
311
312     /// Calls `f` with a string slice of the source text spanning from `start`
313     /// up to but excluding `self.last_pos`, meaning the slice does not include
314     /// the character `self.curr`.
315     pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T
316         where F: FnOnce(&str) -> T
317     {
318         self.with_str_from_to(start, self.last_pos, f)
319     }
320
321     /// Create a Name from a given offset to the current offset, each
322     /// adjusted 1 towards each other (assumes that on either side there is a
323     /// single-byte delimiter).
324     pub fn name_from(&self, start: BytePos) -> ast::Name {
325         debug!("taking an ident from {:?} to {:?}", start, self.last_pos);
326         self.with_str_from(start, token::intern)
327     }
328
329     /// As name_from, with an explicit endpoint.
330     pub fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name {
331         debug!("taking an ident from {:?} to {:?}", start, end);
332         self.with_str_from_to(start, end, token::intern)
333     }
334
335     /// Calls `f` with a string slice of the source text spanning from `start`
336     /// up to but excluding `end`.
337     fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T
338         where F: FnOnce(&str) -> T
339     {
340         f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()])
341     }
342
343     /// Converts CRLF to LF in the given string, raising an error on bare CR.
344     fn translate_crlf<'b>(&self, start: BytePos, s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
345         let mut i = 0;
346         while i < s.len() {
347             let ch = char_at(s, i);
348             let next = i + ch.len_utf8();
349             if ch == '\r' {
350                 if next < s.len() && char_at(s, next) == '\n' {
351                     return translate_crlf_(self, start, s, errmsg, i).into();
352                 }
353                 let pos = start + BytePos(i as u32);
354                 let end_pos = start + BytePos(next as u32);
355                 self.err_span_(pos, end_pos, errmsg);
356             }
357             i = next;
358         }
359         return s.into();
360
361         fn translate_crlf_(rdr: &StringReader,
362                            start: BytePos,
363                            s: &str,
364                            errmsg: &str,
365                            mut i: usize)
366                            -> String {
367             let mut buf = String::with_capacity(s.len());
368             let mut j = 0;
369             while i < s.len() {
370                 let ch = char_at(s, i);
371                 let next = i + ch.len_utf8();
372                 if ch == '\r' {
373                     if j < i {
374                         buf.push_str(&s[j..i]);
375                     }
376                     j = next;
377                     if next >= s.len() || char_at(s, next) != '\n' {
378                         let pos = start + BytePos(i as u32);
379                         let end_pos = start + BytePos(next as u32);
380                         rdr.err_span_(pos, end_pos, errmsg);
381                     }
382                 }
383                 i = next;
384             }
385             if j < s.len() {
386                 buf.push_str(&s[j..]);
387             }
388             buf
389         }
390     }
391
392
393     /// Advance the StringReader by one character. If a newline is
394     /// discovered, add it to the FileMap's list of line start offsets.
395     pub fn bump(&mut self) {
396         self.last_pos = self.pos;
397         let current_byte_offset = self.byte_offset(self.pos).to_usize();
398         if current_byte_offset < self.source_text.len() {
399             assert!(self.curr.is_some());
400             let last_char = self.curr.unwrap();
401             let ch = char_at(&self.source_text, current_byte_offset);
402             let next = current_byte_offset + ch.len_utf8();
403             let byte_offset_diff = next - current_byte_offset;
404             self.pos = self.pos + Pos::from_usize(byte_offset_diff);
405             self.curr = Some(ch);
406             self.col = self.col + CharPos(1);
407             if last_char == '\n' {
408                 self.filemap.next_line(self.last_pos);
409                 self.col = CharPos(0);
410             }
411
412             if byte_offset_diff > 1 {
413                 self.filemap.record_multibyte_char(self.last_pos, byte_offset_diff);
414             }
415         } else {
416             self.curr = None;
417         }
418     }
419
420     pub fn nextch(&self) -> Option<char> {
421         let offset = self.byte_offset(self.pos).to_usize();
422         if offset < self.source_text.len() {
423             Some(char_at(&self.source_text, offset))
424         } else {
425             None
426         }
427     }
428
429     pub fn nextch_is(&self, c: char) -> bool {
430         self.nextch() == Some(c)
431     }
432
433     pub fn nextnextch(&self) -> Option<char> {
434         let offset = self.byte_offset(self.pos).to_usize();
435         let s = &self.source_text[..];
436         if offset >= s.len() {
437             return None;
438         }
439         let next = offset + char_at(s, offset).len_utf8();
440         if next < s.len() {
441             Some(char_at(s, next))
442         } else {
443             None
444         }
445     }
446
447     pub fn nextnextch_is(&self, c: char) -> bool {
448         self.nextnextch() == Some(c)
449     }
450
451     /// Eats <XID_start><XID_continue>*, if possible.
452     fn scan_optional_raw_name(&mut self) -> Option<ast::Name> {
453         if !ident_start(self.curr) {
454             return None;
455         }
456         let start = self.last_pos;
457         while ident_continue(self.curr) {
458             self.bump();
459         }
460
461         self.with_str_from(start, |string| {
462             if string == "_" {
463                 None
464             } else {
465                 Some(token::intern(string))
466             }
467         })
468     }
469
470     /// PRECONDITION: self.curr is not whitespace
471     /// Eats any kind of comment.
472     fn scan_comment(&mut self) -> Option<TokenAndSpan> {
473         if let Some(c) = self.curr {
474             if c.is_whitespace() {
475                 self.span_diagnostic.span_err(syntax_pos::mk_sp(self.last_pos, self.last_pos),
476                                               "called consume_any_line_comment, but there \
477                                                was whitespace");
478             }
479         }
480
481         if self.curr_is('/') {
482             match self.nextch() {
483                 Some('/') => {
484                     self.bump();
485                     self.bump();
486
487                     // line comments starting with "///" or "//!" are doc-comments
488                     let doc_comment = self.curr_is('/') || self.curr_is('!');
489                     let start_bpos = if doc_comment {
490                         self.pos - BytePos(3)
491                     } else {
492                         self.last_pos - BytePos(2)
493                     };
494
495                     while !self.is_eof() {
496                         match self.curr.unwrap() {
497                             '\n' => break,
498                             '\r' => {
499                                 if self.nextch_is('\n') {
500                                     // CRLF
501                                     break;
502                                 } else if doc_comment {
503                                     self.err_span_(self.last_pos,
504                                                    self.pos,
505                                                    "bare CR not allowed in doc-comment");
506                                 }
507                             }
508                             _ => (),
509                         }
510                         self.bump();
511                     }
512
513                     return if doc_comment {
514                         self.with_str_from(start_bpos, |string| {
515                             // comments with only more "/"s are not doc comments
516                             let tok = if is_doc_comment(string) {
517                                 token::DocComment(token::intern(string))
518                             } else {
519                                 token::Comment
520                             };
521
522                             Some(TokenAndSpan {
523                                 tok: tok,
524                                 sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
525                             })
526                         })
527                     } else {
528                         Some(TokenAndSpan {
529                             tok: token::Comment,
530                             sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
531                         })
532                     };
533                 }
534                 Some('*') => {
535                     self.bump();
536                     self.bump();
537                     self.scan_block_comment()
538                 }
539                 _ => None,
540             }
541         } else if self.curr_is('#') {
542             if self.nextch_is('!') {
543
544                 // Parse an inner attribute.
545                 if self.nextnextch_is('[') {
546                     return None;
547                 }
548
549                 // I guess this is the only way to figure out if
550                 // we're at the beginning of the file...
551                 let cmap = CodeMap::new();
552                 cmap.files.borrow_mut().push(self.filemap.clone());
553                 let loc = cmap.lookup_char_pos_adj(self.last_pos);
554                 debug!("Skipping a shebang");
555                 if loc.line == 1 && loc.col == CharPos(0) {
556                     // FIXME: Add shebang "token", return it
557                     let start = self.last_pos;
558                     while !self.curr_is('\n') && !self.is_eof() {
559                         self.bump();
560                     }
561                     return Some(TokenAndSpan {
562                         tok: token::Shebang(self.name_from(start)),
563                         sp: syntax_pos::mk_sp(start, self.last_pos),
564                     });
565                 }
566             }
567             None
568         } else {
569             None
570         }
571     }
572
573     /// If there is whitespace, shebang, or a comment, scan it. Otherwise,
574     /// return None.
575     fn scan_whitespace_or_comment(&mut self) -> Option<TokenAndSpan> {
576         match self.curr.unwrap_or('\0') {
577             // # to handle shebang at start of file -- this is the entry point
578             // for skipping over all "junk"
579             '/' | '#' => {
580                 let c = self.scan_comment();
581                 debug!("scanning a comment {:?}", c);
582                 c
583             },
584             c if is_pattern_whitespace(Some(c)) => {
585                 let start_bpos = self.last_pos;
586                 while is_pattern_whitespace(self.curr) {
587                     self.bump();
588                 }
589                 let c = Some(TokenAndSpan {
590                     tok: token::Whitespace,
591                     sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
592                 });
593                 debug!("scanning whitespace: {:?}", c);
594                 c
595             }
596             _ => None,
597         }
598     }
599
600     /// Might return a sugared-doc-attr
601     fn scan_block_comment(&mut self) -> Option<TokenAndSpan> {
602         // block comments starting with "/**" or "/*!" are doc-comments
603         let is_doc_comment = self.curr_is('*') || self.curr_is('!');
604         let start_bpos = self.last_pos - BytePos(2);
605
606         let mut level: isize = 1;
607         let mut has_cr = false;
608         while level > 0 {
609             if self.is_eof() {
610                 let msg = if is_doc_comment {
611                     "unterminated block doc-comment"
612                 } else {
613                     "unterminated block comment"
614                 };
615                 let last_bpos = self.last_pos;
616                 panic!(self.fatal_span_(start_bpos, last_bpos, msg));
617             }
618             let n = self.curr.unwrap();
619             match n {
620                 '/' if self.nextch_is('*') => {
621                     level += 1;
622                     self.bump();
623                 }
624                 '*' if self.nextch_is('/') => {
625                     level -= 1;
626                     self.bump();
627                 }
628                 '\r' => {
629                     has_cr = true;
630                 }
631                 _ => (),
632             }
633             self.bump();
634         }
635
636         self.with_str_from(start_bpos, |string| {
637             // but comments with only "*"s between two "/"s are not
638             let tok = if is_block_doc_comment(string) {
639                 let string = if has_cr {
640                     self.translate_crlf(start_bpos,
641                                         string,
642                                         "bare CR not allowed in block doc-comment")
643                 } else {
644                     string.into()
645                 };
646                 token::DocComment(token::intern(&string[..]))
647             } else {
648                 token::Comment
649             };
650
651             Some(TokenAndSpan {
652                 tok: tok,
653                 sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
654             })
655         })
656     }
657
658     /// Scan through any digits (base `scan_radix`) or underscores,
659     /// and return how many digits there were.
660     ///
661     /// `real_radix` represents the true radix of the number we're
662     /// interested in, and errors will be emitted for any digits
663     /// between `real_radix` and `scan_radix`.
664     fn scan_digits(&mut self, real_radix: u32, scan_radix: u32) -> usize {
665         assert!(real_radix <= scan_radix);
666         let mut len = 0;
667         loop {
668             let c = self.curr;
669             if c == Some('_') {
670                 debug!("skipping a _");
671                 self.bump();
672                 continue;
673             }
674             match c.and_then(|cc| cc.to_digit(scan_radix)) {
675                 Some(_) => {
676                     debug!("{:?} in scan_digits", c);
677                     // check that the hypothetical digit is actually
678                     // in range for the true radix
679                     if c.unwrap().to_digit(real_radix).is_none() {
680                         self.err_span_(self.last_pos,
681                                        self.pos,
682                                        &format!("invalid digit for a base {} literal", real_radix));
683                     }
684                     len += 1;
685                     self.bump();
686                 }
687                 _ => return len,
688             }
689         }
690     }
691
692     /// Lex a LIT_INTEGER or a LIT_FLOAT
693     fn scan_number(&mut self, c: char) -> token::Lit {
694         let num_digits;
695         let mut base = 10;
696         let start_bpos = self.last_pos;
697
698         self.bump();
699
700         if c == '0' {
701             match self.curr.unwrap_or('\0') {
702                 'b' => {
703                     self.bump();
704                     base = 2;
705                     num_digits = self.scan_digits(2, 10);
706                 }
707                 'o' => {
708                     self.bump();
709                     base = 8;
710                     num_digits = self.scan_digits(8, 10);
711                 }
712                 'x' => {
713                     self.bump();
714                     base = 16;
715                     num_digits = self.scan_digits(16, 16);
716                 }
717                 '0'...'9' | '_' | '.' => {
718                     num_digits = self.scan_digits(10, 10) + 1;
719                 }
720                 _ => {
721                     // just a 0
722                     return token::Integer(self.name_from(start_bpos));
723                 }
724             }
725         } else if c.is_digit(10) {
726             num_digits = self.scan_digits(10, 10) + 1;
727         } else {
728             num_digits = 0;
729         }
730
731         if num_digits == 0 {
732             self.err_span_(start_bpos,
733                            self.last_pos,
734                            "no valid digits found for number");
735             return token::Integer(token::intern("0"));
736         }
737
738         // might be a float, but don't be greedy if this is actually an
739         // integer literal followed by field/method access or a range pattern
740         // (`0..2` and `12.foo()`)
741         if self.curr_is('.') && !self.nextch_is('.') &&
742            !self.nextch()
743                 .unwrap_or('\0')
744                 .is_xid_start() {
745             // might have stuff after the ., and if it does, it needs to start
746             // with a number
747             self.bump();
748             if self.curr.unwrap_or('\0').is_digit(10) {
749                 self.scan_digits(10, 10);
750                 self.scan_float_exponent();
751             }
752             let last_pos = self.last_pos;
753             self.check_float_base(start_bpos, last_pos, base);
754             return token::Float(self.name_from(start_bpos));
755         } else {
756             // it might be a float if it has an exponent
757             if self.curr_is('e') || self.curr_is('E') {
758                 self.scan_float_exponent();
759                 let last_pos = self.last_pos;
760                 self.check_float_base(start_bpos, last_pos, base);
761                 return token::Float(self.name_from(start_bpos));
762             }
763             // but we certainly have an integer!
764             return token::Integer(self.name_from(start_bpos));
765         }
766     }
767
768     /// Scan over `n_digits` hex digits, stopping at `delim`, reporting an
769     /// error if too many or too few digits are encountered.
770     fn scan_hex_digits(&mut self, n_digits: usize, delim: char, below_0x7f_only: bool) -> bool {
771         debug!("scanning {} digits until {:?}", n_digits, delim);
772         let start_bpos = self.last_pos;
773         let mut accum_int = 0;
774
775         let mut valid = true;
776         for _ in 0..n_digits {
777             if self.is_eof() {
778                 let last_bpos = self.last_pos;
779                 panic!(self.fatal_span_(start_bpos,
780                                         last_bpos,
781                                         "unterminated numeric character escape"));
782             }
783             if self.curr_is(delim) {
784                 let last_bpos = self.last_pos;
785                 self.err_span_(start_bpos,
786                                last_bpos,
787                                "numeric character escape is too short");
788                 valid = false;
789                 break;
790             }
791             let c = self.curr.unwrap_or('\x00');
792             accum_int *= 16;
793             accum_int += c.to_digit(16).unwrap_or_else(|| {
794                 self.err_span_char(self.last_pos,
795                                    self.pos,
796                                    "invalid character in numeric character escape",
797                                    c);
798
799                 valid = false;
800                 0
801             });
802             self.bump();
803         }
804
805         if below_0x7f_only && accum_int >= 0x80 {
806             self.err_span_(start_bpos,
807                            self.last_pos,
808                            "this form of character escape may only be used with characters in \
809                             the range [\\x00-\\x7f]");
810             valid = false;
811         }
812
813         match char::from_u32(accum_int) {
814             Some(_) => valid,
815             None => {
816                 let last_bpos = self.last_pos;
817                 self.err_span_(start_bpos, last_bpos, "invalid numeric character escape");
818                 false
819             }
820         }
821     }
822
823     /// Scan for a single (possibly escaped) byte or char
824     /// in a byte, (non-raw) byte string, char, or (non-raw) string literal.
825     /// `start` is the position of `first_source_char`, which is already consumed.
826     ///
827     /// Returns true if there was a valid char/byte, false otherwise.
828     fn scan_char_or_byte(&mut self,
829                          start: BytePos,
830                          first_source_char: char,
831                          ascii_only: bool,
832                          delim: char)
833                          -> bool {
834         match first_source_char {
835             '\\' => {
836                 // '\X' for some X must be a character constant:
837                 let escaped = self.curr;
838                 let escaped_pos = self.last_pos;
839                 self.bump();
840                 match escaped {
841                     None => {}  // EOF here is an error that will be checked later.
842                     Some(e) => {
843                         return match e {
844                             'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true,
845                             'x' => self.scan_byte_escape(delim, !ascii_only),
846                             'u' => {
847                                 let valid = if self.curr_is('{') {
848                                     self.scan_unicode_escape(delim) && !ascii_only
849                                 } else {
850                                     let span = syntax_pos::mk_sp(start, self.last_pos);
851                                     self.span_diagnostic
852                                         .struct_span_err(span, "incorrect unicode escape sequence")
853                                         .span_help(span,
854                                                    "format of unicode escape sequences is \
855                                                     `\\u{…}`")
856                                         .emit();
857                                     false
858                                 };
859                                 if ascii_only {
860                                     self.err_span_(start,
861                                                    self.last_pos,
862                                                    "unicode escape sequences cannot be used as a \
863                                                     byte or in a byte string");
864                                 }
865                                 valid
866
867                             }
868                             '\n' if delim == '"' => {
869                                 self.consume_whitespace();
870                                 true
871                             }
872                             '\r' if delim == '"' && self.curr_is('\n') => {
873                                 self.consume_whitespace();
874                                 true
875                             }
876                             c => {
877                                 let last_pos = self.last_pos;
878                                 let mut err = self.struct_err_span_char(escaped_pos,
879                                                                         last_pos,
880                                                                         if ascii_only {
881                                                                             "unknown byte escape"
882                                                                         } else {
883                                                                             "unknown character \
884                                                                              escape"
885                                                                         },
886                                                                         c);
887                                 if e == '\r' {
888                                     err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
889                                                   "this is an isolated carriage return; consider \
890                                                    checking your editor and version control \
891                                                    settings");
892                                 }
893                                 if (e == '{' || e == '}') && !ascii_only {
894                                     err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
895                                                   "if used in a formatting string, curly braces \
896                                                    are escaped with `{{` and `}}`");
897                                 }
898                                 err.emit();
899                                 false
900                             }
901                         }
902                     }
903                 }
904             }
905             '\t' | '\n' | '\r' | '\'' if delim == '\'' => {
906                 let last_pos = self.last_pos;
907                 self.err_span_char(start,
908                                    last_pos,
909                                    if ascii_only {
910                                        "byte constant must be escaped"
911                                    } else {
912                                        "character constant must be escaped"
913                                    },
914                                    first_source_char);
915                 return false;
916             }
917             '\r' => {
918                 if self.curr_is('\n') {
919                     self.bump();
920                     return true;
921                 } else {
922                     self.err_span_(start,
923                                    self.last_pos,
924                                    "bare CR not allowed in string, use \\r instead");
925                     return false;
926                 }
927             }
928             _ => {
929                 if ascii_only && first_source_char > '\x7F' {
930                     let last_pos = self.last_pos;
931                     self.err_span_(start,
932                                    last_pos,
933                                    "byte constant must be ASCII. Use a \\xHH escape for a \
934                                     non-ASCII byte");
935                     return false;
936                 }
937             }
938         }
939         true
940     }
941
942     /// Scan over a \u{...} escape
943     ///
944     /// At this point, we have already seen the \ and the u, the { is the current character. We
945     /// will read at least one digit, and up to 6, and pass over the }.
946     fn scan_unicode_escape(&mut self, delim: char) -> bool {
947         self.bump(); // past the {
948         let start_bpos = self.last_pos;
949         let mut count = 0;
950         let mut accum_int = 0;
951         let mut valid = true;
952
953         while !self.curr_is('}') && count <= 6 {
954             let c = match self.curr {
955                 Some(c) => c,
956                 None => {
957                     panic!(self.fatal_span_(start_bpos,
958                                             self.last_pos,
959                                             "unterminated unicode escape (found EOF)"));
960                 }
961             };
962             accum_int *= 16;
963             accum_int += c.to_digit(16).unwrap_or_else(|| {
964                 if c == delim {
965                     panic!(self.fatal_span_(self.last_pos,
966                                             self.pos,
967                                             "unterminated unicode escape (needed a `}`)"));
968                 } else {
969                     self.err_span_char(self.last_pos,
970                                        self.pos,
971                                        "invalid character in unicode escape",
972                                        c);
973                 }
974                 valid = false;
975                 0
976             });
977             self.bump();
978             count += 1;
979         }
980
981         if count > 6 {
982             self.err_span_(start_bpos,
983                            self.last_pos,
984                            "overlong unicode escape (can have at most 6 hex digits)");
985             valid = false;
986         }
987
988         if valid && (char::from_u32(accum_int).is_none() || count == 0) {
989             self.err_span_(start_bpos,
990                            self.last_pos,
991                            "invalid unicode character escape");
992             valid = false;
993         }
994
995         self.bump(); // past the ending }
996         valid
997     }
998
999     /// Scan over a float exponent.
1000     fn scan_float_exponent(&mut self) {
1001         if self.curr_is('e') || self.curr_is('E') {
1002             self.bump();
1003             if self.curr_is('-') || self.curr_is('+') {
1004                 self.bump();
1005             }
1006             if self.scan_digits(10, 10) == 0 {
1007                 self.err_span_(self.last_pos,
1008                                self.pos,
1009                                "expected at least one digit in exponent")
1010             }
1011         }
1012     }
1013
1014     /// Check that a base is valid for a floating literal, emitting a nice
1015     /// error if it isn't.
1016     fn check_float_base(&mut self, start_bpos: BytePos, last_bpos: BytePos, base: usize) {
1017         match base {
1018             16 => {
1019                 self.err_span_(start_bpos,
1020                                last_bpos,
1021                                "hexadecimal float literal is not supported")
1022             }
1023             8 => {
1024                 self.err_span_(start_bpos,
1025                                last_bpos,
1026                                "octal float literal is not supported")
1027             }
1028             2 => {
1029                 self.err_span_(start_bpos,
1030                                last_bpos,
1031                                "binary float literal is not supported")
1032             }
1033             _ => (),
1034         }
1035     }
1036
1037     fn binop(&mut self, op: token::BinOpToken) -> token::Token {
1038         self.bump();
1039         if self.curr_is('=') {
1040             self.bump();
1041             return token::BinOpEq(op);
1042         } else {
1043             return token::BinOp(op);
1044         }
1045     }
1046
1047     /// Return the next token from the string, advances the input past that
1048     /// token, and updates the interner
1049     fn next_token_inner(&mut self) -> Result<token::Token, ()> {
1050         let c = self.curr;
1051         if ident_start(c) &&
1052            match (c.unwrap(), self.nextch(), self.nextnextch()) {
1053             // Note: r as in r" or r#" is part of a raw string literal,
1054             // b as in b' is part of a byte literal.
1055             // They are not identifiers, and are handled further down.
1056             ('r', Some('"'), _) |
1057             ('r', Some('#'), _) |
1058             ('b', Some('"'), _) |
1059             ('b', Some('\''), _) |
1060             ('b', Some('r'), Some('"')) |
1061             ('b', Some('r'), Some('#')) => false,
1062             _ => true,
1063         } {
1064             let start = self.last_pos;
1065             while ident_continue(self.curr) {
1066                 self.bump();
1067             }
1068
1069             return Ok(self.with_str_from(start, |string| {
1070                 if string == "_" {
1071                     token::Underscore
1072                 } else {
1073                     // FIXME: perform NFKC normalization here. (Issue #2253)
1074                     token::Ident(str_to_ident(string))
1075                 }
1076             }));
1077         }
1078
1079         if is_dec_digit(c) {
1080             let num = self.scan_number(c.unwrap());
1081             let suffix = self.scan_optional_raw_name();
1082             debug!("next_token_inner: scanned number {:?}, {:?}", num, suffix);
1083             return Ok(token::Literal(num, suffix));
1084         }
1085
1086         match c.expect("next_token_inner called at EOF") {
1087             // One-byte tokens.
1088             ';' => {
1089                 self.bump();
1090                 return Ok(token::Semi);
1091             }
1092             ',' => {
1093                 self.bump();
1094                 return Ok(token::Comma);
1095             }
1096             '.' => {
1097                 self.bump();
1098                 return if self.curr_is('.') {
1099                     self.bump();
1100                     if self.curr_is('.') {
1101                         self.bump();
1102                         Ok(token::DotDotDot)
1103                     } else {
1104                         Ok(token::DotDot)
1105                     }
1106                 } else {
1107                     Ok(token::Dot)
1108                 };
1109             }
1110             '(' => {
1111                 self.bump();
1112                 return Ok(token::OpenDelim(token::Paren));
1113             }
1114             ')' => {
1115                 self.bump();
1116                 return Ok(token::CloseDelim(token::Paren));
1117             }
1118             '{' => {
1119                 self.bump();
1120                 return Ok(token::OpenDelim(token::Brace));
1121             }
1122             '}' => {
1123                 self.bump();
1124                 return Ok(token::CloseDelim(token::Brace));
1125             }
1126             '[' => {
1127                 self.bump();
1128                 return Ok(token::OpenDelim(token::Bracket));
1129             }
1130             ']' => {
1131                 self.bump();
1132                 return Ok(token::CloseDelim(token::Bracket));
1133             }
1134             '@' => {
1135                 self.bump();
1136                 return Ok(token::At);
1137             }
1138             '#' => {
1139                 self.bump();
1140                 return Ok(token::Pound);
1141             }
1142             '~' => {
1143                 self.bump();
1144                 return Ok(token::Tilde);
1145             }
1146             '?' => {
1147                 self.bump();
1148                 return Ok(token::Question);
1149             }
1150             ':' => {
1151                 self.bump();
1152                 if self.curr_is(':') {
1153                     self.bump();
1154                     return Ok(token::ModSep);
1155                 } else {
1156                     return Ok(token::Colon);
1157                 }
1158             }
1159
1160             '$' => {
1161                 self.bump();
1162                 return Ok(token::Dollar);
1163             }
1164
1165             // Multi-byte tokens.
1166             '=' => {
1167                 self.bump();
1168                 if self.curr_is('=') {
1169                     self.bump();
1170                     return Ok(token::EqEq);
1171                 } else if self.curr_is('>') {
1172                     self.bump();
1173                     return Ok(token::FatArrow);
1174                 } else {
1175                     return Ok(token::Eq);
1176                 }
1177             }
1178             '!' => {
1179                 self.bump();
1180                 if self.curr_is('=') {
1181                     self.bump();
1182                     return Ok(token::Ne);
1183                 } else {
1184                     return Ok(token::Not);
1185                 }
1186             }
1187             '<' => {
1188                 self.bump();
1189                 match self.curr.unwrap_or('\x00') {
1190                     '=' => {
1191                         self.bump();
1192                         return Ok(token::Le);
1193                     }
1194                     '<' => {
1195                         return Ok(self.binop(token::Shl));
1196                     }
1197                     '-' => {
1198                         self.bump();
1199                         match self.curr.unwrap_or('\x00') {
1200                             _ => {
1201                                 return Ok(token::LArrow);
1202                             }
1203                         }
1204                     }
1205                     _ => {
1206                         return Ok(token::Lt);
1207                     }
1208                 }
1209             }
1210             '>' => {
1211                 self.bump();
1212                 match self.curr.unwrap_or('\x00') {
1213                     '=' => {
1214                         self.bump();
1215                         return Ok(token::Ge);
1216                     }
1217                     '>' => {
1218                         return Ok(self.binop(token::Shr));
1219                     }
1220                     _ => {
1221                         return Ok(token::Gt);
1222                     }
1223                 }
1224             }
1225             '\'' => {
1226                 // Either a character constant 'a' OR a lifetime name 'abc
1227                 let start_with_quote = self.last_pos;
1228                 self.bump();
1229                 let start = self.last_pos;
1230
1231                 // the eof will be picked up by the final `'` check below
1232                 let c2 = self.curr.unwrap_or('\x00');
1233                 self.bump();
1234
1235                 // If the character is an ident start not followed by another single
1236                 // quote, then this is a lifetime name:
1237                 if ident_start(Some(c2)) && !self.curr_is('\'') {
1238                     while ident_continue(self.curr) {
1239                         self.bump();
1240                     }
1241                     // lifetimes shouldn't end with a single quote
1242                     // if we find one, then this is an invalid character literal
1243                     if self.curr_is('\'') {
1244                         panic!(self.fatal_span_verbose(
1245                                start_with_quote, self.pos,
1246                                String::from("character literal may only contain one codepoint")));
1247
1248                     }
1249
1250                     // Include the leading `'` in the real identifier, for macro
1251                     // expansion purposes. See #12512 for the gory details of why
1252                     // this is necessary.
1253                     let ident = self.with_str_from(start, |lifetime_name| {
1254                         str_to_ident(&format!("'{}", lifetime_name))
1255                     });
1256
1257                     // Conjure up a "keyword checking ident" to make sure that
1258                     // the lifetime name is not a keyword.
1259                     let keyword_checking_ident = self.with_str_from(start, |lifetime_name| {
1260                         str_to_ident(lifetime_name)
1261                     });
1262                     let keyword_checking_token = &token::Ident(keyword_checking_ident);
1263                     let last_bpos = self.last_pos;
1264                     if keyword_checking_token.is_any_keyword() &&
1265                        !keyword_checking_token.is_keyword(keywords::Static) {
1266                         self.err_span_(start, last_bpos, "lifetimes cannot use keyword names");
1267                     }
1268
1269                     return Ok(token::Lifetime(ident));
1270                 }
1271
1272                 let valid = self.scan_char_or_byte(start,
1273                                                    c2,
1274                                                    // ascii_only =
1275                                                    false,
1276                                                    '\'');
1277
1278                 if !self.curr_is('\'') {
1279                     panic!(self.fatal_span_verbose(
1280                            start_with_quote, self.last_pos,
1281                            String::from("character literal may only contain one codepoint")));
1282                 }
1283
1284                 let id = if valid {
1285                     self.name_from(start)
1286                 } else {
1287                     token::intern("0")
1288                 };
1289                 self.bump(); // advance curr past token
1290                 let suffix = self.scan_optional_raw_name();
1291                 return Ok(token::Literal(token::Char(id), suffix));
1292             }
1293             'b' => {
1294                 self.bump();
1295                 let lit = match self.curr {
1296                     Some('\'') => self.scan_byte(),
1297                     Some('"') => self.scan_byte_string(),
1298                     Some('r') => self.scan_raw_byte_string(),
1299                     _ => unreachable!(),  // Should have been a token::Ident above.
1300                 };
1301                 let suffix = self.scan_optional_raw_name();
1302                 return Ok(token::Literal(lit, suffix));
1303             }
1304             '"' => {
1305                 let start_bpos = self.last_pos;
1306                 let mut valid = true;
1307                 self.bump();
1308                 while !self.curr_is('"') {
1309                     if self.is_eof() {
1310                         let last_bpos = self.last_pos;
1311                         panic!(self.fatal_span_(start_bpos,
1312                                                 last_bpos,
1313                                                 "unterminated double quote string"));
1314                     }
1315
1316                     let ch_start = self.last_pos;
1317                     let ch = self.curr.unwrap();
1318                     self.bump();
1319                     valid &= self.scan_char_or_byte(ch_start,
1320                                                     ch,
1321                                                     // ascii_only =
1322                                                     false,
1323                                                     '"');
1324                 }
1325                 // adjust for the ASCII " at the start of the literal
1326                 let id = if valid {
1327                     self.name_from(start_bpos + BytePos(1))
1328                 } else {
1329                     token::intern("??")
1330                 };
1331                 self.bump();
1332                 let suffix = self.scan_optional_raw_name();
1333                 return Ok(token::Literal(token::Str_(id), suffix));
1334             }
1335             'r' => {
1336                 let start_bpos = self.last_pos;
1337                 self.bump();
1338                 let mut hash_count = 0;
1339                 while self.curr_is('#') {
1340                     self.bump();
1341                     hash_count += 1;
1342                 }
1343
1344                 if self.is_eof() {
1345                     let last_bpos = self.last_pos;
1346                     panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
1347                 } else if !self.curr_is('"') {
1348                     let last_bpos = self.last_pos;
1349                     let curr_char = self.curr.unwrap();
1350                     panic!(self.fatal_span_char(start_bpos,
1351                                                 last_bpos,
1352                                                 "found invalid character; only `#` is allowed \
1353                                                  in raw string delimitation",
1354                                                 curr_char));
1355                 }
1356                 self.bump();
1357                 let content_start_bpos = self.last_pos;
1358                 let mut content_end_bpos;
1359                 let mut valid = true;
1360                 'outer: loop {
1361                     if self.is_eof() {
1362                         let last_bpos = self.last_pos;
1363                         panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
1364                     }
1365                     // if self.curr_is('"') {
1366                     // content_end_bpos = self.last_pos;
1367                     // for _ in 0..hash_count {
1368                     // self.bump();
1369                     // if !self.curr_is('#') {
1370                     // continue 'outer;
1371                     let c = self.curr.unwrap();
1372                     match c {
1373                         '"' => {
1374                             content_end_bpos = self.last_pos;
1375                             for _ in 0..hash_count {
1376                                 self.bump();
1377                                 if !self.curr_is('#') {
1378                                     continue 'outer;
1379                                 }
1380                             }
1381                             break;
1382                         }
1383                         '\r' => {
1384                             if !self.nextch_is('\n') {
1385                                 let last_bpos = self.last_pos;
1386                                 self.err_span_(start_bpos,
1387                                                last_bpos,
1388                                                "bare CR not allowed in raw string, use \\r \
1389                                                 instead");
1390                                 valid = false;
1391                             }
1392                         }
1393                         _ => (),
1394                     }
1395                     self.bump();
1396                 }
1397                 self.bump();
1398                 let id = if valid {
1399                     self.name_from_to(content_start_bpos, content_end_bpos)
1400                 } else {
1401                     token::intern("??")
1402                 };
1403                 let suffix = self.scan_optional_raw_name();
1404                 return Ok(token::Literal(token::StrRaw(id, hash_count), suffix));
1405             }
1406             '-' => {
1407                 if self.nextch_is('>') {
1408                     self.bump();
1409                     self.bump();
1410                     return Ok(token::RArrow);
1411                 } else {
1412                     return Ok(self.binop(token::Minus));
1413                 }
1414             }
1415             '&' => {
1416                 if self.nextch_is('&') {
1417                     self.bump();
1418                     self.bump();
1419                     return Ok(token::AndAnd);
1420                 } else {
1421                     return Ok(self.binop(token::And));
1422                 }
1423             }
1424             '|' => {
1425                 match self.nextch() {
1426                     Some('|') => {
1427                         self.bump();
1428                         self.bump();
1429                         return Ok(token::OrOr);
1430                     }
1431                     _ => {
1432                         return Ok(self.binop(token::Or));
1433                     }
1434                 }
1435             }
1436             '+' => {
1437                 return Ok(self.binop(token::Plus));
1438             }
1439             '*' => {
1440                 return Ok(self.binop(token::Star));
1441             }
1442             '/' => {
1443                 return Ok(self.binop(token::Slash));
1444             }
1445             '^' => {
1446                 return Ok(self.binop(token::Caret));
1447             }
1448             '%' => {
1449                 return Ok(self.binop(token::Percent));
1450             }
1451             c => {
1452                 let last_bpos = self.last_pos;
1453                 let bpos = self.pos;
1454                 let mut err = self.struct_fatal_span_char(last_bpos,
1455                                                           bpos,
1456                                                           "unknown start of token",
1457                                                           c);
1458                 unicode_chars::check_for_substitution(&self, c, &mut err);
1459                 self.fatal_errs.push(err);
1460                 Err(())
1461             }
1462         }
1463     }
1464
1465     fn consume_whitespace(&mut self) {
1466         while is_pattern_whitespace(self.curr) && !self.is_eof() {
1467             self.bump();
1468         }
1469     }
1470
1471     fn read_to_eol(&mut self) -> String {
1472         let mut val = String::new();
1473         while !self.curr_is('\n') && !self.is_eof() {
1474             val.push(self.curr.unwrap());
1475             self.bump();
1476         }
1477         if self.curr_is('\n') {
1478             self.bump();
1479         }
1480         return val;
1481     }
1482
1483     fn read_one_line_comment(&mut self) -> String {
1484         let val = self.read_to_eol();
1485         assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/') ||
1486                 (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
1487         return val;
1488     }
1489
1490     fn consume_non_eol_whitespace(&mut self) {
1491         while is_pattern_whitespace(self.curr) && !self.curr_is('\n') && !self.is_eof() {
1492             self.bump();
1493         }
1494     }
1495
1496     fn peeking_at_comment(&self) -> bool {
1497         (self.curr_is('/') && self.nextch_is('/')) || (self.curr_is('/') && self.nextch_is('*')) ||
1498         // consider shebangs comments, but not inner attributes
1499         (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
1500     }
1501
1502     fn scan_byte(&mut self) -> token::Lit {
1503         self.bump();
1504         let start = self.last_pos;
1505
1506         // the eof will be picked up by the final `'` check below
1507         let c2 = self.curr.unwrap_or('\x00');
1508         self.bump();
1509
1510         let valid = self.scan_char_or_byte(start,
1511                                            c2,
1512                                            // ascii_only =
1513                                            true,
1514                                            '\'');
1515         if !self.curr_is('\'') {
1516             // Byte offsetting here is okay because the
1517             // character before position `start` are an
1518             // ascii single quote and ascii 'b'.
1519             let last_pos = self.last_pos;
1520             panic!(self.fatal_span_verbose(start - BytePos(2),
1521                                            last_pos,
1522                                            "unterminated byte constant".to_string()));
1523         }
1524
1525         let id = if valid {
1526             self.name_from(start)
1527         } else {
1528             token::intern("?")
1529         };
1530         self.bump(); // advance curr past token
1531         return token::Byte(id);
1532     }
1533
1534     fn scan_byte_escape(&mut self, delim: char, below_0x7f_only: bool) -> bool {
1535         self.scan_hex_digits(2, delim, below_0x7f_only)
1536     }
1537
1538     fn scan_byte_string(&mut self) -> token::Lit {
1539         self.bump();
1540         let start = self.last_pos;
1541         let mut valid = true;
1542
1543         while !self.curr_is('"') {
1544             if self.is_eof() {
1545                 let last_pos = self.last_pos;
1546                 panic!(self.fatal_span_(start, last_pos, "unterminated double quote byte string"));
1547             }
1548
1549             let ch_start = self.last_pos;
1550             let ch = self.curr.unwrap();
1551             self.bump();
1552             valid &= self.scan_char_or_byte(ch_start,
1553                                             ch,
1554                                             // ascii_only =
1555                                             true,
1556                                             '"');
1557         }
1558         let id = if valid {
1559             self.name_from(start)
1560         } else {
1561             token::intern("??")
1562         };
1563         self.bump();
1564         return token::ByteStr(id);
1565     }
1566
1567     fn scan_raw_byte_string(&mut self) -> token::Lit {
1568         let start_bpos = self.last_pos;
1569         self.bump();
1570         let mut hash_count = 0;
1571         while self.curr_is('#') {
1572             self.bump();
1573             hash_count += 1;
1574         }
1575
1576         if self.is_eof() {
1577             let last_pos = self.last_pos;
1578             panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"));
1579         } else if !self.curr_is('"') {
1580             let last_pos = self.last_pos;
1581             let ch = self.curr.unwrap();
1582             panic!(self.fatal_span_char(start_bpos,
1583                                         last_pos,
1584                                         "found invalid character; only `#` is allowed in raw \
1585                                          string delimitation",
1586                                         ch));
1587         }
1588         self.bump();
1589         let content_start_bpos = self.last_pos;
1590         let mut content_end_bpos;
1591         'outer: loop {
1592             match self.curr {
1593                 None => {
1594                     let last_pos = self.last_pos;
1595                     panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"))
1596                 }
1597                 Some('"') => {
1598                     content_end_bpos = self.last_pos;
1599                     for _ in 0..hash_count {
1600                         self.bump();
1601                         if !self.curr_is('#') {
1602                             continue 'outer;
1603                         }
1604                     }
1605                     break;
1606                 }
1607                 Some(c) => {
1608                     if c > '\x7F' {
1609                         let last_pos = self.last_pos;
1610                         self.err_span_char(last_pos, last_pos, "raw byte string must be ASCII", c);
1611                     }
1612                 }
1613             }
1614             self.bump();
1615         }
1616         self.bump();
1617         return token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
1618                                  hash_count);
1619     }
1620 }
1621
1622 // This tests the character for the unicode property 'PATTERN_WHITE_SPACE' which
1623 // is guaranteed to be forward compatible. http://unicode.org/reports/tr31/#R3
1624 pub fn is_pattern_whitespace(c: Option<char>) -> bool {
1625     c.map_or(false, Pattern_White_Space)
1626 }
1627
1628 fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
1629     match c {
1630         Some(c) => lo <= c && c <= hi,
1631         _ => false,
1632     }
1633 }
1634
1635 fn is_dec_digit(c: Option<char>) -> bool {
1636     return in_range(c, '0', '9');
1637 }
1638
1639 pub fn is_doc_comment(s: &str) -> bool {
1640     let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') ||
1641               s.starts_with("//!");
1642     debug!("is {:?} a doc comment? {}", s, res);
1643     res
1644 }
1645
1646 pub fn is_block_doc_comment(s: &str) -> bool {
1647     // Prevent `/**/` from being parsed as a doc comment
1648     let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') ||
1649                s.starts_with("/*!")) && s.len() >= 5;
1650     debug!("is {:?} a doc comment? {}", s, res);
1651     res
1652 }
1653
1654 fn ident_start(c: Option<char>) -> bool {
1655     let c = match c {
1656         Some(c) => c,
1657         None => return false,
1658     };
1659
1660     (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c > '\x7f' && c.is_xid_start())
1661 }
1662
1663 fn ident_continue(c: Option<char>) -> bool {
1664     let c = match c {
1665         Some(c) => c,
1666         None => return false,
1667     };
1668
1669     (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' ||
1670     (c > '\x7f' && c.is_xid_continue())
1671 }
1672
1673 #[cfg(test)]
1674 mod tests {
1675     use super::*;
1676
1677     use syntax_pos::{BytePos, Span, NO_EXPANSION};
1678     use codemap::CodeMap;
1679     use errors;
1680     use parse::token;
1681     use parse::token::str_to_ident;
1682     use std::io;
1683     use std::rc::Rc;
1684
1685     fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler {
1686         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
1687         let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
1688                                                 None,
1689                                                 Some(cm),
1690                                                 errors::snippet::FormatMode::EnvironmentSelected);
1691         errors::Handler::with_emitter(true, false, Box::new(emitter))
1692     }
1693
1694     // open a string reader for the given string
1695     fn setup<'a>(cm: &CodeMap,
1696                  span_handler: &'a errors::Handler,
1697                  teststr: String)
1698                  -> StringReader<'a> {
1699         let fm = cm.new_filemap("zebra.rs".to_string(), None, teststr);
1700         StringReader::new(span_handler, fm)
1701     }
1702
1703     #[test]
1704     fn t1() {
1705         let cm = Rc::new(CodeMap::new());
1706         let sh = mk_sh(cm.clone());
1707         let mut string_reader = setup(&cm,
1708                                       &sh,
1709                                       "/* my source file */ fn main() { println!(\"zebra\"); }\n"
1710                                           .to_string());
1711         let id = str_to_ident("fn");
1712         assert_eq!(string_reader.next_token().tok, token::Comment);
1713         assert_eq!(string_reader.next_token().tok, token::Whitespace);
1714         let tok1 = string_reader.next_token();
1715         let tok2 = TokenAndSpan {
1716             tok: token::Ident(id),
1717             sp: Span {
1718                 lo: BytePos(21),
1719                 hi: BytePos(23),
1720                 expn_id: NO_EXPANSION,
1721             },
1722         };
1723         assert_eq!(tok1, tok2);
1724         assert_eq!(string_reader.next_token().tok, token::Whitespace);
1725         // the 'main' id is already read:
1726         assert_eq!(string_reader.last_pos.clone(), BytePos(28));
1727         // read another token:
1728         let tok3 = string_reader.next_token();
1729         let tok4 = TokenAndSpan {
1730             tok: token::Ident(str_to_ident("main")),
1731             sp: Span {
1732                 lo: BytePos(24),
1733                 hi: BytePos(28),
1734                 expn_id: NO_EXPANSION,
1735             },
1736         };
1737         assert_eq!(tok3, tok4);
1738         // the lparen is already read:
1739         assert_eq!(string_reader.last_pos.clone(), BytePos(29))
1740     }
1741
1742     // check that the given reader produces the desired stream
1743     // of tokens (stop checking after exhausting the expected vec)
1744     fn check_tokenization(mut string_reader: StringReader, expected: Vec<token::Token>) {
1745         for expected_tok in &expected {
1746             assert_eq!(&string_reader.next_token().tok, expected_tok);
1747         }
1748     }
1749
1750     // make the identifier by looking up the string in the interner
1751     fn mk_ident(id: &str) -> token::Token {
1752         token::Ident(str_to_ident(id))
1753     }
1754
1755     #[test]
1756     fn doublecolonparsing() {
1757         let cm = Rc::new(CodeMap::new());
1758         let sh = mk_sh(cm.clone());
1759         check_tokenization(setup(&cm, &sh, "a b".to_string()),
1760                            vec![mk_ident("a"), token::Whitespace, mk_ident("b")]);
1761     }
1762
1763     #[test]
1764     fn dcparsing_2() {
1765         let cm = Rc::new(CodeMap::new());
1766         let sh = mk_sh(cm.clone());
1767         check_tokenization(setup(&cm, &sh, "a::b".to_string()),
1768                            vec![mk_ident("a"), token::ModSep, mk_ident("b")]);
1769     }
1770
1771     #[test]
1772     fn dcparsing_3() {
1773         let cm = Rc::new(CodeMap::new());
1774         let sh = mk_sh(cm.clone());
1775         check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
1776                            vec![mk_ident("a"), token::Whitespace, token::ModSep, mk_ident("b")]);
1777     }
1778
1779     #[test]
1780     fn dcparsing_4() {
1781         let cm = Rc::new(CodeMap::new());
1782         let sh = mk_sh(cm.clone());
1783         check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
1784                            vec![mk_ident("a"), token::ModSep, token::Whitespace, mk_ident("b")]);
1785     }
1786
1787     #[test]
1788     fn character_a() {
1789         let cm = Rc::new(CodeMap::new());
1790         let sh = mk_sh(cm.clone());
1791         assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
1792                    token::Literal(token::Char(token::intern("a")), None));
1793     }
1794
1795     #[test]
1796     fn character_space() {
1797         let cm = Rc::new(CodeMap::new());
1798         let sh = mk_sh(cm.clone());
1799         assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
1800                    token::Literal(token::Char(token::intern(" ")), None));
1801     }
1802
1803     #[test]
1804     fn character_escaped() {
1805         let cm = Rc::new(CodeMap::new());
1806         let sh = mk_sh(cm.clone());
1807         assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
1808                    token::Literal(token::Char(token::intern("\\n")), None));
1809     }
1810
1811     #[test]
1812     fn lifetime_name() {
1813         let cm = Rc::new(CodeMap::new());
1814         let sh = mk_sh(cm.clone());
1815         assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
1816                    token::Lifetime(token::str_to_ident("'abc")));
1817     }
1818
1819     #[test]
1820     fn raw_string() {
1821         let cm = Rc::new(CodeMap::new());
1822         let sh = mk_sh(cm.clone());
1823         assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
1824                        .next_token()
1825                        .tok,
1826                    token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None));
1827     }
1828
1829     #[test]
1830     fn literal_suffixes() {
1831         let cm = Rc::new(CodeMap::new());
1832         let sh = mk_sh(cm.clone());
1833         macro_rules! test {
1834             ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
1835                 assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
1836                            token::Literal(token::$tok_type(token::intern($tok_contents)),
1837                                           Some(token::intern("suffix"))));
1838                 // with a whitespace separator:
1839                 assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
1840                            token::Literal(token::$tok_type(token::intern($tok_contents)),
1841                                           None));
1842             }}
1843         }
1844
1845         test!("'a'", Char, "a");
1846         test!("b'a'", Byte, "a");
1847         test!("\"a\"", Str_, "a");
1848         test!("b\"a\"", ByteStr, "a");
1849         test!("1234", Integer, "1234");
1850         test!("0b101", Integer, "0b101");
1851         test!("0xABC", Integer, "0xABC");
1852         test!("1.0", Float, "1.0");
1853         test!("1.0e10", Float, "1.0e10");
1854
1855         assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
1856                    token::Literal(token::Integer(token::intern("2")),
1857                                   Some(token::intern("us"))));
1858         assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
1859                    token::Literal(token::StrRaw(token::intern("raw"), 3),
1860                                   Some(token::intern("suffix"))));
1861         assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
1862                    token::Literal(token::ByteStrRaw(token::intern("raw"), 3),
1863                                   Some(token::intern("suffix"))));
1864     }
1865
1866     #[test]
1867     fn line_doc_comments() {
1868         assert!(is_doc_comment("///"));
1869         assert!(is_doc_comment("/// blah"));
1870         assert!(!is_doc_comment("////"));
1871     }
1872
1873     #[test]
1874     fn nested_block_comments() {
1875         let cm = Rc::new(CodeMap::new());
1876         let sh = mk_sh(cm.clone());
1877         let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
1878         match lexer.next_token().tok {
1879             token::Comment => {}
1880             _ => panic!("expected a comment!"),
1881         }
1882         assert_eq!(lexer.next_token().tok,
1883                    token::Literal(token::Char(token::intern("a")), None));
1884     }
1885
1886     #[test]
1887     fn crlf_comments() {
1888         let cm = Rc::new(CodeMap::new());
1889         let sh = mk_sh(cm.clone());
1890         let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
1891         let comment = lexer.next_token();
1892         assert_eq!(comment.tok, token::Comment);
1893         assert_eq!(comment.sp, ::syntax_pos::mk_sp(BytePos(0), BytePos(7)));
1894         assert_eq!(lexer.next_token().tok, token::Whitespace);
1895         assert_eq!(lexer.next_token().tok,
1896                    token::DocComment(token::intern("/// test")));
1897     }
1898 }