]> git.lizzy.rs Git - rust.git/blob - src/lexer/mod.rs
2f8d3a402c52588db6e666fd6869fc10639d8377
[rust.git] / src / lexer / mod.rs
1 use {SyntaxKind, Token};
2 use syntax_kinds::*;
3
4 mod ptr;
5 use self::ptr::Ptr;
6
7 mod classes;
8 use self::classes::*;
9
10 mod numbers;
11 use self::numbers::scan_number;
12
13 mod strings;
14 use self::strings::{is_string_literal_start, scan_byte_char_or_string, scan_char, scan_raw_string,
15                     scan_string};
16
17 mod comments;
18 use self::comments::{scan_comment, scan_shebang};
19
20 pub fn tokenize(text: &str) -> Vec<Token> {
21     let mut text = text;
22     let mut acc = Vec::new();
23     while !text.is_empty() {
24         let token = next_token(text);
25         acc.push(token);
26         let len: u32 = token.len.into();
27         text = &text[len as usize..];
28     }
29     acc
30 }
31 pub fn next_token(text: &str) -> Token {
32     assert!(!text.is_empty());
33     let mut ptr = Ptr::new(text);
34     let c = ptr.bump().unwrap();
35     let kind = next_token_inner(c, &mut ptr);
36     let len = ptr.into_len();
37     Token { kind, len }
38 }
39
40 fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
41     if is_whitespace(c) {
42         ptr.bump_while(is_whitespace);
43         return WHITESPACE;
44     }
45
46     match c {
47         '#' => if scan_shebang(ptr) {
48             return SHEBANG;
49         },
50         '/' => if let Some(kind) = scan_comment(ptr) {
51             return kind;
52         },
53         _ => (),
54     }
55
56     let ident_start = is_ident_start(c) && !is_string_literal_start(c, ptr.next(), ptr.nnext());
57     if ident_start {
58         return scan_ident(c, ptr);
59     }
60
61     if is_dec_digit(c) {
62         let kind = scan_number(c, ptr);
63         scan_literal_suffix(ptr);
64         return kind;
65     }
66
67     // One-byte tokens.
68     match c {
69         ';' => return SEMI,
70         ',' => return COMMA,
71         '(' => return L_PAREN,
72         ')' => return R_PAREN,
73         '{' => return L_CURLY,
74         '}' => return R_CURLY,
75         '[' => return L_BRACK,
76         ']' => return R_BRACK,
77         '<' => return L_ANGLE,
78         '>' => return R_ANGLE,
79         '@' => return AT,
80         '#' => return POUND,
81         '~' => return TILDE,
82         '?' => return QUESTION,
83         '$' => return DOLLAR,
84         '&' => return AMPERSAND,
85         '|' => return PIPE,
86         '+' => return PLUS,
87         '*' => return STAR,
88         '/' => return SLASH,
89         '^' => return CARET,
90         '%' => return PERCENT,
91
92         // Multi-byte tokens.
93         '.' => {
94             return match (ptr.next(), ptr.nnext()) {
95                 (Some('.'), Some('.')) => {
96                     ptr.bump();
97                     ptr.bump();
98                     DOTDOTDOT
99                 }
100                 (Some('.'), Some('=')) => {
101                     ptr.bump();
102                     ptr.bump();
103                     DOTDOTEQ
104                 }
105                 (Some('.'), _) => {
106                     ptr.bump();
107                     DOTDOT
108                 }
109                 _ => DOT,
110             }
111         }
112         ':' => {
113             return match ptr.next() {
114                 Some(':') => {
115                     ptr.bump();
116                     COLONCOLON
117                 }
118                 _ => COLON,
119             }
120         }
121         '=' => {
122             return match ptr.next() {
123                 Some('=') => {
124                     ptr.bump();
125                     EQEQ
126                 }
127                 Some('>') => {
128                     ptr.bump();
129                     FAT_ARROW
130                 }
131                 _ => EQ,
132             }
133         }
134         '!' => {
135             return match ptr.next() {
136                 Some('=') => {
137                     ptr.bump();
138                     NEQ
139                 }
140                 _ => EXCL,
141             }
142         }
143         '-' => {
144             return if ptr.next_is('>') {
145                 ptr.bump();
146                 THIN_ARROW
147             } else {
148                 MINUS
149             }
150         }
151
152         // If the character is an ident start not followed by another single
153         // quote, then this is a lifetime name:
154         '\'' => {
155             return if ptr.next_is_p(is_ident_start) && !ptr.nnext_is('\'') {
156                 ptr.bump();
157                 while ptr.next_is_p(is_ident_continue) {
158                     ptr.bump();
159                 }
160                 // lifetimes shouldn't end with a single quote
161                 // if we find one, then this is an invalid character literal
162                 if ptr.next_is('\'') {
163                     ptr.bump();
164                     return CHAR; // TODO: error reporting
165                 }
166                 LIFETIME
167             } else {
168                 scan_char(ptr);
169                 scan_literal_suffix(ptr);
170                 CHAR
171             };
172         }
173         'b' => {
174             let kind = scan_byte_char_or_string(ptr);
175             scan_literal_suffix(ptr);
176             return kind;
177         }
178         '"' => {
179             scan_string(ptr);
180             scan_literal_suffix(ptr);
181             return STRING;
182         }
183         'r' => {
184             scan_raw_string(ptr);
185             scan_literal_suffix(ptr);
186             return RAW_STRING;
187         }
188         _ => (),
189     }
190     ERROR
191 }
192
193 fn scan_ident(c: char, ptr: &mut Ptr) -> SyntaxKind {
194     let is_single_letter = match ptr.next() {
195         None => true,
196         Some(c) if !is_ident_continue(c) => true,
197         _ => false,
198     };
199     if is_single_letter {
200         return if c == '_' { UNDERSCORE } else { IDENT };
201     }
202     ptr.bump_while(is_ident_continue);
203     if let Some(kind) = ident_to_keyword(ptr.current_token_text()) {
204         return kind;
205     }
206     IDENT
207 }
208
209 fn scan_literal_suffix(ptr: &mut Ptr) {
210     if ptr.next_is_p(is_ident_start) {
211         ptr.bump();
212     }
213     ptr.bump_while(is_ident_continue);
214 }