]> git.lizzy.rs Git - rust.git/blob - src/grammar/verify.rs
Rollup merge of #28215 - matklad:grammar-extern-block-item, r=steveklabnik
[rust.git] / src / grammar / verify.rs
1 // Copyright 2014 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 #![feature(plugin, rustc_private, str_char, collections)]
12
13 extern crate syntax;
14 extern crate rustc;
15
16 #[macro_use]
17 extern crate log;
18
19 use std::collections::HashMap;
20 use std::env;
21 use std::fs::File;
22 use std::io::{BufRead, Read};
23 use std::path::Path;
24
25 use syntax::parse;
26 use syntax::parse::lexer;
27 use rustc::session::{self, config};
28
29 use syntax::ast;
30 use syntax::ast::Name;
31 use syntax::codemap;
32 use syntax::codemap::Pos;
33 use syntax::parse::token;
34 use syntax::parse::lexer::TokenAndSpan;
35
36 fn parse_token_list(file: &str) -> HashMap<String, token::Token> {
37     fn id() -> token::Token {
38         token::Ident(ast::Ident { name: Name(0), ctxt: 0, }, token::Plain)
39     }
40
41     let mut res = HashMap::new();
42
43     res.insert("-1".to_string(), token::Eof);
44
45     for line in file.split('\n') {
46         let eq = match line.trim().rfind('=') {
47             Some(val) => val,
48             None => continue
49         };
50
51         let val = &line[..eq];
52         let num = &line[eq + 1..];
53
54         let tok = match val {
55             "SHR"               => token::BinOp(token::Shr),
56             "DOLLAR"            => token::Dollar,
57             "LT"                => token::Lt,
58             "STAR"              => token::BinOp(token::Star),
59             "FLOAT_SUFFIX"      => id(),
60             "INT_SUFFIX"        => id(),
61             "SHL"               => token::BinOp(token::Shl),
62             "LBRACE"            => token::OpenDelim(token::Brace),
63             "RARROW"            => token::RArrow,
64             "LIT_STR"           => token::Literal(token::Str_(Name(0)), None),
65             "DOTDOT"            => token::DotDot,
66             "MOD_SEP"           => token::ModSep,
67             "DOTDOTDOT"         => token::DotDotDot,
68             "NOT"               => token::Not,
69             "AND"               => token::BinOp(token::And),
70             "LPAREN"            => token::OpenDelim(token::Paren),
71             "ANDAND"            => token::AndAnd,
72             "AT"                => token::At,
73             "LBRACKET"          => token::OpenDelim(token::Bracket),
74             "LIT_STR_RAW"       => token::Literal(token::StrRaw(Name(0), 0), None),
75             "RPAREN"            => token::CloseDelim(token::Paren),
76             "SLASH"             => token::BinOp(token::Slash),
77             "COMMA"             => token::Comma,
78             "LIFETIME"          => token::Lifetime(ast::Ident { name: Name(0), ctxt: 0 }),
79             "CARET"             => token::BinOp(token::Caret),
80             "TILDE"             => token::Tilde,
81             "IDENT"             => id(),
82             "PLUS"              => token::BinOp(token::Plus),
83             "LIT_CHAR"          => token::Literal(token::Char(Name(0)), None),
84             "LIT_BYTE"          => token::Literal(token::Byte(Name(0)), None),
85             "EQ"                => token::Eq,
86             "RBRACKET"          => token::CloseDelim(token::Bracket),
87             "COMMENT"           => token::Comment,
88             "DOC_COMMENT"       => token::DocComment(Name(0)),
89             "DOT"               => token::Dot,
90             "EQEQ"              => token::EqEq,
91             "NE"                => token::Ne,
92             "GE"                => token::Ge,
93             "PERCENT"           => token::BinOp(token::Percent),
94             "RBRACE"            => token::CloseDelim(token::Brace),
95             "BINOP"             => token::BinOp(token::Plus),
96             "POUND"             => token::Pound,
97             "OROR"              => token::OrOr,
98             "LIT_INTEGER"       => token::Literal(token::Integer(Name(0)), None),
99             "BINOPEQ"           => token::BinOpEq(token::Plus),
100             "LIT_FLOAT"         => token::Literal(token::Float(Name(0)), None),
101             "WHITESPACE"        => token::Whitespace,
102             "UNDERSCORE"        => token::Underscore,
103             "MINUS"             => token::BinOp(token::Minus),
104             "SEMI"              => token::Semi,
105             "COLON"             => token::Colon,
106             "FAT_ARROW"         => token::FatArrow,
107             "OR"                => token::BinOp(token::Or),
108             "GT"                => token::Gt,
109             "LE"                => token::Le,
110             "LIT_BYTE_STR"      => token::Literal(token::ByteStr(Name(0)), None),
111             "LIT_BYTE_STR_RAW"  => token::Literal(token::ByteStrRaw(Name(0), 0), None),
112             "QUESTION"          => token::Question,
113             "SHEBANG"           => token::Shebang(Name(0)),
114             _                   => continue,
115         };
116
117         res.insert(num.to_string(), tok);
118     }
119
120     debug!("Token map: {:?}", res);
121     res
122 }
123
124 fn str_to_binop(s: &str) -> token::BinOpToken {
125     match s {
126         "+"     => token::Plus,
127         "/"     => token::Slash,
128         "-"     => token::Minus,
129         "*"     => token::Star,
130         "%"     => token::Percent,
131         "^"     => token::Caret,
132         "&"     => token::And,
133         "|"     => token::Or,
134         "<<"    => token::Shl,
135         ">>"    => token::Shr,
136         _       => panic!("Bad binop str `{}`", s),
137     }
138 }
139
140 /// Assuming a string/byte string literal, strip out the leading/trailing
141 /// hashes and surrounding quotes/raw/byte prefix.
142 fn fix(mut lit: &str) -> ast::Name {
143     if lit.char_at(0) == 'r' {
144         if lit.char_at(1) == 'b' {
145             lit = &lit[2..]
146         } else {
147             lit = &lit[1..];
148         }
149     } else if lit.char_at(0) == 'b' {
150         lit = &lit[1..];
151     }
152
153     let leading_hashes = count(lit);
154
155     // +1/-1 to adjust for single quotes
156     parse::token::intern(&lit[leading_hashes + 1..lit.len() - leading_hashes - 1])
157 }
158
159 /// Assuming a char/byte literal, strip the 'b' prefix and the single quotes.
160 fn fixchar(mut lit: &str) -> ast::Name {
161     if lit.char_at(0) == 'b' {
162         lit = &lit[1..];
163     }
164
165     parse::token::intern(&lit[1..lit.len() - 1])
166 }
167
168 fn count(lit: &str) -> usize {
169     lit.chars().take_while(|c| *c == '#').count()
170 }
171
172 fn parse_antlr_token(s: &str, tokens: &HashMap<String, token::Token>, surrogate_pairs_pos: &[usize],
173                      has_bom: bool)
174                      -> TokenAndSpan {
175     // old regex:
176     // \[@(?P<seq>\d+),(?P<start>\d+):(?P<end>\d+)='(?P<content>.+?)',<(?P<toknum>-?\d+)>,\d+:\d+]
177     let start = s.find("[@").unwrap();
178     let comma = start + s[start..].find(",").unwrap();
179     let colon = comma + s[comma..].find(":").unwrap();
180     let content_start = colon + s[colon..].find("='").unwrap();
181     // Use rfind instead of find, because we don't want to stop at the content
182     let content_end = content_start + s[content_start..].rfind("',<").unwrap();
183     let toknum_end = content_end + s[content_end..].find(">,").unwrap();
184
185     let start = &s[comma + 1 .. colon];
186     let end = &s[colon + 1 .. content_start];
187     let content = &s[content_start + 2 .. content_end];
188     let toknum = &s[content_end + 3 .. toknum_end];
189
190     let not_found = format!("didn't find token {:?} in the map", toknum);
191     let proto_tok = tokens.get(toknum).expect(&not_found[..]);
192
193     let nm = parse::token::intern(content);
194
195     debug!("What we got: content (`{}`), proto: {:?}", content, proto_tok);
196
197     let real_tok = match *proto_tok {
198         token::BinOp(..)           => token::BinOp(str_to_binop(content)),
199         token::BinOpEq(..)         => token::BinOpEq(str_to_binop(&content[..content.len() - 1])),
200         token::Literal(token::Str_(..), n)      => token::Literal(token::Str_(fix(content)), n),
201         token::Literal(token::StrRaw(..), n)    => token::Literal(token::StrRaw(fix(content),
202                                                                              count(content)), n),
203         token::Literal(token::Char(..), n)      => token::Literal(token::Char(fixchar(content)), n),
204         token::Literal(token::Byte(..), n)      => token::Literal(token::Byte(fixchar(content)), n),
205         token::DocComment(..)      => token::DocComment(nm),
206         token::Literal(token::Integer(..), n)   => token::Literal(token::Integer(nm), n),
207         token::Literal(token::Float(..), n)     => token::Literal(token::Float(nm), n),
208         token::Literal(token::ByteStr(..), n)    => token::Literal(token::ByteStr(nm), n),
209         token::Literal(token::ByteStrRaw(..), n) => token::Literal(token::ByteStrRaw(fix(content),
210                                                                                 count(content)), n),
211         token::Ident(..)           => token::Ident(ast::Ident { name: nm, ctxt: 0 },
212                                                    token::ModName),
213         token::Lifetime(..)        => token::Lifetime(ast::Ident { name: nm, ctxt: 0 }),
214         ref t => t.clone()
215     };
216
217     let start_offset = if real_tok == token::Eof {
218         1
219     } else {
220         0
221     };
222
223     let offset = if has_bom { 1 } else { 0 };
224
225     let mut lo = start.parse::<u32>().unwrap() - start_offset - offset;
226     let mut hi = end.parse::<u32>().unwrap() + 1 - offset;
227
228     // Adjust the span: For each surrogate pair already encountered, subtract one position.
229     lo -= surrogate_pairs_pos.binary_search(&(lo as usize)).unwrap_or_else(|x| x) as u32;
230     hi -= surrogate_pairs_pos.binary_search(&(hi as usize)).unwrap_or_else(|x| x) as u32;
231
232     let sp = codemap::Span {
233         lo: codemap::BytePos(lo),
234         hi: codemap::BytePos(hi),
235         expn_id: codemap::NO_EXPANSION
236     };
237
238     TokenAndSpan {
239         tok: real_tok,
240         sp: sp
241     }
242 }
243
244 fn tok_cmp(a: &token::Token, b: &token::Token) -> bool {
245     match a {
246         &token::Ident(id, _) => match b {
247                 &token::Ident(id2, _) => id == id2,
248                 _ => false
249         },
250         _ => a == b
251     }
252 }
253
254 fn span_cmp(antlr_sp: codemap::Span, rust_sp: codemap::Span, cm: &codemap::CodeMap) -> bool {
255     antlr_sp.expn_id == rust_sp.expn_id &&
256         antlr_sp.lo.to_usize() == cm.bytepos_to_file_charpos(rust_sp.lo).to_usize() &&
257         antlr_sp.hi.to_usize() == cm.bytepos_to_file_charpos(rust_sp.hi).to_usize()
258 }
259
260 fn main() {
261     fn next(r: &mut lexer::StringReader) -> TokenAndSpan {
262         use syntax::parse::lexer::Reader;
263         r.next_token()
264     }
265
266     let mut args = env::args().skip(1);
267     let filename = args.next().unwrap();
268     if filename.find("parse-fail").is_some() {
269         return;
270     }
271
272     // Rust's lexer
273     let mut code = String::new();
274     File::open(&Path::new(&filename)).unwrap().read_to_string(&mut code).unwrap();
275
276     let surrogate_pairs_pos: Vec<usize> = code.chars().enumerate()
277                                                      .filter(|&(_, c)| c as usize > 0xFFFF)
278                                                      .map(|(n, _)| n)
279                                                      .enumerate()
280                                                      .map(|(x, n)| x + n)
281                                                      .collect();
282
283     let has_bom = code.starts_with("\u{feff}");
284
285     debug!("Pairs: {:?}", surrogate_pairs_pos);
286
287     let options = config::basic_options();
288     let session = session::build_session(options, None,
289                                          syntax::diagnostics::registry::Registry::new(&[]));
290     let filemap = session.parse_sess.codemap().new_filemap(String::from("<n/a>"), code);
291     let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap);
292     let cm = session.codemap();
293
294     // ANTLR
295     let mut token_file = File::open(&Path::new(&args.next().unwrap())).unwrap();
296     let mut token_list = String::new();
297     token_file.read_to_string(&mut token_list).unwrap();
298     let token_map = parse_token_list(&token_list[..]);
299
300     let stdin = std::io::stdin();
301     let lock = stdin.lock();
302     let lines = lock.lines();
303     let antlr_tokens = lines.map(|l| parse_antlr_token(l.unwrap().trim(),
304                                                        &token_map,
305                                                        &surrogate_pairs_pos[..],
306                                                        has_bom));
307
308     for antlr_tok in antlr_tokens {
309         let rustc_tok = next(&mut lexer);
310         if rustc_tok.tok == token::Eof && antlr_tok.tok == token::Eof {
311             continue
312         }
313
314         assert!(span_cmp(antlr_tok.sp, rustc_tok.sp, cm), "{:?} and {:?} have different spans",
315                 rustc_tok,
316                 antlr_tok);
317
318         macro_rules! matches {
319             ( $($x:pat),+ ) => (
320                 match rustc_tok.tok {
321                     $($x => match antlr_tok.tok {
322                         $x => {
323                             if !tok_cmp(&rustc_tok.tok, &antlr_tok.tok) {
324                                 // FIXME #15677: needs more robust escaping in
325                                 // antlr
326                                 warn!("Different names for {:?} and {:?}", rustc_tok, antlr_tok);
327                             }
328                         }
329                         _ => panic!("{:?} is not {:?}", antlr_tok, rustc_tok)
330                     },)*
331                     ref c => assert!(c == &antlr_tok.tok, "{:?} is not {:?}", antlr_tok, rustc_tok)
332                 }
333             )
334         }
335
336         matches!(
337             token::Literal(token::Byte(..), _),
338             token::Literal(token::Char(..), _),
339             token::Literal(token::Integer(..), _),
340             token::Literal(token::Float(..), _),
341             token::Literal(token::Str_(..), _),
342             token::Literal(token::StrRaw(..), _),
343             token::Literal(token::ByteStr(..), _),
344             token::Literal(token::ByteStrRaw(..), _),
345             token::Ident(..),
346             token::Lifetime(..),
347             token::Interpolated(..),
348             token::DocComment(..),
349             token::Shebang(..)
350         );
351     }
352 }