]> git.lizzy.rs Git - rust.git/blob - src/grammar/verify.rs
lexer tests: makefile/configure
[rust.git] / src / grammar / verify.rs
1 #![feature(globs, phase, macro_rules)]
2
3 extern crate syntax;
4 extern crate rustc;
5
6 #[phase(link)]
7 extern crate regex;
8
9 #[phase(link, plugin)]
10 extern crate log;
11
12 #[phase(plugin)] extern crate regex_macros;
13
14 use std::collections::HashMap;
15 use std::io::File;
16
17 use syntax::parse;
18 use syntax::parse::lexer;
19 use rustc::driver::{session, config};
20
21 use syntax::ast;
22 use syntax::ast::Name;
23 use syntax::parse::token::*;
24 use syntax::parse::lexer::TokenAndSpan;
25
26 fn parse_token_list(file: &str) -> HashMap<String, Token> {
27     fn id() -> Token {
28         IDENT(ast::Ident { name: Name(0), ctxt: 0, }, false)
29     }
30
31     let mut res = HashMap::new();
32
33     res.insert("-1".to_string(), EOF);
34
35     for line in file.split('\n') {
36         let eq = match line.trim().rfind('=') {
37             Some(val) => val,
38             None => continue
39         };
40
41         let val = line.slice_to(eq);
42         let num = line.slice_from(eq + 1);
43
44         let tok = match val {
45             "SHR" => BINOP(SHR),
46             "DOLLAR" => DOLLAR,
47             "LT" => LT,
48             "STAR" => BINOP(STAR),
49             "FLOAT_SUFFIX" => id(),
50             "INT_SUFFIX" => id(),
51             "SHL" => BINOP(SHL),
52             "LBRACE" => LBRACE,
53             "RARROW" => RARROW,
54             "LIT_STR" => LIT_STR(Name(0)),
55             "DOTDOT" => DOTDOT,
56             "MOD_SEP" => MOD_SEP,
57             "DOTDOTDOT" => DOTDOTDOT,
58             "NOT" => NOT,
59             "AND" => BINOP(AND),
60             "LPAREN" => LPAREN,
61             "ANDAND" => ANDAND,
62             "AT" => AT,
63             "LBRACKET" => LBRACKET,
64             "LIT_STR_RAW" => LIT_STR_RAW(Name(0), 0),
65             "RPAREN" => RPAREN,
66             "SLASH" => BINOP(SLASH),
67             "COMMA" => COMMA,
68             "LIFETIME" => LIFETIME(ast::Ident { name: Name(0), ctxt: 0 }),
69             "CARET" => BINOP(CARET),
70             "TILDE" => TILDE,
71             "IDENT" => id(),
72             "PLUS" => BINOP(PLUS),
73             "LIT_CHAR" => LIT_CHAR(Name(0)),
74             "LIT_BYTE" => LIT_BYTE(Name(0)),
75             "EQ" => EQ,
76             "RBRACKET" => RBRACKET,
77             "COMMENT" => COMMENT,
78             "DOC_COMMENT" => DOC_COMMENT(Name(0)),
79             "DOT" => DOT,
80             "EQEQ" => EQEQ,
81             "NE" => NE,
82             "GE" => GE,
83             "PERCENT" => BINOP(PERCENT),
84             "RBRACE" => RBRACE,
85             "BINOP" => BINOP(PLUS),
86             "POUND" => POUND,
87             "OROR" => OROR,
88             "LIT_INTEGER" => LIT_INTEGER(Name(0)),
89             "BINOPEQ" => BINOPEQ(PLUS),
90             "LIT_FLOAT" => LIT_FLOAT(Name(0)),
91             "WHITESPACE" => WS,
92             "UNDERSCORE" => UNDERSCORE,
93             "MINUS" => BINOP(MINUS),
94             "SEMI" => SEMI,
95             "COLON" => COLON,
96             "FAT_ARROW" => FAT_ARROW,
97             "OR" => BINOP(OR),
98             "GT" => GT,
99             "LE" => LE,
100             "LIT_BINARY" => LIT_BINARY(Name(0)),
101             "LIT_BINARY_RAW" => LIT_BINARY_RAW(Name(0), 0),
102             _ => continue
103         };
104
105         res.insert(num.to_string(), tok);
106     }
107
108     debug!("Token map: {}", res);
109     res
110 }
111
112 fn str_to_binop(s: &str) -> BinOp {
113     match s {
114         "+" => PLUS,
115         "/" => SLASH,
116         "-" => MINUS,
117         "*" => STAR,
118         "%" => PERCENT,
119         "^" => CARET,
120         "&" => AND,
121         "|" => OR,
122         "<<" => SHL,
123         ">>" => SHR,
124         _ => fail!("Bad binop str `{}`", s)
125     }
126 }
127
128 /// Assuming a string/binary literal, strip out the leading/trailing
129 /// hashes and surrounding quotes/raw/binary prefix.
130 fn fix(mut lit: &str) -> ast::Name {
131     if lit.char_at(0) == 'r' {
132         if lit.char_at(1) == 'b' {
133             lit = lit.slice_from(2)
134         } else {
135             lit = lit.slice_from(1);
136         }
137     } else if lit.char_at(0) == 'b' {
138         lit = lit.slice_from(1);
139     }
140
141     let leading_hashes = count(lit);
142
143     // +1/-1 to adjust for single quotes
144     parse::token::intern(lit.slice(leading_hashes + 1, lit.len() - leading_hashes - 1))
145 }
146
147 /// Assuming a char/byte literal, strip the 'b' prefix and the single quotes.
148 fn fixchar(mut lit: &str) -> ast::Name {
149     if lit.char_at(0) == 'b' {
150         lit = lit.slice_from(1);
151     }
152
153     parse::token::intern(lit.slice(1, lit.len() - 1))
154 }
155
156 fn count(lit: &str) -> uint {
157     lit.chars().take_while(|c| *c == '#').count()
158 }
159
160 fn parse_antlr_token(s: &str, tokens: &HashMap<String, Token>) -> TokenAndSpan {
161     let re = regex!(r"\[@(?P<seq>\d+),(?P<start>\d+):(?P<end>\d+)='(?P<content>.+?)',<(?P<toknum>-?\d+)>,\d+:\d+]");
162
163     let m = re.captures(s).expect(format!("The regex didn't match {}", s).as_slice());
164     let start = m.name("start");
165     let end = m.name("end");
166     let toknum = m.name("toknum");
167     let content = m.name("content");
168
169     let proto_tok = tokens.find_equiv(&toknum).expect(format!("didn't find token {} in the map", toknum).as_slice());
170
171     let nm = parse::token::intern(content);
172
173     debug!("What we got: content (`{}`), proto: {}", content, proto_tok);
174
175     let real_tok = match *proto_tok {
176         BINOP(..) => BINOP(str_to_binop(content)),
177         BINOPEQ(..) => BINOPEQ(str_to_binop(content.slice_to(content.len() - 1))),
178         LIT_STR(..) => LIT_STR(fix(content)),
179         LIT_STR_RAW(..) => LIT_STR_RAW(fix(content), count(content)),
180         LIT_CHAR(..) => LIT_CHAR(fixchar(content)),
181         LIT_BYTE(..) => LIT_BYTE(fixchar(content)),
182         DOC_COMMENT(..) => DOC_COMMENT(nm),
183         LIT_INTEGER(..) => LIT_INTEGER(nm),
184         LIT_FLOAT(..) => LIT_FLOAT(nm),
185         LIT_BINARY(..) => LIT_BINARY(nm),
186         LIT_BINARY_RAW(..) => LIT_BINARY_RAW(fix(content), count(content)),
187         IDENT(..) => IDENT(ast::Ident { name: nm, ctxt: 0 }, true),
188         LIFETIME(..) => LIFETIME(ast::Ident { name: nm, ctxt: 0 }),
189         ref t => t.clone()
190     };
191
192     let offset = if real_tok == EOF {
193         1
194     } else {
195         0
196     };
197
198     let sp = syntax::codemap::Span {
199         lo: syntax::codemap::BytePos(from_str::<u32>(start).unwrap() - offset),
200         hi: syntax::codemap::BytePos(from_str::<u32>(end).unwrap() + 1),
201         expn_info: None
202     };
203
204     TokenAndSpan {
205         tok: real_tok,
206         sp: sp
207     }
208 }
209
210 fn tok_cmp(a: &Token, b: &Token) -> bool {
211     match a {
212         &IDENT(id, _) => match b {
213                 &IDENT(id2, _) => id == id2,
214                 _ => false
215         },
216         _ => a == b
217     }
218 }
219
220 fn main() {
221     fn next(r: &mut lexer::StringReader) -> TokenAndSpan {
222         use syntax::parse::lexer::Reader;
223         r.next_token()
224     }
225
226     let args = std::os::args();
227
228     let token_map = parse_token_list(File::open(&Path::new(args.get(2).as_slice())).unwrap().read_to_string().unwrap().as_slice());
229     let mut stdin = std::io::stdin();
230     let mut antlr_tokens = stdin.lines().map(|l| parse_antlr_token(l.unwrap().as_slice().trim(), &token_map));
231
232     let code = File::open(&Path::new(args.get(1).as_slice())).unwrap().read_to_string().unwrap();
233     let options = config::basic_options();
234     let session = session::build_session(options, None,
235                                          syntax::diagnostics::registry::Registry::new([]));
236     let filemap = parse::string_to_filemap(&session.parse_sess,
237                                            code,
238                                            String::from_str("<n/a>"));
239     let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap);
240
241     for antlr_tok in antlr_tokens {
242         let rustc_tok = next(&mut lexer);
243         if rustc_tok.tok == EOF && antlr_tok.tok == EOF {
244             continue
245         }
246
247         assert!(rustc_tok.sp == antlr_tok.sp, "{} and {} have different spans", rustc_tok, antlr_tok);
248
249         macro_rules! matches (
250             ( $($x:pat),+ ) => (
251                 match rustc_tok.tok {
252                     $($x => match antlr_tok.tok {
253                         $x => {
254                             if !tok_cmp(&rustc_tok.tok, &antlr_tok.tok) {
255                                 // FIXME #15677: needs more robust escaping in
256                                 // antlr
257                                 warn!("Different names for {} and {}", rustc_tok, antlr_tok);
258                             }
259                         }
260                         _ => fail!("{} is not {}", antlr_tok, rustc_tok)
261                     },)*
262                     ref c => assert!(c == &antlr_tok.tok, "{} is not {}", rustc_tok, antlr_tok)
263                 }
264             )
265         )
266
267         matches!(LIT_BYTE(..),
268             LIT_CHAR(..),
269             LIT_INTEGER(..),
270             LIT_FLOAT(..),
271             LIT_STR(..),
272             LIT_STR_RAW(..),
273             LIT_BINARY(..),
274             LIT_BINARY_RAW(..),
275             IDENT(..),
276             LIFETIME(..),
277             INTERPOLATED(..),
278             DOC_COMMENT(..),
279             SHEBANG(..)
280         );
281     }
282 }