diff options
| -rw-r--r-- | src/librustc/driver/driver.rs | 2 | ||||
| -rw-r--r-- | src/librustdoc/attr_parser.rs | 2 | ||||
| -rw-r--r-- | src/librustpkg/tests.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ast_util.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/ext/base.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/opt_vec.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/parse/attr.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax/parse/classify.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/parse/comments.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer.rs | 13 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 496 | ||||
| -rw-r--r-- | src/libsyntax/parse/obsolete.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 676 | ||||
| -rw-r--r-- | src/libsyntax/parse/prec.rs | 52 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 134 | ||||
| -rw-r--r-- | src/libsyntax/syntax.rc | 5 | ||||
| -rw-r--r-- | src/test/compile-fail/enums-pats-not-idents.rs | 16 |
17 files changed, 889 insertions, 545 deletions
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index a6c061fee1e..2e64c0c45bf 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -149,7 +149,7 @@ pub fn parse_input(sess: Session, cfg: ast::crate_cfg, input: &input) -> @ast::crate { match *input { file_input(ref file) => { - parse::parse_crate_from_file_using_tts(&(*file), cfg, sess.parse_sess) + parse::parse_crate_from_file(&(*file), cfg, sess.parse_sess) } str_input(ref src) => { // FIXME (#2319): Don't really want to box the source string diff --git a/src/librustdoc/attr_parser.rs b/src/librustdoc/attr_parser.rs index 5b97ccffc21..433809b9db2 100644 --- a/src/librustdoc/attr_parser.rs +++ b/src/librustdoc/attr_parser.rs @@ -79,7 +79,7 @@ mod test { let parse_sess = syntax::parse::new_parse_sess(None); let parser = parse::new_parser_from_source_str( - parse_sess, ~[], ~"-", codemap::FssNone, @source); + parse_sess, ~[], ~"-", @source); parser.parse_outer_attributes() } diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 49cc875355b..bcee2992e5a 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -66,7 +66,7 @@ fn test_make_dir_rwx() { fn test_install_valid() { let ctxt = fake_ctxt(); let temp_pkg_id = fake_pkg(); - let temp_workspace() = mk_temp_workspace(); + let temp_workspace = mk_temp_workspace(); // should have test, bench, lib, and main ctxt.install(&temp_workspace, temp_pkg_id); // Check that all files exist diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index bfacfc38df6..c28d369e7f8 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -355,6 +355,10 @@ pub fn operator_prec(op: ast::binop) -> uint { } } +/// Precedence of the `as` operator, which is a binary operator +/// not appearing in the prior table. +pub static as_prec: uint = 11u; + pub fn dtor_ty() -> @ast::Ty { @ast::Ty {id: 0, node: ty_nil, span: dummy_sp()} } @@ -756,7 +760,6 @@ mod test { assert_eq!(refold_test_sc(3,&t),test_sc); } - // extend a syntax context with a sequence of marks given // in a vector. v[0] will be the outermost mark. fn unfold_marks(mrks:~[Mrk],tail:SyntaxContext,table: &mut SCTable) -> SyntaxContext { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index ecbccedbd9d..2d6d74b5c1e 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -461,9 +461,7 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{ // ugh: can't get this to compile with mut because of the // lack of flow sensitivity. - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] + #[cfg(not(stage0))] fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> { match *self { BaseMapChain (~ref map) => map, diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs index fd1c5a960d1..6cf7bba600e 100644 --- a/src/libsyntax/opt_vec.rs +++ b/src/libsyntax/opt_vec.rs @@ -69,9 +69,7 @@ impl<T> OptVec<T> { } } - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] + #[cfg(not(stage0))] fn get<'a>(&'a self, i: uint) -> &'a T { match *self { Empty => fail!(fmt!("Invalid index %u", i)), diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index f851b9781ab..cc580155d70 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -62,12 +62,14 @@ impl parser_attr for Parser { return attrs; } + // matches attribute = # attribute_naked fn parse_attribute(&self, style: ast::attr_style) -> ast::attribute { let lo = self.span.lo; self.expect(&token::POUND); return self.parse_attribute_naked(style, lo); } + // matches attribute_naked = [ meta_item ] fn parse_attribute_naked(&self, style: ast::attr_style, lo: BytePos) -> ast::attribute { self.expect(&token::LBRACKET); @@ -86,6 +88,7 @@ impl parser_attr for Parser { // is an inner attribute of the containing item or an outer attribute of // the first contained item until we see the semi). + // matches inner_attrs* outer_attr? // you can make the 'next' field an Option, but the result is going to be // more useful as a vector. fn parse_inner_attrs_and_next(&self) -> @@ -134,6 +137,9 @@ impl parser_attr for Parser { (inner_attrs, next_outer_attrs) } + // matches meta_item = IDENT + // | IDENT = lit + // | IDENT meta_seq fn parse_meta_item(&self) -> @ast::meta_item { let lo = self.span.lo; let name = self.id_to_str(self.parse_ident()); @@ -156,6 +162,7 @@ impl parser_attr for Parser { } } + // matches meta_seq = ( COMMASEP(meta_item) ) fn parse_meta_seq(&self) -> ~[@ast::meta_item] { copy self.parse_seq( &token::LPAREN, diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index 840fb891bff..e04914c0f1e 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -15,6 +15,13 @@ use ast; use codemap; +// does this expression require a semicolon to be treated +// as a statement? The negation of this: 'can this expression +// be used as a statement without a semicolon' -- is used +// as an early-bail-out in the parser so that, for instance, +// 'if true {...} else {...} +// |x| 5 ' +// isn't parsed as (if true {...} else {...} | x) | 5 pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool { match e.node { ast::expr_if(*) @@ -40,6 +47,9 @@ pub fn expr_is_simple_block(e: @ast::expr) -> bool { } } +// this statement requires a semicolon after it. +// note that in one case (stmt_semi), we've already +// seen the semicolon, and thus don't need another. pub fn stmt_ends_with_semi(stmt: &ast::stmt) -> bool { return match stmt.node { ast::stmt_decl(d, _) => { diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index 40bfd3d380c..4e29c3dcf18 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -309,6 +309,8 @@ pub struct lit { pos: BytePos } +// it appears this function is called only from pprust... that's +// probably not a good thing. pub fn gather_comments_and_literals(span_diagnostic: @diagnostic::span_handler, path: ~str, diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index fa425cf6285..60d6ce504fd 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -225,20 +225,12 @@ pub fn is_whitespace(c: char) -> bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } -fn may_begin_ident(c: char) -> bool { return is_alpha(c) || c == '_'; } - fn in_range(c: char, lo: char, hi: char) -> bool { return lo <= c && c <= hi } -fn is_alpha(c: char) -> bool { - return in_range(c, 'a', 'z') || in_range(c, 'A', 'Z'); -} - fn is_dec_digit(c: char) -> bool { return in_range(c, '0', '9'); } -fn is_alnum(c: char) -> bool { return is_alpha(c) || is_dec_digit(c); } - fn is_hex_digit(c: char) -> bool { return in_range(c, '0', '9') || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'); @@ -294,6 +286,8 @@ fn consume_any_line_comment(rdr: @mut StringReader) } } else if rdr.curr == '#' { if nextch(rdr) == '!' { + // I guess this is the only way to figure out if + // we're at the beginning of the file... let cmap = @CodeMap::new(); (*cmap).files.push(rdr.filemap); let loc = cmap.lookup_char_pos_adj(rdr.last_pos); @@ -444,8 +438,7 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token { } } let mut is_float = false; - if rdr.curr == '.' && !(is_alpha(nextch(rdr)) || nextch(rdr) == '_' || - nextch(rdr) == '.') { + if rdr.curr == '.' && !(ident_start(nextch(rdr)) || nextch(rdr) == '.') { is_float = true; bump(rdr); let dec_part = scan_digits(rdr, 10u); diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 4d8fdcfe617..d27d788e23a 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -13,7 +13,7 @@ use ast::node_id; use ast; -use codemap::{span, CodeMap}; +use codemap::{span, CodeMap, FileMap, FileSubstr}; use codemap; use diagnostic::{span_handler, mk_span_handler, mk_handler, Emitter}; use parse::attr::parser_attr; @@ -22,7 +22,7 @@ use parse::parser::Parser; use parse::token::{ident_interner, mk_ident_interner}; use core::io; -use core::option::{None, Option}; +use core::option::{None, Option, Some}; use core::path::Path; use core::result::{Err, Ok, Result}; @@ -36,9 +36,6 @@ pub mod attr; /// Common routines shared by parser mods pub mod common; -/// Functions dealing with operator precedence -pub mod prec; - /// Routines the parser uses to classify AST nodes pub mod classify; @@ -82,31 +79,15 @@ pub fn new_parse_sess_special_handler(sh: @span_handler, // uses a HOF to parse anything, and <source> includes file and // source_str. -// this appears to be the main entry point for rust parsing by -// rustc and crate: pub fn parse_crate_from_file( input: &Path, cfg: ast::crate_cfg, sess: @mut ParseSess ) -> @ast::crate { - let p = new_parser_from_file(sess, /*bad*/ copy cfg, input); - p.parse_crate_mod(/*bad*/ copy cfg) - // why is there no p.abort_if_errors here? -} - -pub fn parse_crate_from_file_using_tts( - input: &Path, - cfg: ast::crate_cfg, - sess: @mut ParseSess -) -> @ast::crate { - let p = new_parser_from_file(sess, /*bad*/ copy cfg, input); - let tts = p.parse_all_token_trees(); - new_parser_from_tts(sess,cfg,tts).parse_crate_mod(/*bad*/ copy cfg) + new_parser_from_file(sess, /*bad*/ copy cfg, input).parse_crate_mod() // why is there no p.abort_if_errors here? } - - pub fn parse_crate_from_source_str( name: ~str, source: @~str, @@ -117,10 +98,9 @@ pub fn parse_crate_from_source_str( sess, /*bad*/ copy cfg, /*bad*/ copy name, - codemap::FssNone, source ); - maybe_aborted(p.parse_crate_mod(/*bad*/ copy cfg),p) + maybe_aborted(p.parse_crate_mod(),p) } pub fn parse_expr_from_source_str( @@ -133,7 +113,6 @@ pub fn parse_expr_from_source_str( sess, cfg, /*bad*/ copy name, - codemap::FssNone, source ); maybe_aborted(p.parse_expr(), p) @@ -150,7 +129,6 @@ pub fn parse_item_from_source_str( sess, cfg, /*bad*/ copy name, - codemap::FssNone, source ); maybe_aborted(p.parse_item(attrs),p) @@ -166,7 +144,6 @@ pub fn parse_meta_from_source_str( sess, cfg, /*bad*/ copy name, - codemap::FssNone, source ); maybe_aborted(p.parse_meta_item(),p) @@ -183,7 +160,6 @@ pub fn parse_stmt_from_source_str( sess, cfg, /*bad*/ copy name, - codemap::FssNone, source ); maybe_aborted(p.parse_stmt(attrs),p) @@ -199,13 +175,18 @@ pub fn parse_tts_from_source_str( sess, cfg, /*bad*/ copy name, - codemap::FssNone, source ); *p.quote_depth += 1u; + // right now this is re-creating the token trees from ... token trees. maybe_aborted(p.parse_all_token_trees(),p) } +// given a function and parsing information (source str, +// filename, crate cfg, and sess), create a parser, +// apply the function, and check that the parser +// consumed all of the input before returning the function's +// result. pub fn parse_from_source_str<T>( f: &fn(&Parser) -> T, name: ~str, ss: codemap::FileSubstr, @@ -213,7 +194,7 @@ pub fn parse_from_source_str<T>( cfg: ast::crate_cfg, sess: @mut ParseSess ) -> T { - let p = new_parser_from_source_str( + let p = new_parser_from_source_substr( sess, cfg, name, @@ -227,6 +208,7 @@ pub fn parse_from_source_str<T>( maybe_aborted(r,p) } +// return the next unused node id. pub fn next_node_id(sess: @mut ParseSess) -> node_id { let rv = sess.next_id; sess.next_id += 1; @@ -235,39 +217,24 @@ pub fn next_node_id(sess: @mut ParseSess) -> node_id { return rv; } +// Create a new parser from a source string pub fn new_parser_from_source_str(sess: @mut ParseSess, cfg: ast::crate_cfg, name: ~str, - ss: codemap::FileSubstr, source: @~str) -> Parser { - let filemap = sess.cm.new_filemap_w_substr(name, ss, source); - let srdr = lexer::new_string_reader( - copy sess.span_diagnostic, - filemap, - sess.interner - ); - Parser(sess, cfg, srdr as @reader) + filemap_to_parser(sess,string_to_filemap(sess,source,name),cfg) } -/// Read the entire source file, return a parser -/// that draws from that string -pub fn new_parser_result_from_file( - sess: @mut ParseSess, - cfg: ast::crate_cfg, - path: &Path -) -> Result<Parser, ~str> { - match io::read_whole_file_str(path) { - Ok(src) => { - let filemap = sess.cm.new_filemap(path.to_str(), @src); - let srdr = lexer::new_string_reader(copy sess.span_diagnostic, - filemap, - sess.interner); - Ok(Parser(sess, cfg, srdr as @reader)) - - } - Err(e) => Err(e) - } +// Create a new parser from a source string where the origin +// is specified as a substring of another file. +pub fn new_parser_from_source_substr(sess: @mut ParseSess, + cfg: ast::crate_cfg, + name: ~str, + ss: codemap::FileSubstr, + source: @~str) + -> Parser { + filemap_to_parser(sess,substring_to_filemap(sess,source,name,ss),cfg) } /// Create a new parser, handling errors as appropriate @@ -277,35 +244,85 @@ pub fn new_parser_from_file( cfg: ast::crate_cfg, path: &Path ) -> Parser { - match new_parser_result_from_file(sess, cfg, path) { - Ok(parser) => parser, - Err(e) => { - sess.span_diagnostic.handler().fatal(e) - } - } + filemap_to_parser(sess,file_to_filemap(sess,path,None),cfg) } -/// Create a new parser based on a span from an existing parser. Handles -/// error messages correctly when the file does not exist. +/// Given a session, a crate config, a path, and a span, add +/// the file at the given path to the codemap, and return a parser. +/// On an error, use the given span as the source of the problem. pub fn new_sub_parser_from_file( sess: @mut ParseSess, cfg: ast::crate_cfg, path: &Path, sp: span ) -> Parser { - match new_parser_result_from_file(sess, cfg, path) { - Ok(parser) => parser, + filemap_to_parser(sess,file_to_filemap(sess,path,Some(sp)),cfg) +} + +/// Given a filemap and config, return a parser +pub fn filemap_to_parser(sess: @mut ParseSess, + filemap: @FileMap, + cfg: ast::crate_cfg) -> Parser { + tts_to_parser(sess,filemap_to_tts(sess,filemap),cfg) +} + +// must preserve old name for now, because quote! from the *existing* +// compiler expands into it +pub fn new_parser_from_tts(sess: @mut ParseSess, + cfg: ast::crate_cfg, + tts: ~[ast::token_tree]) -> Parser { + tts_to_parser(sess,tts,cfg) +} + + +// base abstractions + +/// Given a session and a path and an optional span (for error reporting), +/// add the path to the session's codemap and return the new filemap. +pub fn file_to_filemap(sess: @mut ParseSess, path: &Path, spanopt: Option<span>) + -> @FileMap { + match io::read_whole_file_str(path) { + Ok(src) => string_to_filemap(sess, @src, path.to_str()), Err(e) => { - sess.span_diagnostic.span_fatal(sp, e) + match spanopt { + Some(span) => sess.span_diagnostic.span_fatal(span, e), + None => sess.span_diagnostic.handler().fatal(e) + } } } } -pub fn new_parser_from_tts( - sess: @mut ParseSess, - cfg: ast::crate_cfg, - tts: ~[ast::token_tree] -) -> Parser { +// given a session and a string, add the string to +// the session's codemap and return the new filemap +pub fn string_to_filemap(sess: @mut ParseSess, source: @~str, path: ~str) + -> @FileMap { + sess.cm.new_filemap(path, source) +} + +// given a session and a string and a path and a FileSubStr, add +// the string to the CodeMap and return the new FileMap +pub fn substring_to_filemap(sess: @mut ParseSess, source: @~str, path: ~str, + filesubstr: FileSubstr) -> @FileMap { + sess.cm.new_filemap_w_substr(path,filesubstr,source) +} + +// given a filemap, produce a sequence of token-trees +pub fn filemap_to_tts(sess: @mut ParseSess, filemap: @FileMap) + -> ~[ast::token_tree] { + // it appears to me that the cfg doesn't matter here... indeed, + // parsing tt's probably shouldn't require a parser at all. + let cfg = ~[]; + let srdr = lexer::new_string_reader(copy sess.span_diagnostic, + filemap, + sess.interner); + let p1 = Parser(sess, cfg, srdr as @reader); + p1.parse_all_token_trees() +} + +// given tts and cfg, produce a parser +pub fn tts_to_parser(sess: @mut ParseSess, + tts: ~[ast::token_tree], + cfg: ast::crate_cfg) -> Parser { let trdr = lexer::new_tt_reader( copy sess.span_diagnostic, sess.interner, @@ -329,8 +346,77 @@ mod test { use std::serialize::Encodable; use std; use core::io; + use core::option::Option; + use core::option::Some; use core::option::None; + use core::int; + use core::num::NumCast; + use core::path::Path; + use codemap::{dummy_sp, CodeMap, span, BytePos, spanned}; + use opt_vec; use ast; + use abi; + use ast_util::mk_ident; + use parse::parser::Parser; + use parse::token::{ident_interner, mk_ident_interner, mk_fresh_ident_interner}; + use diagnostic::{span_handler, mk_span_handler, mk_handler, Emitter}; + + // add known names to interner for testing + fn mk_testing_interner() -> @ident_interner { + let i = mk_fresh_ident_interner(); + // baby hack; in order to put the identifiers + // 'a' and 'b' at known locations, we're going + // to fill up the interner to length 100. If + // the # of preloaded items on the interner + // ever gets larger than 100, we'll have to + // adjust this number (say, to 200) and + // change the numbers in the identifier + // test cases below. + + assert!(i.len() < 100); + for int::range(0,100-((i.len()).to_int())) |_dc| { + i.gensym(@~"dontcare"); + } + i.intern(@~"a"); + i.intern(@~"b"); + i.intern(@~"c"); + i.intern(@~"d"); + i.intern(@~"return"); + assert!(i.get(ast::ident{repr:101,ctxt:0}) == @~"b"); + i + } + + // make a parse_sess that's closed over a + // testing interner (where a -> 100, b -> 101) + fn mk_testing_parse_sess() -> @mut ParseSess { + let interner = mk_testing_interner(); + let cm = @CodeMap::new(); + @mut ParseSess { + cm: cm, + next_id: 1, + span_diagnostic: mk_span_handler(mk_handler(None), cm), + interner: interner, + } + } + + // map a string to tts, using a made-up filename: return both the token_trees + // and the ParseSess + fn string_to_tts_t (source_str : @~str) -> (~[ast::token_tree],@mut ParseSess) { + let ps = mk_testing_parse_sess(); + (filemap_to_tts(ps,string_to_filemap(ps,source_str,~"bogofile")),ps) + } + + // map a string to tts, return the tt without its parsesess + fn string_to_tts_only(source_str : @~str) -> ~[ast::token_tree] { + let (tts,ps) = string_to_tts_t(source_str); + tts + } + + // map string to parser (via tts) + fn string_to_parser(source_str: @~str) -> Parser { + let ps = mk_testing_parse_sess(); + new_parser_from_source_str(ps,~[],~"bogofile",source_str) + } #[test] fn to_json_str<E : Encodable<std::json::Encoder>>(val: @E) -> ~str { do io::with_str_writer |writer| { @@ -339,49 +425,71 @@ mod test { } fn string_to_crate (source_str : @~str) -> @ast::crate { - parse_crate_from_source_str( - ~"bogofile", - source_str, - ~[], - new_parse_sess(None)) + string_to_parser(source_str).parse_crate_mod() } - fn string_to_tt_to_crate (source_str : @~str) -> @ast::crate { - let tts = parse_tts_from_source_str( - ~"bogofile", - source_str, - ~[], - new_parse_sess(None)); - new_parser_from_tts(new_parse_sess(None),~[],tts) - .parse_crate_mod(~[]) + fn string_to_expr (source_str : @~str) -> @ast::expr { + string_to_parser(source_str).parse_expr() } - // make sure that parsing from TTs produces the same result - // as parsing from strings - #[test] fn tts_produce_the_same_result () { - let source_str = @~"fn foo (x : int) { x; }"; - assert_eq!(string_to_tt_to_crate(source_str), - string_to_crate(source_str)); + fn string_to_item (source_str : @~str) -> Option<@ast::item> { + string_to_parser(source_str).parse_item(~[]) } - // check the contents of the tt manually: - #[test] fn alltts () { - let source_str = @~"fn foo (x : int) { x; }"; - let tts = parse_tts_from_source_str( - ~"bogofile", - source_str, - ~[], - new_parse_sess(None)); - assert_eq!( - to_json_str(@tts), - ~"[\ + fn string_to_stmt (source_str : @~str) -> @ast::stmt { + string_to_parser(source_str).parse_stmt(~[]) + } + + // produce a codemap::span + fn sp (a: uint, b: uint) -> span { + span{lo:BytePos(a),hi:BytePos(b),expn_info:None} + } + + // convert a vector of uints to a vector of ast::idents + fn ints_to_idents(ids: ~[uint]) -> ~[ast::ident] { + ids.map(|u| mk_ident(*u)) + } + + #[test] fn path_exprs_1 () { + assert_eq!(string_to_expr(@~"a"), + @ast::expr{id:1, + callee_id:2, + node:ast::expr_path(@ast::Path {span:sp(0,1), + global:false, + idents:~[mk_ident(100)], + rp:None, + types:~[]}), + span:sp(0,1)}) + } + + #[test] fn path_exprs_2 () { + assert_eq!(string_to_expr(@~"::a::b"), + @ast::expr{id:1, + callee_id:2, + node:ast::expr_path(@ast::Path {span:sp(0,6), + global:true, + idents:ints_to_idents(~[100,101]), + rp:None, + types:~[]}), + span:sp(0,6)}) + } + + #[should_fail] + #[test] fn bad_path_expr_1() { + string_to_expr(@~"::abc::def::return"); + } + + #[test] fn string_to_tts_1 () { + let (tts,ps) = string_to_tts_t(@~"fn a (b : int) { b; }"); + assert_eq!(to_json_str(@tts), + ~"[\ [\"tt_tok\",null,[\"IDENT\",\"fn\",false]],\ - [\"tt_tok\",null,[\"IDENT\",\"foo\",false]],\ + [\"tt_tok\",null,[\"IDENT\",\"a\",false]],\ [\ \"tt_delim\",\ [\ [\"tt_tok\",null,\"LPAREN\"],\ - [\"tt_tok\",null,[\"IDENT\",\"x\",false]],\ + [\"tt_tok\",null,[\"IDENT\",\"b\",false]],\ [\"tt_tok\",null,\"COLON\"],\ [\"tt_tok\",null,[\"IDENT\",\"int\",false]],\ [\"tt_tok\",null,\"RPAREN\"]\ @@ -391,21 +499,181 @@ mod test { \"tt_delim\",\ [\ [\"tt_tok\",null,\"LBRACE\"],\ - [\"tt_tok\",null,[\"IDENT\",\"x\",false]],\ + [\"tt_tok\",null,[\"IDENT\",\"b\",false]],\ [\"tt_tok\",null,\"SEMI\"],\ [\"tt_tok\",null,\"RBRACE\"]\ ]\ ]\ ]" - ); - let ast1 = new_parser_from_tts(new_parse_sess(None),~[],tts) - .parse_item(~[]); - let ast2 = parse_item_from_source_str( - ~"bogofile", - @~"fn foo (x : int) { x; }", - ~[],~[], - new_parse_sess(None)); - assert_eq!(ast1,ast2); + ); + } + + #[test] fn ret_expr() { + assert_eq!(string_to_expr(@~"return d"), + @ast::expr{id:3, + callee_id:4, + node:ast::expr_ret( + Some(@ast::expr{id:1,callee_id:2, + node:ast::expr_path( + @ast::Path{span:sp(7,8), + global:false, + idents:~[mk_ident(103)], + rp:None, + types:~[] + }), + span:sp(7,8)})), + span:sp(0,8)}) + } + + #[test] fn parse_stmt_1 () { + assert_eq!(string_to_stmt(@~"b;"), + @spanned{ + node: ast::stmt_expr(@ast::expr{ + id: 1, + callee_id: 2, + node: ast::expr_path( + @ast::Path{ + span:sp(0,1), + global:false, + idents:~[mk_ident(101)], + rp:None, + types: ~[]}), + span: sp(0,1)}, + 3), // fixme + span: sp(0,1)}) + + } + + fn parser_done(p: Parser){ + assert_eq!(*p.token,token::EOF); + } + + #[test] fn parse_ident_pat () { + let parser = string_to_parser(@~"b"); + assert_eq!(parser.parse_pat(false), + @ast::pat{id:1, // fixme + node: ast::pat_ident(ast::bind_by_copy, + @ast::Path{ + span:sp(0,1), + global:false, + idents:~[mk_ident(101)], + rp: None, + types: ~[]}, + None // no idea + ), + span: sp(0,1)}); + parser_done(parser); + } + + #[test] fn parse_arg () { + let parser = string_to_parser(@~"b : int"); + assert_eq!(parser.parse_arg_general(true), + ast::arg{ + mode: ast::infer(1), + is_mutbl: false, + ty: @ast::Ty{id:4, // fixme + node: ast::ty_path(@ast::Path{ + span:sp(4,4), // this is bizarre... + // check this in the original parser? + global:false, + idents:~[mk_ident(105)], + rp: None, + types: ~[]}, + 3), + span:sp(4,7)}, + pat: @ast::pat{id:2, + node: ast::pat_ident(ast::bind_by_copy, + @ast::Path{ + span:sp(0,1), + global:false, + idents:~[mk_ident(101)], + rp: None, + types: ~[]}, + None // no idea + ), + span: sp(0,3)}, // really? + id: 5 // fixme + }) + } + + // check the contents of the tt manually: + #[test] fn parse_fundecl () { + // this test depends on the intern order of "fn" and "int", and on the + // assignment order of the node_ids. + assert_eq!(string_to_item(@~"fn a (b : int) { b; }"), + Some( + @ast::item{ident:mk_ident(100), + attrs:~[], + id: 11, // fixme + node: ast::item_fn(ast::fn_decl{ + inputs: ~[ast::arg{ + mode: ast::infer(1), + is_mutbl: false, + ty: @ast::Ty{id:4, // fixme + node: ast::ty_path(@ast::Path{ + span:sp(10,13), + global:false, + idents:~[mk_ident(106)], + rp: None, + types: ~[]}, + 3), + span:sp(10,13)}, + pat: @ast::pat{id:2, // fixme + node: ast::pat_ident( + ast::bind_by_copy, + @ast::Path{ + span:sp(6,7), + global:false, + idents:~[mk_ident(101)], + rp: None, + types: ~[]}, + None // no idea + ), + span: sp(6,9)}, // bleah. + id: 5 // fixme + }], + output: @ast::Ty{id:6, // fixme + node: ast::ty_nil, + span:sp(15,15)}, // not sure + cf: ast::return_val + }, + ast::impure_fn, + abi::AbiSet::Rust(), + ast::Generics{ // no idea on either of these: + lifetimes: opt_vec::Empty, + ty_params: opt_vec::Empty, + }, + spanned{ + span: sp(15,21), + node: ast::blk_{ + view_items: ~[], + stmts: ~[@spanned{ + node: ast::stmt_semi(@ast::expr{ + id: 7, + callee_id: 8, + node: ast::expr_path( + @ast::Path{ + span:sp(17,18), + global:false, + idents:~[mk_ident(101)], + rp:None, + types: ~[]}), + span: sp(17,18)}, + 9), // fixme + span: sp(17,18)}], + expr: None, + id: 10, // fixme + rules: ast::default_blk // no idea + }}), + vis: ast::inherited, + span: sp(0,21)})); + } + + + #[test] fn parse_exprs () { + // just make sure that they parse.... + string_to_expr(@~"3 + 4"); + string_to_expr(@~"a::z.froob(b,@(987+3))"); } } diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index e6e98a35d6e..ce21e0f672d 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -259,7 +259,7 @@ pub impl Parser { fn try_parse_obsolete_struct_ctor(&self) -> bool { if self.eat_obsolete_ident("new") { self.obsolete(*self.last_span, ObsoleteStructCtor); - self.parse_fn_decl(|p| p.parse_arg()); + self.parse_fn_decl(); self.parse_block(); true } else { @@ -288,7 +288,7 @@ pub impl Parser { self.eat_keyword(&~"priv"); self.bump(); while *self.token != token::RBRACE { - self.parse_single_class_item(ast::private); + self.parse_single_struct_field(ast::private); } self.bump(); true diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c35b0e2e8c2..30275436c06 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -58,7 +58,7 @@ use ast::{view_item_, view_item_extern_mod, view_item_use}; use ast::{view_path, view_path_glob, view_path_list, view_path_simple}; use ast::visibility; use ast; -use ast_util::{ident_to_path, operator_prec}; +use ast_util::{as_prec, ident_to_path, operator_prec}; use ast_util; use codemap::{span, BytePos, spanned, mk_sp}; use codemap; @@ -82,9 +82,8 @@ use parse::obsolete::ObsoleteMode; use parse::obsolete::{ObsoleteLifetimeNotation, ObsoleteConstManagedPointer}; use parse::obsolete::{ObsoletePurity, ObsoleteStaticMethod}; use parse::obsolete::{ObsoleteConstItem, ObsoleteFixedLengthVectorType}; -use parse::prec::{as_prec, token_to_binop}; use parse::token::{can_begin_expr, is_ident, is_ident_or_path}; -use parse::token::{is_plain_ident, INTERPOLATED, special_idents}; +use parse::token::{is_plain_ident, INTERPOLATED, special_idents, token_to_binop}; use parse::token; use parse::{new_sub_parser_from_file, next_node_id, ParseSess}; use opt_vec; @@ -99,7 +98,6 @@ use core::vec; enum restriction { UNRESTRICTED, RESTRICT_STMT_EXPR, - RESTRICT_NO_CALL_EXPRS, RESTRICT_NO_BAR_OP, RESTRICT_NO_BAR_OR_DOUBLEBAR_OP, } @@ -369,7 +367,7 @@ pub impl Parser { let opt_abis = self.parse_opt_abis(); let abis = opt_abis.get_or_default(AbiSet::Rust()); - let purity = self.parse_purity(); + let purity = self.parse_unsafety(); self.expect_keyword(&~"fn"); let (decl, lifetimes) = self.parse_ty_fn_decl(); return ty_bare_fn(@TyBareFn { @@ -403,7 +401,7 @@ pub impl Parser { // At this point, the allocation type and lifetime bound have been // parsed. - let purity = self.parse_purity(); + let purity = self.parse_unsafety(); let onceness = parse_onceness(self); self.expect_keyword(&~"fn"); @@ -428,7 +426,8 @@ pub impl Parser { } } - fn parse_purity(&self) -> purity { + // looks like this should be called parse_unsafety + fn parse_unsafety(&self) -> purity { if self.eat_keyword(&~"pure") { self.obsolete(*self.last_span, ObsoletePurity); return impure_fn; @@ -474,6 +473,7 @@ pub impl Parser { (decl, lifetimes) } + // parse the methods in a trait declaration fn parse_trait_methods(&self) -> ~[trait_method] { do self.parse_unspanned_seq( &token::LBRACE, @@ -521,7 +521,7 @@ pub impl Parser { token::LBRACE => { debug!("parse_trait_methods(): parsing provided method"); let (inner_attrs, body) = - p.parse_inner_attrs_and_block(true); + p.parse_inner_attrs_and_block(); let attrs = vec::append(attrs, inner_attrs); provided(@ast::method { ident: ident, @@ -559,6 +559,7 @@ pub impl Parser { } // parse [mut/const/imm] ID : TY + // now used only by obsolete record syntax parser... fn parse_ty_field(&self) -> ty_field { let lo = self.span.lo; let mutbl = self.parse_mutability(); @@ -763,6 +764,7 @@ pub impl Parser { return ty_rptr(opt_lifetime, mt); } + // parse an optional mode. fn parse_arg_mode(&self) -> mode { if self.eat(&token::BINOP(token::MINUS)) { self.obsolete(*self.span, ObsoleteMode); @@ -829,10 +831,12 @@ pub impl Parser { ty: t, pat: pat, id: self.get_id() } } + // parse a single function argument fn parse_arg(&self) -> arg_or_capture_item { either::Left(self.parse_arg_general(true)) } + // parse an argument in a lambda header e.g. |arg, arg| fn parse_fn_block_arg(&self) -> arg_or_capture_item { let m = self.parse_arg_mode(); let is_mutbl = self.eat_keyword(&~"mut"); @@ -869,6 +873,7 @@ pub impl Parser { } } + // matches token_lit = LIT_INT | ... fn lit_from_token(&self, tok: &token::Token) -> lit_ { match *tok { token::LIT_INT(i, it) => lit_int(i, it), @@ -883,6 +888,7 @@ pub impl Parser { } } + // matches lit = true | false | token_lit fn parse_lit(&self) -> lit { let lo = self.span.lo; let lit = if self.eat_keyword(&~"true") { @@ -898,30 +904,45 @@ pub impl Parser { codemap::spanned { node: lit, span: mk_sp(lo, self.last_span.hi) } } - // parse a path that doesn't have type parameters attached - fn parse_path_without_tps(&self) - -> @ast::Path { - maybe_whole!(self, nt_path); + // parse a path into a vector of idents, whether the path starts + // with ::, and a span. + fn parse_path(&self) -> (~[ast::ident],bool,span) { + let lo = self.span.lo; + let is_global = self.eat(&token::MOD_SEP); + let (ids,span{lo:_,hi,expn_info}) = self.parse_path_non_global(); + (ids,is_global,span{lo:lo,hi:hi,expn_info:expn_info}) + } + + // parse a path beginning with an identifier into a vector of idents and a span + fn parse_path_non_global(&self) -> (~[ast::ident],span) { let lo = self.span.lo; - let global = self.eat(&token::MOD_SEP); let mut ids = ~[]; + // must be at least one to begin: + ids.push(self.parse_ident()); loop { - // if there's a ::< coming, stop processing - // the path. - let is_not_last = - self.look_ahead(2u) != token::LT - && self.look_ahead(1u) == token::MOD_SEP; - - if is_not_last { - ids.push(self.parse_ident()); - self.expect(&token::MOD_SEP); - } else { - ids.push(self.parse_ident()); - break; + match *self.token { + token::MOD_SEP => { + match self.look_ahead(1u) { + token::IDENT(id,_) => { + self.bump(); + ids.push(self.parse_ident()); + } + _ => break + } + } + _ => break } } - @ast::Path { span: mk_sp(lo, self.last_span.hi), - global: global, + (ids, mk_sp(lo, self.last_span.hi)) + } + + // parse a path that doesn't have type parameters attached + fn parse_path_without_tps(&self) + -> @ast::Path { + maybe_whole!(self, nt_path); + let (ids,is_global,sp) = self.parse_path(); + @ast::Path { span: sp, + global: is_global, idents: ids, rp: None, types: ~[] } @@ -1054,6 +1075,9 @@ pub impl Parser { } } + // matches lifetimes = ( lifetime ) | ( lifetime , lifetimes ) + // actually, it matches the empty one too, but putting that in there + // messes up the grammar.... fn parse_lifetimes(&self) -> OptVec<ast::Lifetime> { /*! * @@ -1079,7 +1103,8 @@ pub impl Parser { token::GT => { return res; } token::BINOP(token::SHR) => { return res; } _ => { - self.fatal(~"expected `,` or `>` after lifetime name"); + self.fatal(fmt!("expected `,` or `>` after lifetime name, got: %?", + *self.token)); } } } @@ -1101,11 +1126,12 @@ pub impl Parser { } } - fn parse_field(&self, sep: token::Token) -> field { + // parse ident COLON expr + fn parse_field(&self) -> field { let lo = self.span.lo; let m = self.parse_mutability(); let i = self.parse_ident(); - self.expect(&sep); + self.expect(&token::COLON); let e = self.parse_expr(); spanned(lo, e.span.hi, ast::field_ { mutbl: m, ident: i, expr: e }) } @@ -1158,7 +1184,7 @@ pub impl Parser { self.bump(); // (e) is parenthesized e // (e,) is a tuple with only one field, e - let mut one_tuple = false; + let mut trailing_comma = false; if *self.token == token::RPAREN { hi = self.span.hi; self.bump(); @@ -1172,13 +1198,13 @@ pub impl Parser { es.push(self.parse_expr()); } else { - one_tuple = true; + trailing_comma = true; } } hi = self.span.hi; self.expect(&token::RPAREN); - return if es.len() == 1 && !one_tuple { + return if es.len() == 1 && !trailing_comma { self.mk_expr(lo, self.span.hi, expr_paren(es[0])) } else { @@ -1246,6 +1272,7 @@ pub impl Parser { } hi = self.span.hi; } else if self.eat_keyword(&~"__log") { + // LOG expression self.expect(&token::LPAREN); let lvl = self.parse_expr(); self.expect(&token::COMMA); @@ -1254,12 +1281,14 @@ pub impl Parser { hi = self.span.hi; self.expect(&token::RPAREN); } else if self.eat_keyword(&~"return") { + // RETURN expression if can_begin_expr(&*self.token) { let e = self.parse_expr(); hi = e.span.hi; ex = expr_ret(Some(e)); } else { ex = expr_ret(None); } } else if self.eat_keyword(&~"break") { + // BREAK expression if is_ident(&*self.token) { ex = expr_break(Some(self.parse_ident())); } else { @@ -1267,6 +1296,7 @@ pub impl Parser { } hi = self.span.hi; } else if self.eat_keyword(&~"copy") { + // COPY expression let e = self.parse_expr(); ex = expr_copy(e); hi = e.span.hi; @@ -1277,6 +1307,7 @@ pub impl Parser { // `!`, as an operator, is prefix, so we know this isn't that if *self.token == token::NOT { + // MACRO INVOCATION expression self.bump(); match *self.token { token::LPAREN | token::LBRACE => {} @@ -1301,7 +1332,7 @@ pub impl Parser { let mut fields = ~[]; let mut base = None; - fields.push(self.parse_field(token::COLON)); + fields.push(self.parse_field()); while *self.token != token::RBRACE { if self.try_parse_obsolete_with() { break; @@ -1318,7 +1349,7 @@ pub impl Parser { // Accept an optional trailing comma. break; } - fields.push(self.parse_field(token::COLON)); + fields.push(self.parse_field()); } hi = pth.span.hi; @@ -1331,6 +1362,7 @@ pub impl Parser { hi = pth.span.hi; ex = expr_path(pth); } else { + // other literal expression let lit = self.parse_lit(); hi = lit.span.hi; ex = expr_lit(@lit); @@ -1339,6 +1371,7 @@ pub impl Parser { return self.mk_expr(lo, hi, ex); } + // parse a block or unsafe block fn parse_block_expr( &self, lo: BytePos, @@ -1349,16 +1382,12 @@ pub impl Parser { return self.mk_expr(blk.span.lo, blk.span.hi, expr_block(blk)); } - // parse a.b or a(13) or just a + // parse a.b or a(13) or a[4] or just a fn parse_dot_or_call_expr(&self) -> @expr { let b = self.parse_bottom_expr(); self.parse_dot_or_call_expr_with(b) } - fn permits_call(&self) -> bool { - return *self.restriction != RESTRICT_NO_CALL_EXPRS; - } - fn parse_dot_or_call_expr_with(&self, e0: @expr) -> @expr { let mut e = e0; let lo = e.span.lo; @@ -1379,7 +1408,7 @@ pub impl Parser { // expr.f() method call match *self.token { - token::LPAREN if self.permits_call() => { + token::LPAREN => { let es = self.parse_unspanned_seq( &token::LPAREN, &token::RPAREN, @@ -1403,7 +1432,7 @@ pub impl Parser { if self.expr_is_complete(e) { break; } match *self.token { // expr(...) - token::LPAREN if self.permits_call() => { + token::LPAREN => { let es = self.parse_unspanned_seq( &token::LPAREN, &token::RPAREN, @@ -1771,10 +1800,10 @@ pub impl Parser { let lo = self.span.lo; let lhs = self.parse_binops(); match *self.token { - token::EQ => { - self.bump(); - let rhs = self.parse_expr(); - self.mk_expr(lo, rhs.span.hi, expr_assign(lhs, rhs)) + token::EQ => { + self.bump(); + let rhs = self.parse_expr(); + self.mk_expr(lo, rhs.span.hi, expr_assign(lhs, rhs)) } token::BINOPEQ(op) => { self.bump(); @@ -1815,6 +1844,7 @@ pub impl Parser { } } + // parse an 'if' expression ('if' token already eaten) fn parse_if_expr(&self) -> @expr { let lo = self.last_span.lo; let cond = self.parse_expr(); @@ -1829,7 +1859,7 @@ pub impl Parser { self.mk_expr(lo, hi, expr_if(cond, thn, els)) } - // `|args| { ... }` like in `do` expressions + // `|args| { ... }` or `{ ...}` like in `do` expressions fn parse_lambda_block_expr(&self) -> @expr { self.parse_lambda_expr_( || { @@ -1863,6 +1893,9 @@ pub impl Parser { || self.parse_expr()) } + // parse something of the form |args| expr + // this is used both in parsing a lambda expr + // and in parsing a block expr as e.g. in for... fn parse_lambda_expr_( &self, parse_decl: &fn() -> fn_decl, @@ -1893,6 +1926,9 @@ pub impl Parser { } } + // parse a 'for' or 'do'. + // the 'for' and 'do' expressions parse as calls, but look like + // function calls followed by a closure expression. fn parse_sugary_call_expr(&self, keyword: ~str, sugar: CallSugar, ctor: &fn(v: @expr) -> expr_) -> @expr { @@ -1953,7 +1989,7 @@ pub impl Parser { fn parse_while_expr(&self) -> @expr { let lo = self.last_span.lo; let cond = self.parse_expr(); - let body = self.parse_block_no_value(); + let body = self.parse_block(); let hi = body.span.hi; return self.mk_expr(lo, hi, expr_while(cond, body)); } @@ -1981,7 +2017,7 @@ pub impl Parser { } let lo = self.last_span.lo; - let body = self.parse_block_no_value(); + let body = self.parse_block(); let hi = body.span.hi; return self.mk_expr(lo, hi, expr_loop(body, opt_ident)); } else { @@ -2079,6 +2115,7 @@ pub impl Parser { } } + // parse patterns, separated by '|' s fn parse_pats(&self) -> ~[@pat] { let mut pats = ~[]; loop { @@ -2133,6 +2170,7 @@ pub impl Parser { (before, slice, after) } + // parse the fields of a struct-like pattern fn parse_pat_fields(&self, refutable: bool) -> (~[ast::field_pat], bool) { let mut fields = ~[]; let mut etc = false; @@ -2176,6 +2214,9 @@ pub impl Parser { return (fields, etc); } + // parse a pattern. The 'refutable' argument + // appears to control whether the binding_mode + // 'bind_infer' or 'bind_by_copy' is used. fn parse_pat(&self, refutable: bool) -> @pat { maybe_whole!(self, nt_pat); @@ -2183,7 +2224,9 @@ pub impl Parser { let mut hi = self.span.hi; let pat; match *self.token { + // parse _ token::UNDERSCORE => { self.bump(); pat = pat_wild; } + // parse @pat token::AT => { self.bump(); let sub = self.parse_pat(refutable); @@ -2207,6 +2250,7 @@ pub impl Parser { }; } token::TILDE => { + // parse ~pat self.bump(); let sub = self.parse_pat(refutable); hi = sub.span.hi; @@ -2229,6 +2273,7 @@ pub impl Parser { }; } token::BINOP(token::AND) => { + // parse &pat let lo = self.span.lo; self.bump(); let sub = self.parse_pat(refutable); @@ -2259,6 +2304,7 @@ pub impl Parser { pat = pat_wild; } token::LPAREN => { + // parse (pat,pat,pat,...) as tuple self.bump(); if *self.token == token::RPAREN { hi = self.span.hi; @@ -2283,6 +2329,7 @@ pub impl Parser { } } token::LBRACKET => { + // parse [pat,pat,...] as vector pattern self.bump(); let (before, slice, after) = self.parse_pat_vec_elements(refutable); @@ -2295,6 +2342,7 @@ pub impl Parser { || self.is_keyword(&~"true") || self.is_keyword(&~"false") { + // parse an expression pattern or exp .. exp let val = self.parse_expr_res(RESTRICT_NO_BAR_OP); if self.eat(&token::DOTDOT) { let end = self.parse_expr_res(RESTRICT_NO_BAR_OP); @@ -2303,34 +2351,39 @@ pub impl Parser { pat = pat_lit(val); } } else if self.eat_keyword(&~"ref") { + // parse ref pat let mutbl = self.parse_mutability(); pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl)); } else if self.eat_keyword(&~"copy") { + // parse copy pat pat = self.parse_pat_ident(refutable, bind_by_copy); } else { // XXX---refutable match bindings should work same as let let binding_mode = if refutable {bind_infer} else {bind_by_copy}; - let cannot_be_enum_or_struct; + let can_be_enum_or_struct; match self.look_ahead(1) { token::LPAREN | token::LBRACKET | token::LT | token::LBRACE | token::MOD_SEP => - cannot_be_enum_or_struct = false, + can_be_enum_or_struct = true, _ => - cannot_be_enum_or_struct = true + can_be_enum_or_struct = false } - if is_plain_ident(&*self.token) && cannot_be_enum_or_struct { + if is_plain_ident(&*self.token) && !can_be_enum_or_struct { let name = self.parse_path_without_tps(); let sub; if self.eat(&token::AT) { + // parse foo @ pat sub = Some(self.parse_pat(refutable)); } else { + // or just foo sub = None; }; pat = pat_ident(binding_mode, name, sub); } else { + // parse an enum pat let enum_path = self.parse_path_with_tps(true); match *self.token { token::LBRACE => { @@ -2342,14 +2395,13 @@ pub impl Parser { } _ => { let mut args: ~[@pat] = ~[]; - let mut star_pat = false; match *self.token { token::LPAREN => match self.look_ahead(1u) { token::BINOP(token::STAR) => { // This is a "top constructor only" pat self.bump(); self.bump(); - star_pat = true; self.expect(&token::RPAREN); + pat = pat_enum(enum_path, None); } _ => { args = self.parse_unspanned_seq( @@ -2360,23 +2412,21 @@ pub impl Parser { ), |p| p.parse_pat(refutable) ); + pat = pat_enum(enum_path, Some(args)); } }, - _ => () - } - // at this point, we're not sure whether it's a - // enum or a bind - if star_pat { - pat = pat_enum(enum_path, None); - } - else if vec::is_empty(args) && - vec::len(enum_path.idents) == 1u { - pat = pat_ident(binding_mode, - enum_path, - None); - } - else { - pat = pat_enum(enum_path, Some(args)); + _ => { + if vec::len(enum_path.idents)==1u { + // it could still be either an enum + // or an identifier pattern, resolve + // will sort it out: + pat = pat_ident(binding_mode, + enum_path, + None); + } else { + pat = pat_enum(enum_path, Some(args)); + } + } } } } @@ -2388,6 +2438,8 @@ pub impl Parser { @ast::pat { id: self.get_id(), node: pat, span: mk_sp(lo, hi) } } + // used by the copy foo and ref foo patterns to give a good + // error message when parsing mistakes like ref foo(a,b) fn parse_pat_ident(&self, refutable: bool, binding_mode: ast::binding_mode) -> ast::pat_ { if !is_plain_ident(&*self.token) { @@ -2395,6 +2447,7 @@ pub impl Parser { *self.last_span, ~"expected identifier, found path"); } + // why a path here, and not just an identifier? let name = self.parse_path_without_tps(); let sub = if self.eat(&token::AT) { Some(self.parse_pat(refutable)) @@ -2416,8 +2469,7 @@ pub impl Parser { } // parse a local variable declaration - fn parse_local(&self, is_mutbl: bool, - allow_init: bool) -> @local { + fn parse_local(&self, is_mutbl: bool) -> @local { let lo = self.span.lo; let pat = self.parse_pat(false); let mut ty = @Ty { @@ -2426,7 +2478,7 @@ pub impl Parser { span: mk_sp(lo, lo), }; if self.eat(&token::COLON) { ty = self.parse_ty(false); } - let init = if allow_init { self.parse_initializer() } else { None }; + let init = self.parse_initializer(); @spanned( lo, self.last_span.hi, @@ -2440,18 +2492,19 @@ pub impl Parser { ) } + // parse a "let" stmt fn parse_let(&self) -> @decl { let is_mutbl = self.eat_keyword(&~"mut"); let lo = self.span.lo; - let mut locals = ~[self.parse_local(is_mutbl, true)]; + let mut locals = ~[self.parse_local(is_mutbl)]; while self.eat(&token::COMMA) { - locals.push(self.parse_local(is_mutbl, true)); + locals.push(self.parse_local(is_mutbl)); } return @spanned(lo, self.last_span.hi, decl_local(locals)); } - /* assumes "let" token has already been consumed */ - fn parse_instance_var(&self, pr: visibility) -> @struct_field { + // parse a structure field + fn parse_name_and_ty(&self, pr: visibility) -> @struct_field { let mut is_mutbl = struct_immutable; let lo = self.span.lo; if self.eat_keyword(&~"mut") { @@ -2470,6 +2523,7 @@ pub impl Parser { }) } + // parse a statement. may include decl fn parse_stmt(&self, first_item_attrs: ~[attribute]) -> @stmt { maybe_whole!(self, nt_stmt); @@ -2489,6 +2543,11 @@ pub impl Parser { } else if is_ident(&*self.token) && !self.is_any_keyword(© *self.token) && self.look_ahead(1) == token::NOT { + // parse a macro invocation. Looks like there's serious + // overlap here; if this clause doesn't catch it (and it + // won't, for brace-delimited macros) it will fall through + // to the macro clause of parse_item_or_view_item. This + // could use some cleanup, it appears to me. check_expected_item(self, first_item_attrs); @@ -2530,7 +2589,7 @@ pub impl Parser { self.parse_outer_attributes()); match self.parse_item_or_view_item(/*bad*/ copy item_attrs, - true, false, false) { + false) { iovi_item(i) => { let hi = i.span.hi; let decl = @spanned(lo, hi, decl_item(i)); @@ -2554,52 +2613,41 @@ pub impl Parser { } } + // is this expression a successfully-parsed statement? fn expr_is_complete(&self, e: @expr) -> bool { return *self.restriction == RESTRICT_STMT_EXPR && !classify::expr_requires_semi_to_be_stmt(e); } + // parse a block. No inner attrs are allowed. fn parse_block(&self) -> blk { - // disallow inner attrs: - let (attrs, blk) = self.parse_inner_attrs_and_block(false); - assert!(vec::is_empty(attrs)); - return blk; + maybe_whole!(self, nt_block); + + let lo = self.span.lo; + if self.eat_keyword(&~"unsafe") { + self.obsolete(copy *self.span, ObsoleteUnsafeBlock); + } + self.expect(&token::LBRACE); + + return self.parse_block_tail_(lo, default_blk, ~[]); } - // I claim the existence of the 'parse_attrs' flag strongly - // suggests a name-change or refactoring for this function. - fn parse_inner_attrs_and_block(&self, parse_attrs: bool) + // parse a block. Inner attrs are allowed. + fn parse_inner_attrs_and_block(&self) -> (~[attribute], blk) { maybe_whole!(pair_empty self, nt_block); - fn maybe_parse_inner_attrs_and_next(p: &Parser, parse_attrs: bool) -> - (~[attribute], ~[attribute]) { - if parse_attrs { - p.parse_inner_attrs_and_next() - } else { - (~[], ~[]) - } - } - let lo = self.span.lo; if self.eat_keyword(&~"unsafe") { self.obsolete(copy *self.span, ObsoleteUnsafeBlock); } self.expect(&token::LBRACE); - let (inner, next) = - maybe_parse_inner_attrs_and_next(self, parse_attrs); + let (inner, next) = self.parse_inner_attrs_and_next(); (inner, self.parse_block_tail_(lo, default_blk, next)) } - fn parse_block_no_value(&self) -> blk { - // We parse blocks that cannot have a value the same as any other - // block; the type checker will make sure that the tail expression (if - // any) has unit type. - return self.parse_block(); - } - // Precondition: already parsed the '{' or '#{' // I guess that also means "already parsed the 'impure'" if // necessary, and this should take a qualifier. @@ -2620,7 +2668,7 @@ pub impl Parser { items: items, _ } = self.parse_items_and_view_items(first_item_attrs, - IMPORTS_AND_ITEMS_ALLOWED, false); + false, false); for items.each |item| { let decl = @spanned(item.span.lo, item.span.hi, decl_item(*item)); @@ -2739,6 +2787,9 @@ pub impl Parser { if self.eat_keyword(&~"once") { ast::Once } else { ast::Many } } + // matches optbounds = ( ( : ( boundseq )? )? ) + // where boundseq = ( bound + boundseq ) | bound + // and bound = 'static | ty fn parse_optional_ty_param_bounds(&self) -> @OptVec<TyParamBound> { if !self.eat(&token::COLON) { return @opt_vec::Empty; @@ -2799,6 +2850,7 @@ pub impl Parser { return @result; } + // matches typaram = IDENT optbounds fn parse_ty_param(&self) -> TyParam { let ident = self.parse_ident(); let bounds = self.parse_optional_ty_param_bounds(); @@ -2842,7 +2894,8 @@ pub impl Parser { (lifetimes, opt_vec::take_vec(result)) } - fn parse_fn_decl(&self, parse_arg_fn: &fn(&Parser) -> arg_or_capture_item) + // parse the argument list and result type of a function declaration + fn parse_fn_decl(&self) -> fn_decl { let args_or_capture_items: ~[arg_or_capture_item] = @@ -2850,7 +2903,7 @@ pub impl Parser { &token::LPAREN, &token::RPAREN, seq_sep_trailing_disallowed(token::COMMA), - parse_arg_fn + |p| p.parse_arg() ); let inputs = either::lefts(args_or_capture_items); @@ -2883,6 +2936,8 @@ pub impl Parser { self.bump(); } + // parse the argument list and result type of a function + // that may have a self type. fn parse_fn_decl_with_self( &self, parse_arg_fn: @@ -3030,6 +3085,7 @@ pub impl Parser { (spanned(lo, hi, self_ty), fn_decl) } + // parse the |arg, arg| header on a lambda fn parse_fn_block_decl(&self) -> fn_decl { let inputs_captures = { if self.eat(&token::OROR) { @@ -3056,6 +3112,7 @@ pub impl Parser { } } + // parse the name and optional generic types of a function header. fn parse_fn_header(&self) -> (ident, ast::Generics) { let id = self.parse_ident(); let generics = self.parse_generics(); @@ -3073,15 +3130,17 @@ pub impl Parser { span: mk_sp(lo, hi) } } + // parse an item-position function declaration. fn parse_item_fn(&self, purity: purity, abis: AbiSet) -> item_info { let (ident, generics) = self.parse_fn_header(); - let decl = self.parse_fn_decl(|p| p.parse_arg()); - let (inner_attrs, body) = self.parse_inner_attrs_and_block(true); + let decl = self.parse_fn_decl(); + let (inner_attrs, body) = self.parse_inner_attrs_and_block(); (ident, item_fn(decl, purity, abis, generics, body), Some(inner_attrs)) } + // parse a method in a trait impl fn parse_method(&self) -> @method { let attrs = self.parse_outer_attributes(); let lo = self.span.lo; @@ -3094,7 +3153,7 @@ pub impl Parser { p.parse_arg() }; - let (inner_attrs, body) = self.parse_inner_attrs_and_block(true); + let (inner_attrs, body) = self.parse_inner_attrs_and_block(); let hi = body.span.hi; let attrs = vec::append(attrs, inner_attrs); @ast::method { @@ -3227,7 +3286,7 @@ pub impl Parser { is_tuple_like = false; fields = ~[]; while *self.token != token::RBRACE { - match self.parse_class_item() { + match self.parse_struct_decl_field() { dtor_decl(ref blk, ref attrs, s) => { match the_dtor { Some((_, _, s_first)) => { @@ -3310,12 +3369,13 @@ pub impl Parser { } } - fn parse_single_class_item(&self, vis: visibility) -> @struct_field { + // parse a structure field declaration + fn parse_single_struct_field(&self, vis: visibility) -> @struct_field { if self.eat_obsolete_ident("let") { self.obsolete(*self.last_span, ObsoleteLet); } - let a_var = self.parse_instance_var(vis); + let a_var = self.parse_name_and_ty(vis); match *self.token { token::SEMI => { self.obsolete(copy *self.span, ObsoleteFieldTerminator); @@ -3329,7 +3389,7 @@ pub impl Parser { self.span_fatal( copy *self.span, fmt!( - "expected `;`, `,`, or '}' but found `%s`", + "expected `,`, or '}' but found `%s`", self.this_token_to_str() ) ); @@ -3338,13 +3398,8 @@ pub impl Parser { a_var } - fn parse_dtor(&self, attrs: ~[attribute]) -> class_contents { - let lo = self.last_span.lo; - let body = self.parse_block(); - dtor_decl(body, attrs, mk_sp(lo, self.last_span.hi)) - } - - fn parse_class_item(&self) -> class_contents { + // parse an element of a struct definition + fn parse_struct_decl_field(&self) -> class_contents { if self.try_parse_obsolete_priv_section() { return members(~[]); @@ -3353,11 +3408,11 @@ pub impl Parser { let attrs = self.parse_outer_attributes(); if self.eat_keyword(&~"priv") { - return members(~[self.parse_single_class_item(private)]) + return members(~[self.parse_single_struct_field(private)]) } if self.eat_keyword(&~"pub") { - return members(~[self.parse_single_class_item(public)]); + return members(~[self.parse_single_struct_field(public)]); } if self.try_parse_obsolete_struct_ctor() { @@ -3365,13 +3420,16 @@ pub impl Parser { } if self.eat_keyword(&~"drop") { - return self.parse_dtor(attrs); + let lo = self.last_span.lo; + let body = self.parse_block(); + return dtor_decl(body, attrs, mk_sp(lo, self.last_span.hi)) } else { - return members(~[self.parse_single_class_item(inherited)]); + return members(~[self.parse_single_struct_field(inherited)]); } } + // parse visiility: PUB, PRIV, or nothing fn parse_visibility(&self) -> visibility { if self.eat_keyword(&~"pub") { public } else if self.eat_keyword(&~"priv") { private } @@ -3399,14 +3457,12 @@ pub impl Parser { items: starting_items, _ } = self.parse_items_and_view_items(first_item_attrs, - VIEW_ITEMS_AND_ITEMS_ALLOWED, - true); + true, true); let mut items: ~[@item] = starting_items; let attrs_remaining_len = attrs_remaining.len(); - // looks like this code depends on the invariant that - // outer attributes can't occur on view items (or macros - // invocations?) + // don't think this other loop is even necessary.... + let mut first = true; while *self.token != term { let mut attrs = self.parse_outer_attributes(); @@ -3418,9 +3474,7 @@ pub impl Parser { attrs); match self.parse_item_or_view_item( /*bad*/ copy attrs, - true, - false, - true + true // macros allowed ) { iovi_item(item) => items.push(item), iovi_view_item(view_item) => { @@ -3525,6 +3579,7 @@ pub impl Parser { self.mod_path_stack.pop(); } + // read a module from a source file. fn eval_src_mod(&self, id: ast::ident, outer_attrs: ~[ast::attribute], id_sp: span) -> (ast::item_, ~[ast::attribute]) { @@ -3582,12 +3637,13 @@ pub impl Parser { } } + // parse a function declaration from a foreign module fn parse_item_foreign_fn(&self, attrs: ~[attribute]) -> @foreign_item { let lo = self.span.lo; let vis = self.parse_visibility(); let purity = self.parse_fn_purity(); let (ident, generics) = self.parse_fn_header(); - let decl = self.parse_fn_decl(|p| p.parse_arg()); + let decl = self.parse_fn_decl(); let hi = self.span.hi; self.expect(&token::SEMI); @ast::foreign_item { ident: ident, @@ -3598,6 +3654,7 @@ pub impl Parser { vis: vis } } + // parse a const definition from a foreign module fn parse_item_foreign_const(&self, vis: ast::visibility, attrs: ~[attribute]) -> @foreign_item { let lo = self.span.lo; @@ -3622,6 +3679,7 @@ pub impl Parser { vis: vis } } + // parse safe/unsafe and fn fn parse_fn_purity(&self) -> purity { if self.eat_keyword(&~"fn") { impure_fn } else if self.eat_keyword(&~"pure") { @@ -3636,45 +3694,30 @@ pub impl Parser { else { self.unexpected(); } } - fn parse_foreign_item(&self, attrs: ~[attribute]) -> @foreign_item { - let vis = self.parse_visibility(); - if self.is_keyword(&~"const") || self.is_keyword(&~"static") { - self.parse_item_foreign_const(vis, attrs) - } else { - self.parse_item_foreign_fn(attrs) - } - } + // at this point, this is essentially a wrapper for + // parse_foreign_items. fn parse_foreign_mod_items(&self, sort: ast::foreign_mod_sort, abis: AbiSet, first_item_attrs: ~[attribute]) -> foreign_mod { - // Shouldn't be any view items since we've already parsed an item attr let ParsedItemsAndViewItems { attrs_remaining: attrs_remaining, view_items: view_items, items: _, foreign_items: foreign_items - } = self.parse_items_and_view_items(first_item_attrs, - FOREIGN_ITEMS_ALLOWED, - true); - - let mut items: ~[@foreign_item] = foreign_items; + } = self.parse_foreign_items(first_item_attrs, true); let mut initial_attrs = attrs_remaining; - while *self.token != token::RBRACE { - let attrs = vec::append(initial_attrs, - self.parse_outer_attributes()); - initial_attrs = ~[]; - items.push(self.parse_foreign_item(attrs)); - } + assert!(*self.token == token::RBRACE); ast::foreign_mod { sort: sort, abis: abis, view_items: view_items, - items: items + items: foreign_items } } + // parse extern foo; or extern mod foo { ... } or extern { ... } fn parse_item_foreign_mod(&self, lo: BytePos, opt_abis: Option<AbiSet>, @@ -3744,15 +3787,9 @@ pub impl Parser { }) } - fn parse_type_decl(&self) -> (BytePos, ident) { - let lo = self.last_span.lo; - let id = self.parse_ident(); - (lo, id) - } - // parse type Foo = Bar; fn parse_item_type(&self) -> item_info { - let (_, ident) = self.parse_type_decl(); + let ident = self.parse_ident(); self.parse_region_param(); let tps = self.parse_generics(); self.expect(&token::EQ); @@ -3769,11 +3806,13 @@ pub impl Parser { } } + // parse a structure-like enum variant definition + // this should probably be renamed or refactored... fn parse_struct_def(&self) -> @struct_def { let mut the_dtor: Option<(blk, ~[attribute], codemap::span)> = None; let mut fields: ~[@struct_field] = ~[]; while *self.token != token::RBRACE { - match self.parse_class_item() { + match self.parse_struct_decl_field() { dtor_decl(ref blk, ref attrs, s) => { match the_dtor { Some((_, _, s_first)) => { @@ -3812,6 +3851,7 @@ pub impl Parser { }; } + // parse the part of an "enum" decl following the '{' fn parse_enum_def(&self, _generics: &ast::Generics) -> enum_def { let mut variants = ~[]; let mut all_nullary = true, have_disr = false; @@ -3821,7 +3861,7 @@ pub impl Parser { let vis = self.parse_visibility(); - let ident, needs_comma, kind; + let ident, kind; let mut args = ~[], disr_expr = None; ident = self.parse_ident(); if self.eat(&token::LBRACE) { @@ -3850,7 +3890,6 @@ pub impl Parser { } else { kind = tuple_variant_kind(~[]); } - needs_comma = true; let vr = ast::variant_ { name: ident, @@ -3862,7 +3901,7 @@ pub impl Parser { }; variants.push(spanned(vlo, self.last_span.hi, vr)); - if needs_comma && !self.eat(&token::COMMA) { break; } + if !self.eat(&token::COMMA) { break; } } self.expect(&token::RBRACE); if (have_disr && !all_nullary) { @@ -3873,6 +3912,7 @@ pub impl Parser { ast::enum_def { variants: variants } } + // parse an "enum" declaration fn parse_item_enum(&self) -> item_info { let id = self.parse_ident(); self.parse_region_param(); @@ -3938,6 +3978,7 @@ pub impl Parser { } } + // parse a string as an ABI spec on an extern type or module fn parse_opt_abis(&self) -> Option<AbiSet> { match *self.token { token::LIT_STR(s) => { @@ -3983,31 +4024,53 @@ pub impl Parser { // parse one of the items or view items allowed by the // flags; on failure, return iovi_none. + // NB: this function no longer parses the items inside an + // extern mod. fn parse_item_or_view_item( &self, attrs: ~[attribute], - items_allowed: bool, - foreign_items_allowed: bool, macros_allowed: bool ) -> item_or_view_item { - assert!(items_allowed != foreign_items_allowed); - maybe_whole!(iovi self, nt_item); let lo = self.span.lo; - let visibility; - if self.eat_keyword(&~"pub") { - visibility = public; - } else if self.eat_keyword(&~"priv") { - visibility = private; - } else { - visibility = inherited; + let visibility = self.parse_visibility(); + + // must be a view item: + if self.eat_keyword(&~"use") { + // USE ITEM (iovi_view_item) + let view_item = self.parse_use(); + self.expect(&token::SEMI); + return iovi_view_item(@ast::view_item { + node: view_item, + attrs: attrs, + vis: visibility, + span: mk_sp(lo, self.last_span.hi) + }); } + // either a view item or an item: + if self.eat_keyword(&~"extern") { + let opt_abis = self.parse_opt_abis(); - if items_allowed && - (self.is_keyword(&~"const") || - (self.is_keyword(&~"static") && - !self.token_is_keyword(&~"fn", &self.look_ahead(1)))) { + if self.eat_keyword(&~"fn") { + // EXTERN FUNCTION ITEM + let abis = opt_abis.get_or_default(AbiSet::C()); + let (ident, item_, extra_attrs) = + self.parse_item_fn(extern_fn, abis); + return iovi_item(self.mk_item(lo, self.last_span.hi, ident, + item_, visibility, + maybe_append(attrs, + extra_attrs))); + } else { + // EXTERN MODULE ITEM (iovi_view_item) + return self.parse_item_foreign_mod(lo, opt_abis, visibility, attrs, + true); + } + } + // the rest are all guaranteed to be items: + if (self.is_keyword(&~"const") || + (self.is_keyword(&~"static") && + !self.token_is_keyword(&~"fn", &self.look_ahead(1)))) { // CONST / STATIC ITEM if self.is_keyword(&~"const") { self.obsolete(*self.span, ObsoleteConstItem); @@ -4018,13 +4081,7 @@ pub impl Parser { visibility, maybe_append(attrs, extra_attrs))); } - if foreign_items_allowed && - (self.is_keyword(&~"const") || self.is_keyword(&~"static")) { - // FOREIGN CONST ITEM - let item = self.parse_item_foreign_const(visibility, attrs); - return iovi_foreign_item(item); - } - if items_allowed && self.is_keyword(&~"fn") && + if self.is_keyword(&~"fn") && !self.fn_expr_lookahead(self.look_ahead(1u)) { // FUNCTION ITEM self.bump(); @@ -4034,7 +4091,7 @@ pub impl Parser { visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && self.eat_keyword(&~"pure") { + if self.eat_keyword(&~"pure") { // PURE FUNCTION ITEM (obsolete) self.obsolete(*self.last_span, ObsoletePurity); self.expect_keyword(&~"fn"); @@ -4044,16 +4101,9 @@ pub impl Parser { visibility, maybe_append(attrs, extra_attrs))); } - if foreign_items_allowed && - (self.is_keyword(&~"fn") || self.is_keyword(&~"pure") || - self.is_keyword(&~"unsafe")) { - // FOREIGN FUNCTION ITEM (no items allowed) - let item = self.parse_item_foreign_fn(attrs); - return iovi_foreign_item(item); - } - if items_allowed && self.is_keyword(&~"unsafe") + if self.is_keyword(&~"unsafe") && self.look_ahead(1u) != token::LBRACE { - // UNSAFE FUNCTION ITEM (where items are allowed) + // UNSAFE FUNCTION ITEM self.bump(); self.expect_keyword(&~"fn"); let (ident, item_, extra_attrs) = @@ -4062,59 +4112,35 @@ pub impl Parser { visibility, maybe_append(attrs, extra_attrs))); } - if self.eat_keyword(&~"extern") { - let opt_abis = self.parse_opt_abis(); - - if items_allowed && self.eat_keyword(&~"fn") { - // EXTERN FUNCTION ITEM - let abis = opt_abis.get_or_default(AbiSet::C()); - let (ident, item_, extra_attrs) = - self.parse_item_fn(extern_fn, abis); - return iovi_item(self.mk_item(lo, self.last_span.hi, ident, - item_, visibility, - maybe_append(attrs, - extra_attrs))); - } - if !foreign_items_allowed { - // EXTERN MODULE ITEM - return self.parse_item_foreign_mod(lo, opt_abis, visibility, attrs, - items_allowed); - } - } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"mod") { + if self.eat_keyword(&~"mod") { // MODULE ITEM let (ident, item_, extra_attrs) = self.parse_item_mod(attrs); return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_, visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"type") { + if self.eat_keyword(&~"type") { // TYPE ITEM let (ident, item_, extra_attrs) = self.parse_item_type(); return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_, visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"enum") { + if self.eat_keyword(&~"enum") { // ENUM ITEM let (ident, item_, extra_attrs) = self.parse_item_enum(); return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_, visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"trait") { + if self.eat_keyword(&~"trait") { // TRAIT ITEM let (ident, item_, extra_attrs) = self.parse_item_trait(); return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_, visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"impl") { + if self.eat_keyword(&~"impl") { // IMPL ITEM let (ident, item_, extra_attrs) = self.parse_item_impl(visibility); @@ -4122,25 +4148,49 @@ pub impl Parser { visibility, maybe_append(attrs, extra_attrs))); } - if items_allowed && !foreign_items_allowed && - self.eat_keyword(&~"struct") { + if self.eat_keyword(&~"struct") { // STRUCT ITEM let (ident, item_, extra_attrs) = self.parse_item_struct(); return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_, visibility, maybe_append(attrs, extra_attrs))); } - if !foreign_items_allowed && self.eat_keyword(&~"use") { - // USE ITEM - let view_item = self.parse_use(); - self.expect(&token::SEMI); - return iovi_view_item(@ast::view_item { - node: view_item, - attrs: attrs, - vis: visibility, - span: mk_sp(lo, self.last_span.hi) - }); + self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility) + } + + // parse a foreign item; on failure, return iovi_none. + fn parse_foreign_item( + &self, + attrs: ~[attribute], + macros_allowed: bool + ) -> item_or_view_item { + maybe_whole!(iovi self, nt_item); + let lo = self.span.lo; + + let visibility = self.parse_visibility(); + + if (self.is_keyword(&~"const") || self.is_keyword(&~"static")) { + // FOREIGN CONST ITEM + let item = self.parse_item_foreign_const(visibility, attrs); + return iovi_foreign_item(item); } + if (self.is_keyword(&~"fn") || self.is_keyword(&~"pure") || + self.is_keyword(&~"unsafe")) { + // FOREIGN FUNCTION ITEM + let item = self.parse_item_foreign_fn(attrs); + return iovi_foreign_item(item); + } + self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility) + } + + // this is the fall-through for parsing items. + fn parse_macro_use_or_failure( + &self, + attrs: ~[attribute], + macros_allowed: bool, + lo : BytePos, + visibility : visibility + ) -> item_or_view_item { if macros_allowed && !self.is_any_keyword(© *self.token) && self.look_ahead(1) == token::NOT && (is_plain_ident(&self.look_ahead(2)) @@ -4197,7 +4247,7 @@ pub impl Parser { } fn parse_item(&self, attrs: ~[attribute]) -> Option<@ast::item> { - match self.parse_item_or_view_item(attrs, true, false, true) { + match self.parse_item_or_view_item(attrs, true) { iovi_none => None, iovi_view_item(_) => @@ -4209,6 +4259,7 @@ pub impl Parser { } } + // parse, e.g., "use a::b::{z,y}" fn parse_use(&self) -> view_item_ { return view_item_use(self.parse_view_paths()); } @@ -4353,101 +4404,73 @@ pub impl Parser { // Parses a sequence of items. Stops when it finds program // text that can't be parsed as an item - // - mod_items uses VIEW_ITEMS_AND_ITEMS_ALLOWED - // - block_tail_ uses IMPORTS_AND_ITEMS_ALLOWED - // - foreign_mod_items uses FOREIGN_ITEMS_ALLOWED + // - mod_items uses extern_mod_allowed = true + // - block_tail_ uses extern_mod_allowed = false fn parse_items_and_view_items(&self, first_item_attrs: ~[attribute], - mode: view_item_parse_mode, + mut extern_mod_allowed: bool, macros_allowed: bool) -> ParsedItemsAndViewItems { let mut attrs = vec::append(first_item_attrs, self.parse_outer_attributes()); - - let items_allowed = match mode { - VIEW_ITEMS_AND_ITEMS_ALLOWED | IMPORTS_AND_ITEMS_ALLOWED => true, - FOREIGN_ITEMS_ALLOWED => false - }; - let foreign_items_allowed = match mode { - FOREIGN_ITEMS_ALLOWED => true, - VIEW_ITEMS_AND_ITEMS_ALLOWED | IMPORTS_AND_ITEMS_ALLOWED => false - }; - // First, parse view items. - let mut (view_items, items, foreign_items) = (~[], ~[], ~[]); + let mut (view_items, items) = (~[], ~[]); let mut done = false; - if mode != FOREIGN_ITEMS_ALLOWED { - let mut extern_mod_allowed = match mode { - VIEW_ITEMS_AND_ITEMS_ALLOWED => true, - IMPORTS_AND_ITEMS_ALLOWED => false, - FOREIGN_ITEMS_ALLOWED => { - self.bug(~"couldn't get here with FOREIGN_ITEMS_ALLOWED") + // I think this code would probably read better as a single + // loop with a mutable three-state-variable (for extern mods, + // view items, and regular items) ... except that because + // of macros, I'd like to delay that entire check until later. + loop { + match self.parse_item_or_view_item(/*bad*/ copy attrs, + macros_allowed) { + iovi_none => { + done = true; + break; } - }; - - loop { - match self.parse_item_or_view_item(/*bad*/ copy attrs, - items_allowed, - foreign_items_allowed, - macros_allowed) { - iovi_none => { - done = true; - break; - } - iovi_view_item(view_item) => { - match view_item.node { - view_item_use(*) => { - // `extern mod` must precede `use`. - extern_mod_allowed = false; - } - view_item_extern_mod(*) - if !extern_mod_allowed => { - self.span_err(view_item.span, - ~"\"extern mod\" \ - declarations are not \ - allowed here"); - } - view_item_extern_mod(*) => {} + iovi_view_item(view_item) => { + match view_item.node { + view_item_use(*) => { + // `extern mod` must precede `use`. + extern_mod_allowed = false; } - view_items.push(view_item); - } - iovi_item(item) => { - assert!(items_allowed); - items.push(item); - attrs = self.parse_outer_attributes(); - break; - } - iovi_foreign_item(foreign_item) => { - assert!(foreign_items_allowed); - foreign_items.push(foreign_item); - attrs = self.parse_outer_attributes(); - break; + view_item_extern_mod(*) + if !extern_mod_allowed => { + self.span_err(view_item.span, + ~"\"extern mod\" \ + declarations are not \ + allowed here"); + } + view_item_extern_mod(*) => {} } + view_items.push(view_item); + } + iovi_item(item) => { + items.push(item); + attrs = self.parse_outer_attributes(); + break; + } + iovi_foreign_item(_) => { + fail!(); } - attrs = self.parse_outer_attributes(); } + attrs = self.parse_outer_attributes(); } // Next, parse items. if !done { loop { match self.parse_item_or_view_item(/*bad*/ copy attrs, - items_allowed, - foreign_items_allowed, macros_allowed) { iovi_none => break, iovi_view_item(view_item) => { self.span_err(view_item.span, ~"`use` and `extern mod` declarations \ must precede items"); - view_items.push(view_item); } iovi_item(item) => { - assert!(items_allowed); items.push(item) } - iovi_foreign_item(foreign_item) => { - assert!(foreign_items_allowed); - foreign_items.push(foreign_item); + iovi_foreign_item(_) => { + fail!(); } } attrs = self.parse_outer_attributes(); @@ -4458,12 +4481,49 @@ pub impl Parser { attrs_remaining: attrs, view_items: view_items, items: items, + foreign_items: ~[] + } + } + + // Parses a sequence of foreign items. Stops when it finds program + // text that can't be parsed as an item + fn parse_foreign_items(&self, first_item_attrs: ~[attribute], + macros_allowed: bool) + -> ParsedItemsAndViewItems { + let mut attrs = vec::append(first_item_attrs, + self.parse_outer_attributes()); + let mut foreign_items = ~[]; + loop { + match self.parse_foreign_item(/*bad*/ copy attrs, macros_allowed) { + iovi_none => break, + iovi_view_item(view_item) => { + // I think this can't occur: + self.span_err(view_item.span, + ~"`use` and `extern mod` declarations \ + must precede items"); + } + iovi_item(_) => { + // FIXME #5668: this will occur for a macro invocation: + fail!(); + } + iovi_foreign_item(foreign_item) => { + foreign_items.push(foreign_item); + } + } + attrs = self.parse_outer_attributes(); + } + + ParsedItemsAndViewItems { + attrs_remaining: attrs, + view_items: ~[], + items: ~[], foreign_items: foreign_items } } - // Parses a source module as a crate - fn parse_crate_mod(&self, _cfg: crate_cfg) -> @crate { + // Parses a source module as a crate. This is the main + // entry point for the parser. + fn parse_crate_mod(&self) -> @crate { let lo = self.span.lo; // parse the crate's inner attrs, maybe (oops) one // of the attrs of an item: diff --git a/src/libsyntax/parse/prec.rs b/src/libsyntax/parse/prec.rs deleted file mode 100644 index 5f37c922c1e..00000000000 --- a/src/libsyntax/parse/prec.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use ast; -use ast::*; -use parse::token::*; -use parse::token::Token; - -/// Unary operators have higher precedence than binary -pub static unop_prec: uint = 100u; - -/** - * Precedence of the `as` operator, which is a binary operator - * but is not represented in the precedence table. - */ -pub static as_prec: uint = 11u; - -/** - * Maps a token to a record specifying the corresponding binary - * operator and its precedence - */ -pub fn token_to_binop(tok: Token) -> Option<ast::binop> { - match tok { - BINOP(STAR) => Some(mul), - BINOP(SLASH) => Some(quot), - BINOP(PERCENT) => Some(rem), - // 'as' sits between here with 11 - BINOP(PLUS) => Some(add), - BINOP(MINUS) => Some(subtract), - BINOP(SHL) => Some(shl), - BINOP(SHR) => Some(shr), - BINOP(AND) => Some(bitand), - BINOP(CARET) => Some(bitxor), - BINOP(OR) => Some(bitor), - LT => Some(lt), - LE => Some(le), - GE => Some(ge), - GT => Some(gt), - EQEQ => Some(eq), - NE => Some(ne), - ANDAND => Some(and), - OROR => Some(or), - _ => None - } -} diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 413f1688df1..0327a3b80da 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -364,6 +364,34 @@ impl<'self> to_bytes::IterBytes for StringRef<'self> { } } +/** + * Maps a token to a record specifying the corresponding binary + * operator + */ +pub fn token_to_binop(tok: Token) -> Option<ast::binop> { + match tok { + BINOP(STAR) => Some(ast::mul), + BINOP(SLASH) => Some(ast::quot), + BINOP(PERCENT) => Some(ast::rem), + BINOP(PLUS) => Some(ast::add), + BINOP(MINUS) => Some(ast::subtract), + BINOP(SHL) => Some(ast::shl), + BINOP(SHR) => Some(ast::shr), + BINOP(AND) => Some(ast::bitand), + BINOP(CARET) => Some(ast::bitxor), + BINOP(OR) => Some(ast::bitor), + LT => Some(ast::lt), + LE => Some(ast::le), + GE => Some(ast::ge), + GT => Some(ast::gt), + EQEQ => Some(ast::eq), + NE => Some(ast::ne), + ANDAND => Some(ast::and), + OROR => Some(ast::or), + _ => None + } +} + pub struct ident_interner { priv interner: Interner<@~str>, } @@ -390,60 +418,68 @@ pub impl ident_interner { } } +// return a fresh interner, preloaded with special identifiers. +// EFFECT: stores this interner in TLS +pub fn mk_fresh_ident_interner() -> @ident_interner { + // the indices here must correspond to the numbers in + // special_idents. + let init_vec = ~[ + @~"_", // 0 + @~"anon", // 1 + @~"drop", // 2 + @~"", // 3 + @~"unary", // 4 + @~"!", // 5 + @~"[]", // 6 + @~"unary-", // 7 + @~"__extensions__", // 8 + @~"self", // 9 + @~"item", // 10 + @~"block", // 11 + @~"stmt", // 12 + @~"pat", // 13 + @~"expr", // 14 + @~"ty", // 15 + @~"ident", // 16 + @~"path", // 17 + @~"tt", // 18 + @~"matchers", // 19 + @~"str", // 20 + @~"TyVisitor", // 21 + @~"arg", // 22 + @~"descrim", // 23 + @~"__rust_abi", // 24 + @~"__rust_stack_shim", // 25 + @~"TyDesc", // 26 + @~"dtor", // 27 + @~"main", // 28 + @~"<opaque>", // 29 + @~"blk", // 30 + @~"static", // 31 + @~"intrinsic", // 32 + @~"__foreign_mod__", // 33 + @~"__field__", // 34 + @~"C", // 35 + @~"Self", // 36 + ]; + + let rv = @ident_interner { + interner: interner::Interner::prefill(init_vec) + }; + unsafe { + task::local_data::local_data_set(interner_key!(), @rv); + } + rv +} + +// if an interner exists in TLS, return it. Otherwise, prepare a +// fresh one. pub fn mk_ident_interner() -> @ident_interner { unsafe { match task::local_data::local_data_get(interner_key!()) { Some(interner) => *interner, None => { - // the indices here must correspond to the numbers in - // special_idents. - let init_vec = ~[ - @~"_", // 0 - @~"anon", // 1 - @~"drop", // 2 - @~"", // 3 - @~"unary", // 4 - @~"!", // 5 - @~"[]", // 6 - @~"unary-", // 7 - @~"__extensions__", // 8 - @~"self", // 9 - @~"item", // 10 - @~"block", // 11 - @~"stmt", // 12 - @~"pat", // 13 - @~"expr", // 14 - @~"ty", // 15 - @~"ident", // 16 - @~"path", // 17 - @~"tt", // 18 - @~"matchers", // 19 - @~"str", // 20 - @~"TyVisitor", // 21 - @~"arg", // 22 - @~"descrim", // 23 - @~"__rust_abi", // 24 - @~"__rust_stack_shim", // 25 - @~"TyDesc", // 26 - @~"dtor", // 27 - @~"main", // 28 - @~"<opaque>", // 29 - @~"blk", // 30 - @~"static", // 31 - @~"intrinsic", // 32 - @~"__foreign_mod__", // 33 - @~"__field__", // 34 - @~"C", // 35 - @~"Self", // 36 - ]; - - let rv = @ident_interner { - interner: interner::Interner::prefill(init_vec) - }; - - task::local_data::local_data_set(interner_key!(), @rv); - - rv + mk_fresh_ident_interner() } } } diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 2682139ce3f..a401d9eb8ac 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! This module contains the Rust parser. It maps source text + * to token trees and to ASTs. It contains code for expanding + * macros. + */ + #[link(name = "syntax", vers = "0.7-pre", uuid = "9311401b-d6ea-4cd9-a1d9-61f89499c645")]; diff --git a/src/test/compile-fail/enums-pats-not-idents.rs b/src/test/compile-fail/enums-pats-not-idents.rs new file mode 100644 index 00000000000..9eb98341112 --- /dev/null +++ b/src/test/compile-fail/enums-pats-not-idents.rs @@ -0,0 +1,16 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//error-pattern:unresolved enum variant + +fn main() { + // a bug in the parser is allowing this: + let a() = 13; +} |
