1 // Copyright 2012 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.
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.
11 //! The main parser interface
15 use codemap::{Span, CodeMap, FileMap};
16 use diagnostic::{SpanHandler, mk_span_handler, default_handler};
17 use parse::attr::ParserAttr;
18 use parse::parser::Parser;
20 use std::cell::RefCell;
31 /// Common routines shared by parser mods
34 /// Routines the parser uses to classify AST nodes
37 /// Reporting obsolete syntax
40 // info about a parsing session.
41 pub struct ParseSess {
42 pub span_diagnostic: SpanHandler, // better be the same as the one in the reader!
43 /// Used to determine and report recursive mod inclusions
44 included_mod_stack: RefCell<Vec<Path>>,
47 pub fn new_parse_sess() -> ParseSess {
49 span_diagnostic: mk_span_handler(default_handler(), CodeMap::new()),
50 included_mod_stack: RefCell::new(Vec::new()),
54 pub fn new_parse_sess_special_handler(sh: SpanHandler) -> ParseSess {
57 included_mod_stack: RefCell::new(Vec::new()),
61 // a bunch of utility functions of the form parse_<thing>_from_<source>
62 // where <thing> includes crate, expr, item, stmt, tts, and one that
63 // uses a HOF to parse anything, and <source> includes file and
66 pub fn parse_crate_from_file(
68 cfg: ast::CrateConfig,
71 new_parser_from_file(sess, cfg, input).parse_crate_mod()
72 // why is there no p.abort_if_errors here?
75 pub fn parse_crate_attrs_from_file(
77 cfg: ast::CrateConfig,
79 ) -> Vec<ast::Attribute> {
80 let mut parser = new_parser_from_file(sess, cfg, input);
81 let (inner, _) = parser.parse_inner_attrs_and_next();
85 pub fn parse_crate_from_source_str(name: ~str,
87 cfg: ast::CrateConfig,
90 let mut p = new_parser_from_source_str(sess,
94 maybe_aborted(p.parse_crate_mod(),p)
97 pub fn parse_crate_attrs_from_source_str(name: ~str,
99 cfg: ast::CrateConfig,
101 -> Vec<ast::Attribute> {
102 let mut p = new_parser_from_source_str(sess,
106 let (inner, _) = maybe_aborted(p.parse_inner_attrs_and_next(),p);
110 pub fn parse_expr_from_source_str(name: ~str,
112 cfg: ast::CrateConfig,
115 let mut p = new_parser_from_source_str(sess, cfg, name, source);
116 maybe_aborted(p.parse_expr(), p)
119 pub fn parse_item_from_source_str(name: ~str,
121 cfg: ast::CrateConfig,
123 -> Option<@ast::Item> {
124 let mut p = new_parser_from_source_str(sess, cfg, name, source);
125 let attrs = p.parse_outer_attributes();
126 maybe_aborted(p.parse_item(attrs),p)
129 pub fn parse_meta_from_source_str(name: ~str,
131 cfg: ast::CrateConfig,
134 let mut p = new_parser_from_source_str(sess, cfg, name, source);
135 maybe_aborted(p.parse_meta_item(),p)
138 pub fn parse_stmt_from_source_str(name: ~str,
140 cfg: ast::CrateConfig,
141 attrs: Vec<ast::Attribute> ,
144 let mut p = new_parser_from_source_str(
150 maybe_aborted(p.parse_stmt(attrs),p)
153 pub fn parse_tts_from_source_str(name: ~str,
155 cfg: ast::CrateConfig,
157 -> Vec<ast::TokenTree> {
158 let mut p = new_parser_from_source_str(
165 // right now this is re-creating the token trees from ... token trees.
166 maybe_aborted(p.parse_all_token_trees(),p)
169 // Create a new parser from a source string
170 pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess,
171 cfg: ast::CrateConfig,
175 filemap_to_parser(sess, string_to_filemap(sess, source, name), cfg)
178 /// Create a new parser, handling errors as appropriate
179 /// if the file doesn't exist
180 pub fn new_parser_from_file<'a>(sess: &'a ParseSess,
181 cfg: ast::CrateConfig,
182 path: &Path) -> Parser<'a> {
183 filemap_to_parser(sess, file_to_filemap(sess, path, None), cfg)
186 /// Given a session, a crate config, a path, and a span, add
187 /// the file at the given path to the codemap, and return a parser.
188 /// On an error, use the given span as the source of the problem.
189 pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
190 cfg: ast::CrateConfig,
192 sp: Span) -> Parser<'a> {
193 filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg)
196 /// Given a filemap and config, return a parser
197 pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
198 filemap: Rc<FileMap>,
199 cfg: ast::CrateConfig) -> Parser<'a> {
200 tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg)
203 // must preserve old name for now, because quote! from the *existing*
204 // compiler expands into it
205 pub fn new_parser_from_tts<'a>(sess: &'a ParseSess,
206 cfg: ast::CrateConfig,
207 tts: Vec<ast::TokenTree>) -> Parser<'a> {
208 tts_to_parser(sess, tts, cfg)
214 /// Given a session and a path and an optional span (for error reporting),
215 /// add the path to the session's codemap and return the new filemap.
216 pub fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
218 let err = |msg: &str| {
220 Some(sp) => sess.span_diagnostic.span_fatal(sp, msg),
221 None => sess.span_diagnostic.handler().fatal(msg),
224 let bytes = match File::open(path).read_to_end() {
227 err(format!("couldn't read {}: {}", path.display(), e));
231 match str::from_utf8(bytes.as_slice()) {
233 return string_to_filemap(sess, s.to_owned(),
234 path.as_str().unwrap().to_str())
236 None => err(format!("{} is not UTF-8 encoded", path.display())),
241 // given a session and a string, add the string to
242 // the session's codemap and return the new filemap
243 pub fn string_to_filemap(sess: &ParseSess, source: ~str, path: ~str)
245 sess.span_diagnostic.cm.new_filemap(path, source)
248 // given a filemap, produce a sequence of token-trees
249 pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
250 -> Vec<ast::TokenTree> {
251 // it appears to me that the cfg doesn't matter here... indeed,
252 // parsing tt's probably shouldn't require a parser at all.
253 let cfg = Vec::new();
254 let srdr = lexer::new_string_reader(&sess.span_diagnostic, filemap);
255 let mut p1 = Parser(sess, cfg, ~srdr);
256 p1.parse_all_token_trees()
259 // given tts and cfg, produce a parser
260 pub fn tts_to_parser<'a>(sess: &'a ParseSess,
261 tts: Vec<ast::TokenTree>,
262 cfg: ast::CrateConfig) -> Parser<'a> {
263 let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, tts);
264 Parser(sess, cfg, ~trdr)
267 // abort if necessary
268 pub fn maybe_aborted<T>(result: T, mut p: Parser) -> T {
278 use serialize::{json, Encodable};
280 use std::io::MemWriter;
282 use codemap::{Span, BytePos, Spanned};
283 use owned_slice::OwnedSlice;
286 use parse::parser::Parser;
287 use parse::token::{str_to_ident};
288 use util::parser_testing::{string_to_tts, string_to_parser};
289 use util::parser_testing::{string_to_expr, string_to_item};
290 use util::parser_testing::string_to_stmt;
292 fn to_json_str<'a, E: Encodable<json::Encoder<'a>, io::IoError>>(val: &E) -> ~str {
293 let mut writer = MemWriter::new();
294 let mut encoder = json::Encoder::new(&mut writer as &mut io::Writer);
295 let _ = val.encode(&mut encoder);
296 str::from_utf8(writer.unwrap().as_slice()).unwrap().to_owned()
299 // produce a codemap::span
300 fn sp(a: u32, b: u32) -> Span {
301 Span{lo:BytePos(a),hi:BytePos(b),expn_info:None}
304 #[test] fn path_exprs_1() {
305 assert!(string_to_expr(~"a") ==
307 id: ast::DUMMY_NODE_ID,
308 node: ast::ExprPath(ast::Path {
313 identifier: str_to_ident("a"),
314 lifetimes: Vec::new(),
315 types: OwnedSlice::empty(),
323 #[test] fn path_exprs_2 () {
324 assert!(string_to_expr(~"::a::b") ==
326 id: ast::DUMMY_NODE_ID,
327 node: ast::ExprPath(ast::Path {
332 identifier: str_to_ident("a"),
333 lifetimes: Vec::new(),
334 types: OwnedSlice::empty(),
337 identifier: str_to_ident("b"),
338 lifetimes: Vec::new(),
339 types: OwnedSlice::empty(),
348 #[test] fn bad_path_expr_1() {
349 string_to_expr(~"::abc::def::return");
352 // check the token-tree-ization of macros
353 #[test] fn string_to_tts_macro () {
354 let tts = string_to_tts(~"macro_rules! zip (($a)=>($a))");
355 let tts: &[ast::TokenTree] = tts.as_slice();
358 ast::TTTok(_,token::NOT),
360 ast::TTDelim(ref delim_elts)] => {
361 let delim_elts: &[ast::TokenTree] = delim_elts.as_slice();
363 [ast::TTTok(_,token::LPAREN),
364 ast::TTDelim(ref first_set),
365 ast::TTTok(_,token::FAT_ARROW),
366 ast::TTDelim(ref second_set),
367 ast::TTTok(_,token::RPAREN)] => {
368 let first_set: &[ast::TokenTree] =
369 first_set.as_slice();
371 [ast::TTTok(_,token::LPAREN),
372 ast::TTTok(_,token::DOLLAR),
374 ast::TTTok(_,token::RPAREN)] => {
375 let second_set: &[ast::TokenTree] =
376 second_set.as_slice();
378 [ast::TTTok(_,token::LPAREN),
379 ast::TTTok(_,token::DOLLAR),
381 ast::TTTok(_,token::RPAREN)] => {
382 assert_eq!("correct","correct")
384 _ => assert_eq!("wrong 4","correct")
388 error!("failing value 3: {:?}",first_set);
389 assert_eq!("wrong 3","correct")
394 error!("failing value 2: {:?}",delim_elts);
395 assert_eq!("wrong","correct");
400 error!("failing value: {:?}",tts);
401 assert_eq!("wrong 1","correct");
406 #[test] fn string_to_tts_1 () {
407 let tts = string_to_tts(~"fn a (b : int) { b; }");
408 assert_eq!(to_json_str(&tts),
411 \"variant\":\"TTTok\",\
415 \"variant\":\"IDENT\",\
424 \"variant\":\"TTTok\",\
428 \"variant\":\"IDENT\",\
437 \"variant\":\"TTDelim\",\
441 \"variant\":\"TTTok\",\
448 \"variant\":\"TTTok\",\
452 \"variant\":\"IDENT\",\
461 \"variant\":\"TTTok\",\
468 \"variant\":\"TTTok\",\
472 \"variant\":\"IDENT\",\
481 \"variant\":\"TTTok\",\
491 \"variant\":\"TTDelim\",\
495 \"variant\":\"TTTok\",\
502 \"variant\":\"TTTok\",\
506 \"variant\":\"IDENT\",\
515 \"variant\":\"TTTok\",\
522 \"variant\":\"TTTok\",\
535 #[test] fn ret_expr() {
536 assert!(string_to_expr(~"return d") ==
538 id: ast::DUMMY_NODE_ID,
539 node:ast::ExprRet(Some(@ast::Expr{
540 id: ast::DUMMY_NODE_ID,
541 node:ast::ExprPath(ast::Path{
546 identifier: str_to_ident("d"),
547 lifetimes: Vec::new(),
548 types: OwnedSlice::empty(),
558 #[test] fn parse_stmt_1 () {
559 assert!(string_to_stmt(~"b;") ==
561 node: ast::StmtExpr(@ast::Expr {
562 id: ast::DUMMY_NODE_ID,
563 node: ast::ExprPath(ast::Path {
568 identifier: str_to_ident("b"),
569 lifetimes: Vec::new(),
570 types: OwnedSlice::empty(),
580 fn parser_done(p: Parser){
581 assert_eq!(p.token.clone(), token::EOF);
584 #[test] fn parse_ident_pat () {
585 let sess = new_parse_sess();
586 let mut parser = string_to_parser(&sess, ~"b");
587 assert!(parser.parse_pat() ==
588 @ast::Pat{id: ast::DUMMY_NODE_ID,
590 ast::BindByValue(ast::MutImmutable),
596 identifier: str_to_ident("b"),
597 lifetimes: Vec::new(),
598 types: OwnedSlice::empty(),
607 // check the contents of the tt manually:
608 #[test] fn parse_fundecl () {
609 // this test depends on the intern order of "fn" and "int"
610 assert!(string_to_item(~"fn a (b : int) { b; }") ==
612 @ast::Item{ident:str_to_ident("a"),
614 id: ast::DUMMY_NODE_ID,
615 node: ast::ItemFn(ast::P(ast::FnDecl {
616 inputs: vec!(ast::Arg{
617 ty: ast::P(ast::Ty{id: ast::DUMMY_NODE_ID,
618 node: ast::TyPath(ast::Path{
625 lifetimes: Vec::new(),
626 types: OwnedSlice::empty(),
629 }, None, ast::DUMMY_NODE_ID),
633 id: ast::DUMMY_NODE_ID,
635 ast::BindByValue(ast::MutImmutable),
643 lifetimes: Vec::new(),
644 types: OwnedSlice::empty(),
652 id: ast::DUMMY_NODE_ID
654 output: ast::P(ast::Ty{id: ast::DUMMY_NODE_ID,
656 span:sp(15,15)}), // not sure
662 ast::Generics{ // no idea on either of these:
663 lifetimes: Vec::new(),
664 ty_params: OwnedSlice::empty(),
667 view_items: Vec::new(),
668 stmts: vec!(@Spanned{
669 node: ast::StmtSemi(@ast::Expr{
670 id: ast::DUMMY_NODE_ID,
691 id: ast::DUMMY_NODE_ID,
692 rules: ast::DefaultBlock, // no idea
700 #[test] fn parse_exprs () {
701 // just make sure that they parse....
702 string_to_expr(~"3 + 4");
703 string_to_expr(~"a::z.froob(b,@(987+3))");
706 #[test] fn attrs_fix_bug () {
707 string_to_item(~"pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
708 -> Result<@Writer, ~str> {
711 (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
715 fn wb() -> c_int { O_WRONLY as c_int }
717 let mut fflags: c_int = wb();