Add a special macro nonterminal $crate
This commit is contained in:
parent
5e5924b799
commit
ad7c647773
@ -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(..) |
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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)]
|
||||
|
@ -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),
|
||||
|
24
src/test/auxiliary/macro_crate_nonterminal.rs
Normal file
24
src/test/auxiliary/macro_crate_nonterminal.rs
Normal 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);
|
||||
}
|
22
src/test/run-pass/macro-crate-nonterminal-renamed.rs
Normal file
22
src/test/run-pass/macro-crate-nonterminal-renamed.rs
Normal 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);
|
||||
}
|
22
src/test/run-pass/macro-crate-nonterminal.rs
Normal file
22
src/test/run-pass/macro-crate-nonterminal.rs
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user