]> git.lizzy.rs Git - rust.git/blob - src/grammar/verify.rs
doc: remove incomplete sentence
[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(globs, phase, macro_rules)]
12
13 extern crate syntax;
14 extern crate rustc;
15
16 #[phase(link)]
17 extern crate regex;
18
19 #[phase(link, plugin)]
20 extern crate log;
21
22 #[phase(plugin)] extern crate regex_macros;
23
24 use std::collections::HashMap;
25 use std::io::File;
26
27 use syntax::parse;
28 use syntax::parse::lexer;
29 use rustc::session::{mod, config};
30
31 use syntax::ast;
32 use syntax::ast::Name;
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.slice_to(eq);
52         let num = line.slice_from(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_BINARY"        => token::Literal(token::Binary(Name(0)), None),
111             "LIT_BINARY_RAW"    => token::Literal(token::BinaryRaw(Name(0), 0), None),
112             _                   => continue,
113         };
114
115         res.insert(num.to_string(), tok);
116     }
117
118     debug!("Token map: {}", res);
119     res
120 }
121
122 fn str_to_binop(s: &str) -> token::BinOpToken {
123     match s {
124         "+"     => token::Plus,
125         "/"     => token::Slash,
126         "-"     => token::Minus,
127         "*"     => token::Star,
128         "%"     => token::Percent,
129         "^"     => token::Caret,
130         "&"     => token::And,
131         "|"     => token::Or,
132         "<<"    => token::Shl,
133         ">>"    => token::Shr,
134         _       => panic!("Bad binop str `{}`", s),
135     }
136 }
137
138 /// Assuming a string/binary literal, strip out the leading/trailing
139 /// hashes and surrounding quotes/raw/binary prefix.
140 fn fix(mut lit: &str) -> ast::Name {
141     if lit.char_at(0) == 'r' {
142         if lit.char_at(1) == 'b' {
143             lit = lit.slice_from(2)
144         } else {
145             lit = lit.slice_from(1);
146         }
147     } else if lit.char_at(0) == 'b' {
148         lit = lit.slice_from(1);
149     }
150
151     let leading_hashes = count(lit);
152
153     // +1/-1 to adjust for single quotes
154     parse::token::intern(lit.slice(leading_hashes + 1, lit.len() - leading_hashes - 1))
155 }
156
157 /// Assuming a char/byte literal, strip the 'b' prefix and the single quotes.
158 fn fixchar(mut lit: &str) -> ast::Name {
159     if lit.char_at(0) == 'b' {
160         lit = lit.slice_from(1);
161     }
162
163     parse::token::intern(lit.slice(1, lit.len() - 1))
164 }
165
166 fn count(lit: &str) -> uint {
167     lit.chars().take_while(|c| *c == '#').count()
168 }
169
170 fn parse_antlr_token(s: &str, tokens: &HashMap<String, token::Token>) -> TokenAndSpan {
171     let re = regex!(
172       r"\[@(?P<seq>\d+),(?P<start>\d+):(?P<end>\d+)='(?P<content>.+?)',<(?P<toknum>-?\d+)>,\d+:\d+]"
173     );
174
175     let m = re.captures(s).expect(format!("The regex didn't match {}", s).as_slice());
176     let start = m.name("start").unwrap_or("");
177     let end = m.name("end").unwrap_or("");
178     let toknum = m.name("toknum").unwrap_or("");
179     let content = m.name("content").unwrap_or("");
180
181     let proto_tok = tokens.get(toknum).expect(format!("didn't find token {} in the map",
182                                                               toknum).as_slice());
183
184     let nm = parse::token::intern(content);
185
186     debug!("What we got: content (`{}`), proto: {}", content, proto_tok);
187
188     let real_tok = match *proto_tok {
189         token::BinOp(..)           => token::BinOp(str_to_binop(content)),
190         token::BinOpEq(..)         => token::BinOpEq(str_to_binop(content.slice_to(
191                                                                     content.len() - 1))),
192         token::Literal(token::Str_(..), n)      => token::Literal(token::Str_(fix(content)), n),
193         token::Literal(token::StrRaw(..), n)    => token::Literal(token::StrRaw(fix(content),
194                                                                              count(content)), n),
195         token::Literal(token::Char(..), n)      => token::Literal(token::Char(fixchar(content)), n),
196         token::Literal(token::Byte(..), n)      => token::Literal(token::Byte(fixchar(content)), n),
197         token::DocComment(..)      => token::DocComment(nm),
198         token::Literal(token::Integer(..), n)   => token::Literal(token::Integer(nm), n),
199         token::Literal(token::Float(..), n)     => token::Literal(token::Float(nm), n),
200         token::Literal(token::Binary(..), n)    => token::Literal(token::Binary(nm), n),
201         token::Literal(token::BinaryRaw(..), n) => token::Literal(token::BinaryRaw(fix(content),
202                                                                                 count(content)), n),
203         token::Ident(..)           => token::Ident(ast::Ident { name: nm, ctxt: 0 },
204                                                    token::ModName),
205         token::Lifetime(..)        => token::Lifetime(ast::Ident { name: nm, ctxt: 0 }),
206         ref t => t.clone()
207     };
208
209     let offset = if real_tok == token::Eof
210  {
211         1
212     } else {
213         0
214     };
215
216     let sp = syntax::codemap::Span {
217         lo: syntax::codemap::BytePos(start.parse::<u32>().unwrap() - offset),
218         hi: syntax::codemap::BytePos(end.parse::<u32>().unwrap() + 1),
219         expn_id: syntax::codemap::NO_EXPANSION
220     };
221
222     TokenAndSpan {
223         tok: real_tok,
224         sp: sp
225     }
226 }
227
228 fn tok_cmp(a: &token::Token, b: &token::Token) -> bool {
229     match a {
230         &token::Ident(id, _) => match b {
231                 &token::Ident(id2, _) => id == id2,
232                 _ => false
233         },
234         _ => a == b
235     }
236 }
237
238 fn main() {
239     fn next(r: &mut lexer::StringReader) -> TokenAndSpan {
240         use syntax::parse::lexer::Reader;
241         r.next_token()
242     }
243
244     let args = std::os::args();
245
246     let mut token_file = File::open(&Path::new(args[2].as_slice()));
247     let token_map = parse_token_list(token_file.read_to_string().unwrap().as_slice());
248
249     let mut stdin = std::io::stdin();
250     let mut lock = stdin.lock();
251     let lines = lock.lines();
252     let mut antlr_tokens = lines.map(|l| parse_antlr_token(l.unwrap().as_slice().trim(),
253                                                                    &token_map));
254
255     let code = File::open(&Path::new(args[1].as_slice())).unwrap().read_to_string().unwrap();
256     let options = config::basic_options();
257     let session = session::build_session(options, None,
258                                          syntax::diagnostics::registry::Registry::new(&[]));
259     let filemap = parse::string_to_filemap(&session.parse_sess,
260                                            code,
261                                            String::from_str("<n/a>"));
262     let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap);
263
264     for antlr_tok in antlr_tokens {
265         let rustc_tok = next(&mut lexer);
266         if rustc_tok.tok == token::Eof && antlr_tok.tok == token::Eof {
267             continue
268         }
269
270         assert!(rustc_tok.sp == antlr_tok.sp, "{} and {} have different spans", rustc_tok,
271                 antlr_tok);
272
273         macro_rules! matches (
274             ( $($x:pat),+ ) => (
275                 match rustc_tok.tok {
276                     $($x => match antlr_tok.tok {
277                         $x => {
278                             if !tok_cmp(&rustc_tok.tok, &antlr_tok.tok) {
279                                 // FIXME #15677: needs more robust escaping in
280                                 // antlr
281                                 warn!("Different names for {} and {}", rustc_tok, antlr_tok);
282                             }
283                         }
284                         _ => panic!("{} is not {}", antlr_tok, rustc_tok)
285                     },)*
286                     ref c => assert!(c == &antlr_tok.tok, "{} is not {}", rustc_tok, antlr_tok)
287                 }
288             )
289         );
290
291         matches!(
292             token::Literal(token::Byte(..), _),
293             token::Literal(token::Char(..), _),
294             token::Literal(token::Integer(..), _),
295             token::Literal(token::Float(..), _),
296             token::Literal(token::Str_(..), _),
297             token::Literal(token::StrRaw(..), _),
298             token::Literal(token::Binary(..), _),
299             token::Literal(token::BinaryRaw(..), _),
300             token::Ident(..),
301             token::Lifetime(..),
302             token::Interpolated(..),
303             token::DocComment(..),
304             token::Shebang(..)
305         );
306     }
307 }