Add a special macro nonterminal $crate

This commit is contained in:
Keegan McAllister 2014-09-15 18:27:28 -07:00
parent 5e5924b799
commit ad7c647773
12 changed files with 153 additions and 9 deletions

View File

@ -166,6 +166,9 @@ fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader,
}
}
// Special macro vars are like keywords
token::SpecialVarNt(_) => "kw-2",
token::Lifetime(..) => "lifetime",
token::DocComment(..) => "doccomment",
token::Underscore | token::Eof | token::Interpolated(..) |

View File

@ -884,6 +884,7 @@ impl TokenTree {
match *self {
TtToken(_, token::DocComment(_)) => 2,
TtToken(_, token::SubstNt(..)) => 2,
TtToken(_, token::SpecialVarNt(..)) => 2,
TtToken(_, token::MatchNt(..)) => 3,
TtDelimited(_, ref delimed) => {
delimed.tts.len() + 2
@ -925,6 +926,12 @@ impl TokenTree {
TtToken(sp, token::Ident(name, name_st))];
v[index]
}
(&TtToken(sp, token::SpecialVarNt(var)), _) => {
let v = [TtToken(sp, token::Dollar),
TtToken(sp, token::Ident(token::str_to_ident(var.as_str()),
token::Plain))];
v[index]
}
(&TtToken(sp, token::MatchNt(name, kind, name_st, kind_st)), _) => {
let v = [TtToken(sp, token::SubstNt(name, name_st)),
TtToken(sp, token::Colon),

View File

@ -432,7 +432,7 @@ pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
}
let mut new_items = match it.node {
ast::ItemMac(..) => expand_item_mac(it, fld),
ast::ItemMac(..) => expand_item_mac(it, None, fld),
ast::ItemMod(_) | ast::ItemForeignMod(_) => {
let valid_ident =
it.ident.name != parse::token::special_idents::invalid.name;
@ -529,8 +529,9 @@ fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
pub fn expand_item_mac(it: P<ast::Item>, fld: &mut MacroExpander)
-> SmallVector<P<ast::Item>> {
pub fn expand_item_mac(it: P<ast::Item>,
imported_from: Option<ast::Ident>,
fld: &mut MacroExpander) -> SmallVector<P<ast::Item>> {
let (extname, path_span, tts) = match it.node {
ItemMac(codemap::Spanned {
node: MacInvocTT(ref pth, ref tts, _),
@ -611,7 +612,8 @@ pub fn expand_item_mac(it: P<ast::Item>, fld: &mut MacroExpander)
});
// DON'T mark before expansion.
let MacroDef { name, ext }
= macro_rules::add_new_extension(fld.cx, it.span, it.ident, tts);
= macro_rules::add_new_extension(fld.cx, it.span, it.ident,
imported_from, tts);
fld.cx.syntax_env.insert(intern(name.as_slice()), ext);
if attr::contains_name(it.attrs.as_slice(), "macro_export") {
@ -1190,7 +1192,7 @@ pub fn expand_crate(parse_sess: &parse::ParseSess,
expander.cx.cfg(),
expander.cx.parse_sess())
.expect("expected a serialized item");
expand_item_mac(item, &mut expander);
expand_item_mac(item, Some(crate_name), &mut expander);
}
}

View File

@ -110,6 +110,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
struct MacroRulesMacroExpander {
name: Ident,
imported_from: Option<Ident>,
lhses: Vec<Rc<NamedMatch>>,
rhses: Vec<Rc<NamedMatch>>,
}
@ -123,6 +124,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
generic_extension(cx,
sp,
self.name,
self.imported_from,
arg,
self.lhses[],
self.rhses[])
@ -133,6 +135,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
fn generic_extension<'cx>(cx: &'cx ExtCtxt,
sp: Span,
name: Ident,
imported_from: Option<Ident>,
arg: &[ast::TokenTree],
lhses: &[Rc<NamedMatch>],
rhses: &[Rc<NamedMatch>])
@ -156,6 +159,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
};
// `None` is because we're not interpolating
let mut arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
None,
None,
arg.iter()
.map(|x| (*x).clone())
@ -177,6 +181,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
// rhs has holes ( `$id` and `$(...)` that need filled)
let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
Some(named_matches),
imported_from,
rhs);
let p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
// Let the context choose how to interpret the result.
@ -209,6 +214,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt,
sp: Span,
name: Ident,
imported_from: Option<Ident>,
arg: Vec<ast::TokenTree> )
-> MacroDef {
@ -246,6 +252,7 @@ pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt,
// Parse the macro_rules! invocation (`none` is for no interpolations):
let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
None,
None,
arg.clone());
let argument_map = parse_or_else(cx.parse_sess(),
@ -266,6 +273,7 @@ pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt,
let exp = box MacroRulesMacroExpander {
name: name,
imported_from: imported_from,
lhses: lhses,
rhses: rhses,
};

View File

@ -15,7 +15,7 @@ use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
use parse::token::{Eof, DocComment, Interpolated, MatchNt, SubstNt};
use parse::token::{Token, NtIdent};
use parse::token::{Token, NtIdent, SpecialMacroVar};
use parse::token;
use parse::lexer::TokenAndSpan;
@ -39,6 +39,10 @@ pub struct TtReader<'a> {
stack: Vec<TtFrame>,
/* for MBE-style macro transcription */
interpolations: HashMap<Ident, Rc<NamedMatch>>,
imported_from: Option<Ident>,
// Some => return imported_from as the next token
crate_name_next: Option<Span>,
repeat_idx: Vec<uint>,
repeat_len: Vec<uint>,
/* cached: */
@ -53,6 +57,7 @@ pub struct TtReader<'a> {
/// should) be none.
pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
imported_from: Option<Ident>,
src: Vec<ast::TokenTree> )
-> TtReader<'a> {
let mut r = TtReader {
@ -71,6 +76,8 @@ pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
None => HashMap::new(),
Some(x) => x,
},
imported_from: imported_from,
crate_name_next: None,
repeat_idx: Vec::new(),
repeat_len: Vec::new(),
desugar_doc_comments: false,
@ -162,6 +169,14 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
sp: r.cur_span.clone(),
};
loop {
match r.crate_name_next.take() {
None => (),
Some(sp) => {
r.cur_span = sp;
r.cur_tok = token::Ident(r.imported_from.unwrap(), token::Plain);
return ret_val;
},
}
let should_pop = match r.stack.last() {
None => {
assert_eq!(ret_val.tok, token::Eof);
@ -307,6 +322,18 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
sep: None
});
}
TtToken(sp, token::SpecialVarNt(SpecialMacroVar::CrateMacroVar)) => {
r.stack.last_mut().unwrap().idx += 1;
if r.imported_from.is_some() {
r.cur_span = sp;
r.cur_tok = token::ModSep;
r.crate_name_next = Some(sp);
return ret_val;
}
// otherwise emit nothing and proceed to the next token
}
TtToken(sp, tok) => {
r.cur_span = sp;
r.cur_tok = tok;

View File

@ -291,7 +291,7 @@ pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
pub fn tts_to_parser<'a>(sess: &'a ParseSess,
tts: Vec<ast::TokenTree>,
cfg: ast::CrateConfig) -> Parser<'a> {
let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, tts);
let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
Parser::new(sess, cfg, box trdr)
}

