about summary refs log tree commit diff
path: root/src/comp/syntax/parse/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/comp/syntax/parse/parser.rs')
-rw-r--r--src/comp/syntax/parse/parser.rs125
1 files changed, 96 insertions, 29 deletions
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index b52b02a3323..3f5cae0a161 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -1531,14 +1531,23 @@ fn parse_let(p: parser) -> @ast::decl {
     ret @spanned(lo, p.last_span.hi, ast::decl_local(locals));
 }
 
-fn parse_stmt(p: parser) -> @ast::stmt {
+fn parse_stmt(p: parser, first_item_attrs: [ast::attribute]) -> @ast::stmt {
+    fn check_expected_item(p: parser, current_attrs: [ast::attribute]) {
+        // If we have attributes then we should have an item
+        if vec::is_not_empty(current_attrs) {
+            p.fatal("expected item");
+        }
+    }
+
     let lo = p.span.lo;
-    if eat_word(p, "let") {
+    if is_word(p, "let") {
+        check_expected_item(p, first_item_attrs);
+        expect_word(p, "let");
         let decl = parse_let(p);
         ret @spanned(lo, decl.span.hi, ast::stmt_decl(decl, p.get_id()));
     } else {
         let item_attrs;
-        alt parse_outer_attrs_or_ext(p) {
+        alt parse_outer_attrs_or_ext(p, first_item_attrs) {
           none. { item_attrs = []; }
           some(left(attrs)) { item_attrs = attrs; }
           some(right(ext)) {
@@ -1546,6 +1555,8 @@ fn parse_stmt(p: parser) -> @ast::stmt {
           }
         }
 
+        let item_attrs = first_item_attrs + item_attrs;
+
         alt parse_item(p, item_attrs) {
           some(i) {
             let hi = i.span.hi;
@@ -1555,10 +1566,7 @@ fn parse_stmt(p: parser) -> @ast::stmt {
           none() { /* fallthrough */ }
         }
 
-        // If we have attributes then we should have an item
-        if vec::len(item_attrs) > 0u {
-            ret p.fatal("expected item");
-        }
+        check_expected_item(p, item_attrs);
 
         // Remainder are line-expr stmts.
         let e = parse_expr_res(p, RESTRICT_STMT_EXPR);
@@ -1605,16 +1613,37 @@ fn stmt_ends_with_semi(stmt: ast::stmt) -> bool {
 }
 
 fn parse_block(p: parser) -> ast::blk {
+    let (attrs, blk) = parse_inner_attrs_and_block(p, false);
+    assert vec::is_empty(attrs);
+    ret blk;
+}
+
+fn parse_inner_attrs_and_block(
+    p: parser, parse_attrs: bool) -> ([ast::attribute], ast::blk) {
+
+    fn maybe_parse_inner_attrs_and_next(
+        p: parser, parse_attrs: bool) ->
+        {inner: [ast::attribute], next: [ast::attribute]} {
+        if parse_attrs {
+            parse_inner_attrs_and_next(p)
+        } else {
+            {inner: [], next: []}
+        }
+    }
+
     let lo = p.span.lo;
     if eat_word(p, "unchecked") {
         expect(p, token::LBRACE);
-        be parse_block_tail(p, lo, ast::unchecked_blk);
+        let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
+        ret (inner, parse_block_tail_(p, lo, ast::unchecked_blk, next));
     } else if eat_word(p, "unsafe") {
         expect(p, token::LBRACE);
-        be parse_block_tail(p, lo, ast::unsafe_blk);
+        let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
+        ret (inner, parse_block_tail_(p, lo, ast::unsafe_blk, next));
     } else {
         expect(p, token::LBRACE);
-        be parse_block_tail(p, lo, ast::default_blk);
+        let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
+        ret (inner, parse_block_tail_(p, lo, ast::default_blk, next));
     }
 }
 
@@ -1630,15 +1659,28 @@ fn parse_block_no_value(p: parser) -> ast::blk {
 // necessary, and this should take a qualifier.
 // some blocks start with "#{"...
 fn parse_block_tail(p: parser, lo: uint, s: ast::blk_check_mode) -> ast::blk {
-    let stmts = [], expr = none;
-    let view_items = parse_view_import_only(p);
+    parse_block_tail_(p, lo, s, [])
+}
+
+fn parse_block_tail_(p: parser, lo: uint, s: ast::blk_check_mode,
+                     first_item_attrs: [ast::attribute]) -> ast::blk {
+    let stmts = [];
+    let expr = none;
+    let view_items = maybe_parse_view_import_only(p, first_item_attrs);
+    let initial_attrs = first_item_attrs;
+
+    if p.token == token::RBRACE && !vec::is_empty(initial_attrs) {
+        p.fatal("expected item");
+    }
+
     while p.token != token::RBRACE {
         alt p.token {
           token::SEMI. {
             p.bump(); // empty
           }
           _ {
-            let stmt = parse_stmt(p);
+            let stmt = parse_stmt(p, initial_attrs);
+            initial_attrs = [];
             alt stmt.node {
               ast::stmt_expr(e, stmt_id) { // Expression without semicolon:
                 alt p.token {
@@ -1751,7 +1793,8 @@ fn parse_item_fn(p: parser, purity: ast::purity,
     let lo = p.last_span.lo;
     let t = parse_fn_header(p);
     let decl = parse_fn_decl(p, purity);
-    let body = parse_block(p);
+    let (inner_attrs, body) = parse_inner_attrs_and_block(p, true);
+    let attrs = attrs + inner_attrs;
     ret mk_item(p, lo, body.span.hi, t.ident,
                 ast::item_fn(decl, t.tps, body), attrs);
 }
@@ -1832,8 +1875,7 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
 fn parse_mod_items(p: parser, term: token::token,
                    first_item_attrs: [ast::attribute]) -> ast::_mod {
     // Shouldn't be any view items since we've already parsed an item attr
-    let view_items =
-        if vec::len(first_item_attrs) == 0u { parse_view(p) } else { [] };
+    let view_items = maybe_parse_view(p, first_item_attrs);
     let items: [@ast::item] = [];
     let initial_attrs = first_item_attrs;
     while p.token != term {
@@ -2134,14 +2176,20 @@ fn parse_item(p: parser, attrs: [ast::attribute]) -> option::t<@ast::item> {
 // extensions, which both begin with token.POUND
 type attr_or_ext = option::t<either::t<[ast::attribute], @ast::expr>>;
 
-fn parse_outer_attrs_or_ext(p: parser) -> attr_or_ext {
+fn parse_outer_attrs_or_ext(
+    p: parser,
+    first_item_attrs: [ast::attribute]) -> attr_or_ext {
+    let expect_item_next = vec::is_not_empty(first_item_attrs);
     if p.token == token::POUND {
         let lo = p.span.lo;
-        p.bump();
-        if p.token == token::LBRACKET {
+        if p.look_ahead(1u) == token::LBRACKET {
+            p.bump();
             let first_attr = parse_attribute_naked(p, ast::attr_outer, lo);
             ret some(left([first_attr] + parse_outer_attributes(p)));
-        } else if !(p.token == token::LT || p.token == token::LBRACKET) {
+        } else if !(p.look_ahead(1u) == token::LT
+                    || p.look_ahead(1u) == token::LBRACKET
+                    || expect_item_next) {
+            p.bump();
             ret some(right(parse_syntax_ext_naked(p, lo)));
         } else { ret none; }
     } else { ret none; }
@@ -2182,6 +2230,10 @@ fn parse_inner_attrs_and_next(p: parser) ->
     let inner_attrs: [ast::attribute] = [];
     let next_outer_attrs: [ast::attribute] = [];
     while p.token == token::POUND {
+        if p.look_ahead(1u) != token::LBRACKET {
+            // This is an extension
+            break;
+        }
         let attr = parse_attribute(p, ast::attr_inner);
         if p.token == token::SEMI {
             p.bump();
@@ -2377,22 +2429,37 @@ fn is_view_item(p: parser) -> bool {
     }
 }
 
-fn parse_view(p: parser) -> [@ast::view_item] {
-    parse_view_while(p, is_view_item)
+fn maybe_parse_view(
+    p: parser,
+    first_item_attrs: [ast::attribute]) -> [@ast::view_item] {
+
+    maybe_parse_view_while(p, first_item_attrs, is_view_item)
 }
 
-fn parse_view_import_only(p: parser) -> [@ast::view_item] {
-    parse_view_while(p, bind is_word(_, "import"))
+fn maybe_parse_view_import_only(
+    p: parser,
+    first_item_attrs: [ast::attribute]) -> [@ast::view_item] {
+
+    maybe_parse_view_while(p, first_item_attrs, bind is_word(_, "import"))
 }
 
-fn parse_view_while(p: parser, f: fn@(parser) -> bool) -> [@ast::view_item] {
-    let items = [];
-    while f(p) { items += [parse_view_item(p)]; }
-    ret items;
+fn maybe_parse_view_while(
+    p: parser,
+    first_item_attrs: [ast::attribute],
+    f: fn@(parser) -> bool) -> [@ast::view_item] {
+
+    if vec::len(first_item_attrs) == 0u {
+        let items = [];
+        while f(p) { items += [parse_view_item(p)]; }
+        ret items;
+    } else {
+        // Shouldn't be any view items since we've already parsed an item attr
+        ret [];
+    }
 }
 
 fn parse_native_view(p: parser) -> [@ast::view_item] {
-    parse_view_while(p, is_view_item)
+    maybe_parse_view_while(p, [], is_view_item)
 }
 
 fn parse_crate_from_source_file(input: str, cfg: ast::crate_cfg,