// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use ast; use codemap::{spanned, Spanned, mk_sp}; use parse::common::*; //resolve bug? use parse::token; use parse::parser::Parser; use parse::token::INTERPOLATED; // a parser that can parse attributes. pub trait ParserAttr { fn parse_outer_attributes(&mut self) -> ~[ast::Attribute]; fn parse_attribute(&mut self, permit_inner: bool) -> ast::Attribute; fn parse_inner_attrs_and_next(&mut self) -> (~[ast::Attribute], ~[ast::Attribute]); fn parse_meta_item(&mut self) -> @ast::MetaItem; fn parse_meta_seq(&mut self) -> ~[@ast::MetaItem]; fn parse_optional_meta(&mut self) -> ~[@ast::MetaItem]; } impl ParserAttr for Parser { // Parse attributes that appear before an item fn parse_outer_attributes(&mut self) -> ~[ast::Attribute] { let mut attrs: ~[ast::Attribute] = ~[]; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); match self.token { token::INTERPOLATED(token::NtAttr(..)) => { attrs.push(self.parse_attribute(false)); } token::POUND => { if self.look_ahead(1, |t| *t != token::LBRACKET) { break; } attrs.push(self.parse_attribute(false)); } token::DOC_COMMENT(s) => { let attr = ::attr::mk_sugared_doc_attr( self.id_to_interned_str(s), self.span.lo, self.span.hi ); if attr.node.style != ast::AttrOuter { self.fatal("expected outer comment"); } attrs.push(attr); self.bump(); } _ => break } } return attrs; } // matches attribute = # [ meta_item ] // // if permit_inner is true, then a trailing `;` indicates an inner // attribute fn parse_attribute(&mut self, permit_inner: bool) -> ast::Attribute { debug!("parse_attributes: permit_inner={:?} self.token={:?}", permit_inner, self.token); let (span, value) = match self.token { INTERPOLATED(token::NtAttr(attr)) => { assert!(attr.node.style == ast::AttrOuter); self.bump(); (attr.span, attr.node.value) } token::POUND => { let lo = self.span.lo; self.bump(); self.expect(&token::LBRACKET); let meta_item = self.parse_meta_item(); self.expect(&token::RBRACKET); let hi = self.span.hi; (mk_sp(lo, hi), meta_item) } _ => { let token_str = self.this_token_to_str(); self.fatal(format!("expected `\\#` but found `{}`", token_str)); } }; let style = if permit_inner && self.token == token::SEMI { self.bump(); ast::AttrInner } else { ast::AttrOuter }; return Spanned { span: span, node: ast::Attribute_ { style: style, value: value, is_sugared_doc: false } }; } // Parse attributes that appear after the opening of an item, each // terminated by a semicolon. In addition to a vector of inner attributes, // this function also returns a vector that may contain the first outer // attribute of the next item (since we can't know whether the attribute // 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(&mut self) -> (~[ast::Attribute], ~[ast::Attribute]) { let mut inner_attrs: ~[ast::Attribute] = ~[]; let mut next_outer_attrs: ~[ast::Attribute] = ~[]; loop { let attr = match self.token { token::INTERPOLATED(token::NtAttr(..)) => { self.parse_attribute(true) } token::POUND => { if self.look_ahead(1, |t| *t != token::LBRACKET) { // This is an extension break; } self.parse_attribute(true) } token::DOC_COMMENT(s) => { self.bump(); ::attr::mk_sugared_doc_attr(self.id_to_interned_str(s), self.span.lo, self.span.hi) } _ => { break; } }; if attr.node.style == ast::AttrInner { inner_attrs.push(attr); } else { next_outer_attrs.push(attr); break; } } (inner_attrs, next_outer_attrs) } // matches meta_item = IDENT // | IDENT = lit // | IDENT meta_seq fn parse_meta_item(&mut self) -> @ast::MetaItem { let lo = self.span.lo; let ident = self.parse_ident(); let name = self.id_to_interned_str(ident); match self.token { token::EQ => { self.bump(); let lit = self.parse_lit(); // FIXME #623 Non-string meta items are not serialized correctly; // just forbid them for now match lit.node { ast::LitStr(..) => {} _ => { self.span_err( lit.span, "non-string literals are not allowed in meta-items"); } } let hi = self.span.hi; @spanned(lo, hi, ast::MetaNameValue(name, lit)) } token::LPAREN => { let inner_items = self.parse_meta_seq(); let hi = self.span.hi; @spanned(lo, hi, ast::MetaList(name, inner_items)) } _ => { let hi = self.last_span.hi; @spanned(lo, hi, ast::MetaWord(name)) } } } // matches meta_seq = ( COMMASEP(meta_item) ) fn parse_meta_seq(&mut self) -> ~[@ast::MetaItem] { self.parse_seq(&token::LPAREN, &token::RPAREN, seq_sep_trailing_disallowed(token::COMMA), |p| p.parse_meta_item()).node } fn parse_optional_meta(&mut self) -> ~[@ast::MetaItem] { match self.token { token::LPAREN => self.parse_meta_seq(), _ => ~[] } } }