View File

@ -75,8 +75,8 @@ use parse::classify;
use parse::common::{SeqSep, seq_sep_none, seq_sep_trailing_allowed};
use parse::lexer::{Reader, TokenAndSpan};
use parse::obsolete::*;
use parse::token::{self, MatchNt, SubstNt, InternedString};
use parse::token::{keywords, special_idents};
use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
use parse::token::{keywords, special_idents, SpecialMacroVar};
use parse::{new_sub_parser_from_file, ParseSess};
use print::pprust;
use ptr::P;
@ -2747,6 +2747,9 @@ impl<'a> Parser<'a> {
op: repeat,
num_captures: name_num
}))
} else if p.token.is_keyword_allow_following_colon(keywords::Crate) {
p.bump();
TtToken(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar))
} else {
// A nonterminal that matches or not
let namep = match p.token { token::Ident(_, p) => p, _ => token::Plain };

View File

@ -61,6 +61,21 @@ pub enum IdentStyle {
Plain,
}
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Show, Copy)]
pub enum SpecialMacroVar {
/// `$crate` will be filled in with the name of the crate a macro was
/// imported from, if any.
CrateMacroVar,
}
impl SpecialMacroVar {
pub fn as_str(self) -> &'static str {
match self {
SpecialMacroVar::CrateMacroVar => "crate",
}
}
}
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Show, Copy)]
pub enum Lit {
Byte(ast::Name),
@ -143,6 +158,8 @@ pub enum Token {
// In right-hand-sides of MBE macros:
/// A syntactic variable that will be filled in by macro expansion.
SubstNt(ast::Ident, IdentStyle),
/// A macro variable with special meaning.
SpecialVarNt(SpecialMacroVar),
// Junk. These carry no data because we don't really care about the data
// they *would* carry, and don't really want to allocate a new ident for
@ -265,6 +282,13 @@ impl Token {
}
}
pub fn is_keyword_allow_following_colon(&self, kw: keywords::Keyword) -> bool {
match *self {
Ident(sid, _) => { kw.to_name() == sid.name }
_ => { false }
}
}
/// Returns `true` if the token is either a special identifier, or a strict
/// or reserved keyword.
#[allow(non_upper_case_globals)]

View File

@ -272,6 +272,8 @@ pub fn token_to_string(tok: &Token) -> String {
token::Comment => "/* */".to_string(),
token::Shebang(s) => format!("/* shebang: {}*/", s.as_str()),
token::SpecialVarNt(var) => format!("${}", var.as_str()),
token::Interpolated(ref nt) => match *nt {
token::NtExpr(ref e) => expr_to_string(&**e),
token::NtMeta(ref e) => meta_item_to_string(&**e),

View File

@ -0,0 +1,24 @@
// Copyright 2014 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.
#![feature(macro_rules)]
pub fn increment(x: uint) -> uint {
x + 1
}
#[macro_export]
macro_rules! increment {
($x:expr) => ($crate::increment($x))
}
pub fn check_local() {
assert_eq!(increment!(3), 4);
}

View File

@ -0,0 +1,22 @@
// Copyright 2014 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.
// aux-build:macro_crate_nonterminal.rs
// ignore-stage1
#![feature(phase)]
#[phase(plugin, link)]
extern crate "macro_crate_nonterminal" as new_name;
pub fn main() {
new_name::check_local();
assert_eq!(increment!(5), 6);
}

View File

@ -0,0 +1,22 @@
// Copyright 2014 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.
// aux-build:macro_crate_nonterminal.rs
// ignore-stage1
#![feature(phase)]
#[phase(plugin, link)]
extern crate macro_crate_nonterminal;
pub fn main() {
macro_crate_nonterminal::check_local();
assert_eq!(increment!(5), 6);
}