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
16 use codemap
::{span, CodeMap, FileMap, FileSubstr}
;
18 use diagnostic
::{span_handler, mk_span_handler, mk_handler, Emitter}
;
19 use parse
::attr
::parser_attr
;
20 use parse
::lexer
::reader
;
21 use parse
::parser
::Parser
;
33 /// Common routines shared by parser mods
36 /// Routines the parser uses to classify AST nodes
39 /// Reporting obsolete syntax
42 // info about a parsing session.
43 pub struct ParseSess
{
44 cm
: @codemap
::CodeMap
, // better be the same as the one in the reader!
46 span_diagnostic
: @span_handler
, // better be the same as the one in the reader!
49 pub fn new_parse_sess(demitter
: Option
<Emitter
>) -> @
mut ParseSess
{
50 let cm
= @CodeMap
::new();
54 span_diagnostic
: mk_span_handler(mk_handler(demitter
), cm
),
58 pub fn new_parse_sess_special_handler(sh
: @span_handler
,
59 cm
: @codemap
::CodeMap
)
68 // a bunch of utility functions of the form parse_<thing>_from_<source>
69 // where <thing> includes crate, expr, item, stmt, tts, and one that
70 // uses a HOF to parse anything, and <source> includes file and
73 pub fn parse_crate_from_file(
78 new_parser_from_file(sess
, /*bad*/ copy cfg
, input
).parse_crate_mod()
79 // why is there no p.abort_if_errors here?
82 pub fn parse_crate_from_source_str(
88 let p
= new_parser_from_source_str(
94 maybe_aborted(p
.parse_crate_mod(),p
)
97 pub fn parse_expr_from_source_str(
103 let p
= new_parser_from_source_str(
109 maybe_aborted(p
.parse_expr(), p
)
112 pub fn parse_item_from_source_str(
116 attrs
: ~[ast
::attribute
],
118 ) -> Option
<@ast
::item
> {
119 let p
= new_parser_from_source_str(
125 maybe_aborted(p
.parse_item(attrs
),p
)
128 pub fn parse_meta_from_source_str(
133 ) -> @ast
::meta_item
{
134 let p
= new_parser_from_source_str(
140 maybe_aborted(p
.parse_meta_item(),p
)
143 pub fn parse_stmt_from_source_str(
147 attrs
: ~[ast
::attribute
],
150 let p
= new_parser_from_source_str(
156 maybe_aborted(p
.parse_stmt(attrs
),p
)
159 pub fn parse_tts_from_source_str(
164 ) -> ~[ast
::token_tree
] {
165 let p
= new_parser_from_source_str(
171 *p
.quote_depth
+= 1u;
172 // right now this is re-creating the token trees from ... token trees.
173 maybe_aborted(p
.parse_all_token_trees(),p
)
176 // given a function and parsing information (source str,
177 // filename, crate cfg, and sess), create a parser,
178 // apply the function, and check that the parser
179 // consumed all of the input before returning the function's
181 pub fn parse_from_source_str
<T
>(
182 f
: &fn(&Parser
) -> T
,
183 name
: @
str, ss
: codemap
::FileSubstr
,
188 let p
= new_parser_from_source_substr(
196 if !p
.reader
.is_eof() {
197 p
.reader
.fatal(~"expected end-of-string");
202 // return the next unused node id.
203 pub fn next_node_id(sess
: @
mut ParseSess
) -> node_id
{
204 let rv
= sess
.next_id
;
206 // ID 0 is reserved for the crate and doesn't actually exist in the AST
211 // Create a new parser from a source string
212 pub fn new_parser_from_source_str(sess
: @
mut ParseSess
,
217 filemap_to_parser(sess
,string_to_filemap(sess
,source
,name
),cfg
)
220 // Create a new parser from a source string where the origin
221 // is specified as a substring of another file.
222 pub fn new_parser_from_source_substr(sess
: @
mut ParseSess
,
225 ss
: codemap
::FileSubstr
,
228 filemap_to_parser(sess
,substring_to_filemap(sess
,source
,name
,ss
),cfg
)
231 /// Create a new parser, handling errors as appropriate
232 /// if the file doesn't exist
233 pub fn new_parser_from_file(
234 sess
: @
mut ParseSess
,
238 filemap_to_parser(sess
,file_to_filemap(sess
,path
,None
),cfg
)
241 /// Given a session, a crate config, a path, and a span, add
242 /// the file at the given path to the codemap, and return a parser.
243 /// On an error, use the given span as the source of the problem.
244 pub fn new_sub_parser_from_file(
245 sess
: @
mut ParseSess
,
250 filemap_to_parser(sess
,file_to_filemap(sess
,path
,Some(sp
)),cfg
)
253 /// Given a filemap and config, return a parser
254 pub fn filemap_to_parser(sess
: @
mut ParseSess
,
256 cfg
: ast
::crate_cfg
) -> Parser
{
257 tts_to_parser(sess
,filemap_to_tts(sess
,filemap
),cfg
)
260 // must preserve old name for now, because quote! from the *existing*
261 // compiler expands into it
262 pub fn new_parser_from_tts(sess
: @
mut ParseSess
,
264 tts
: ~[ast
::token_tree
]) -> Parser
{
265 tts_to_parser(sess
,tts
,cfg
)
271 /// Given a session and a path and an optional span (for error reporting),
272 /// add the path to the session's codemap and return the new filemap.
273 pub fn file_to_filemap(sess
: @
mut ParseSess
, path
: &Path
, spanopt
: Option
<span
>)
275 match io
::read_whole_file_str(path
) {
276 Ok(src
) => string_to_filemap(sess
, src
.to_managed(), path
.to_str().to_managed()),
279 Some(span
) => sess
.span_diagnostic
.span_fatal(span
, e
),
280 None
=> sess
.span_diagnostic
.handler().fatal(e
)
286 // given a session and a string, add the string to
287 // the session's codemap and return the new filemap
288 pub fn string_to_filemap(sess
: @
mut ParseSess
, source
: @
str, path
: @
str)
290 sess
.cm
.new_filemap(path
, source
)
293 // given a session and a string and a path and a FileSubStr, add
294 // the string to the CodeMap and return the new FileMap
295 pub fn substring_to_filemap(sess
: @
mut ParseSess
, source
: @
str, path
: @
str,
296 filesubstr
: FileSubstr
) -> @FileMap
{
297 sess
.cm
.new_filemap_w_substr(path
,filesubstr
,source
)
300 // given a filemap, produce a sequence of token-trees
301 pub fn filemap_to_tts(sess
: @
mut ParseSess
, filemap
: @FileMap
)
302 -> ~[ast
::token_tree
] {
303 // it appears to me that the cfg doesn't matter here... indeed,
304 // parsing tt's probably shouldn't require a parser at all.
306 let srdr
= lexer
::new_string_reader(copy sess
.span_diagnostic
, filemap
);
307 let p1
= Parser(sess
, cfg
, srdr
as @reader
);
308 p1
.parse_all_token_trees()
311 // given tts and cfg, produce a parser
312 pub fn tts_to_parser(sess
: @
mut ParseSess
,
313 tts
: ~[ast
::token_tree
],
314 cfg
: ast
::crate_cfg
) -> Parser
{
315 let trdr
= lexer
::new_tt_reader(
316 copy sess
.span_diagnostic
,
320 Parser(sess
, cfg
, trdr
as @reader
)
323 // abort if necessary
324 pub fn maybe_aborted
<T
>(result
: T
, p
: Parser
) -> T
{
334 use extra
::serialize
::Encodable
;
337 use codemap
::{span, BytePos, spanned}
;
341 use parse
::parser
::Parser
;
342 use parse
::token
::{str_to_ident}
;
343 use util
::parser_testing
::{string_to_tts_and_sess, string_to_parser}
;
344 use util
::parser_testing
::{string_to_expr, string_to_item}
;
345 use util
::parser_testing
::{string_to_stmt, strs_to_idents}
;
347 // map a string to tts, return the tt without its parsesess
348 fn string_to_tts_only(source_str
: @
str) -> ~[ast
::token_tree
] {
349 let (tts
,_ps
) = string_to_tts_and_sess(source_str
);
354 #[cfg(test)] fn to_json_str<E : Encodable<extra::json::Encoder>>(val: @E) -> ~str {
355 do io
::with_str_writer
|writer
| {
356 let mut encoder
= extra
::json
::Encoder(writer
);
357 val
.encode(&mut encoder
);
361 // produce a codemap::span
362 fn sp (a
: uint
, b
: uint
) -> span
{
363 span{lo:BytePos(a),hi:BytePos(b),expn_info:None}
366 #[test] fn path_exprs_1 () {
367 assert_eq
!(string_to_expr(@
"a"),
369 node
:ast
::expr_path(@ast
::Path
{span
:sp(0,1),
371 idents
:~[str_to_ident("a")],
377 #[test] fn path_exprs_2 () {
378 assert_eq
!(string_to_expr(@
"::a::b"),
381 @ast
::Path
{span
:sp(0,6),
383 idents
:strs_to_idents(~["a","b"]),
389 // FIXME (#6416): For some reason, this fails and causes a test failure, even though it's
390 // marked as `#[should_fail]`.
392 #[test] fn bad_path_expr_1() {
393 string_to_expr(@"::abc::def::return");
396 #[test] fn string_to_tts_1 () {
397 let (tts
,_ps
) = string_to_tts_and_sess(@
"fn a (b : int) { b; }");
398 assert_eq
!(to_json_str(@tts
),
400 [\"tt_tok\",null,[\"IDENT\",\"fn\",false]],\
401 [\"tt_tok\",null,[\"IDENT\",\"a\",false]],\
405 [\"tt_tok\",null,\"LPAREN\"],\
406 [\"tt_tok\",null,[\"IDENT\",\"b\",false]],\
407 [\"tt_tok\",null,\"COLON\"],\
408 [\"tt_tok\",null,[\"IDENT\",\"int\",false]],\
409 [\"tt_tok\",null,\"RPAREN\"]\
415 [\"tt_tok\",null,\"LBRACE\"],\
416 [\"tt_tok\",null,[\"IDENT\",\"b\",false]],\
417 [\"tt_tok\",null,\"SEMI\"],\
418 [\"tt_tok\",null,\"RBRACE\"]\
425 #[test] fn ret_expr() {
426 assert_eq
!(string_to_expr(@
"return d"),
429 Some(@ast
::expr
{id
:1,
431 @ast
::Path
{span
:sp(7,8),
433 idents
:~[str_to_ident("d")],
441 #[test] fn parse_stmt_1 () {
442 assert_eq
!(string_to_stmt(@
"b;"),
444 node
: ast
::stmt_expr(@ast
::expr
{
446 node
: ast
::expr_path(
450 idents
:~[str_to_ident("b")],
459 fn parser_done(p
: Parser
){
460 assert_eq
!(copy
*p
.token
,token
::EOF
);
463 #[test] fn parse_ident_pat () {
464 let parser
= string_to_parser(@
"b");
465 assert_eq
!(parser
.parse_pat(),
466 @ast
::pat
{id
:1, // fixme
467 node
: ast
::pat_ident(ast
::bind_infer
,
471 idents
:~[str_to_ident("b")],
480 #[test] fn parse_arg () {
481 let parser
= string_to_parser(@
"b : int");
482 assert_eq
!(parser
.parse_arg_general(true),
485 ty
: @ast
::Ty
{id
:3, // fixme
486 node
: ast
::ty_path(@ast
::Path
{
487 span
:sp(4,4), // this is bizarre...
488 // check this in the original parser?
490 idents
:~[str_to_ident("int")],
496 node
: ast
::pat_ident(ast
::bind_infer
,
500 idents
:~[str_to_ident("b")],
510 // check the contents of the tt manually:
511 #[test] fn parse_fundecl () {
512 // this test depends on the intern order of "fn" and "int", and on the
513 // assignment order of the node_ids.
514 assert_eq
!(string_to_item(@
"fn a (b : int) { b; }"),
516 @ast
::item
{ident
:str_to_ident("a"),
519 node
: ast
::item_fn(ast
::fn_decl
{
522 ty
: @ast
::Ty
{id
:3, // fixme
523 node
: ast
::ty_path(@ast
::Path
{
526 idents
:~[str_to_ident("int")],
531 pat
: @ast
::pat
{id
:1, // fixme
532 node
: ast
::pat_ident(
537 idents
:~[str_to_ident("b")],
545 output
: @ast
::Ty
{id
:5, // fixme
547 span
:sp(15,15)}, // not sure
552 ast
::Generics
{ // no idea on either of these:
553 lifetimes
: opt_vec
::Empty
,
554 ty_params
: opt_vec
::Empty
,
561 node
: ast
::stmt_semi(@ast
::expr
{
563 node
: ast
::expr_path(
567 idents
:~[str_to_ident("b")],
575 rules
: ast
::default_blk
// no idea
582 #[test] fn parse_exprs () {
583 // just make sure that they parse....
584 string_to_expr(@
"3 + 4");
585 string_to_expr(@
"a::z.froob(b,@(987+3))");
588 #[test] fn attrs_fix_bug () {
589 string_to_item(@
"pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
590 -> Result<@Writer, ~str> {
593 (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
597 fn wb() -> c_int { O_WRONLY as c_int }
599 let mut fflags: c_int = wb();