diff options
| author | Steven Fackler <sfackler@gmail.com> | 2013-12-25 11:10:33 -0700 |
|---|---|---|
| committer | Steven Fackler <sfackler@palantir.com> | 2014-01-16 15:01:48 -0800 |
| commit | 328b47d83720f011cb1c95685c3e7df8195efbd3 (patch) | |
| tree | b370ba6b6fa4ef5d88ca4b1f1e7739be81381f10 /src/libsyntax/ext | |
| parent | a5ed0c58cb9f38af940403c34e283b68c89f5aa2 (diff) | |
| download | rust-328b47d83720f011cb1c95685c3e7df8195efbd3.tar.gz rust-328b47d83720f011cb1c95685c3e7df8195efbd3.zip | |
Load macros from external modules
Diffstat (limited to 'src/libsyntax/ext')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 111 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/generic.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 110 | ||||
| -rw-r--r-- | src/libsyntax/ext/format.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/quote.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/registrar.rs | 60 |
7 files changed, 216 insertions, 75 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 8515c3aba50..ef6d154c651 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -20,6 +20,7 @@ use parse::token::{ident_to_str, intern, str_to_ident}; use util::small_vector::SmallVector; use std::hashmap::HashMap; +use std::unstable::dynamic_lib::DynamicLibrary; // new-style macro! tt code: // @@ -120,6 +121,9 @@ pub type SyntaxExpanderTTItemFun = pub type SyntaxExpanderTTItemFunNoCtxt = fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree]) -> MacResult; +pub type MacroCrateRegistrationFun = + extern "Rust" fn(|ast::Name, SyntaxExtension|); + pub trait AnyMacro { fn make_expr(&self) -> @ast::Expr; fn make_items(&self) -> SmallVector<@ast::Item>; @@ -151,24 +155,21 @@ pub enum SyntaxExtension { IdentTT(~SyntaxExpanderTTItemTrait:'static, Option<Span>), } - -// The SyntaxEnv is the environment that's threaded through the expansion -// of macros. It contains bindings for macros, and also a special binding -// for " block" (not a legal identifier) that maps to a BlockInfo -pub type SyntaxEnv = MapChain<Name, SyntaxExtension>; - pub struct BlockInfo { // should macros escape from this scope? macros_escape : bool, // what are the pending renames? - pending_renames : RenameList + pending_renames : RenameList, + // references for crates loaded in this scope + macro_crates: ~[DynamicLibrary], } impl BlockInfo { pub fn new() -> BlockInfo { BlockInfo { macros_escape: false, - pending_renames: ~[] + pending_renames: ~[], + macro_crates: ~[], } } } @@ -189,7 +190,7 @@ pub fn syntax_expander_table() -> SyntaxEnv { None) } - let mut syntax_expanders = MapChain::new(); + let mut syntax_expanders = SyntaxEnv::new(); syntax_expanders.insert(intern(&"macro_rules"), IdentTT(~SyntaxExpanderTTItem { expander: SyntaxExpanderTTItemExpanderWithContext( @@ -280,25 +281,38 @@ pub fn syntax_expander_table() -> SyntaxEnv { syntax_expanders } +pub struct MacroCrate { + lib: Option<Path>, + cnum: ast::CrateNum, +} + +pub trait CrateLoader { + fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate; + fn get_exported_macros(&mut self, crate_num: ast::CrateNum) -> ~[@ast::Item]; + fn get_registrar_symbol(&mut self, crate_num: ast::CrateNum) -> Option<~str>; +} + // One of these is made during expansion and incrementally updated as we go; // when a macro expansion occurs, the resulting nodes have the backtrace() // -> expn_info of their expansion context stored into their span. -pub struct ExtCtxt { +pub struct ExtCtxt<'a> { parse_sess: @parse::ParseSess, cfg: ast::CrateConfig, backtrace: Option<@ExpnInfo>, + loader: &'a mut CrateLoader, mod_path: ~[ast::Ident], trace_mac: bool } -impl ExtCtxt { - pub fn new(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig) - -> ExtCtxt { +impl<'a> ExtCtxt<'a> { + pub fn new<'a>(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig, + loader: &'a mut CrateLoader) -> ExtCtxt<'a> { ExtCtxt { parse_sess: parse_sess, cfg: cfg, backtrace: None, + loader: loader, mod_path: ~[], trace_mac: false } @@ -456,20 +470,27 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt, // able to refer to a macro that was added to an enclosing // scope lexically later than the deeper scope. -// Only generic to make it easy to test -struct MapChainFrame<K, V> { +struct MapChainFrame { info: BlockInfo, - map: HashMap<K, V>, + map: HashMap<Name, SyntaxExtension>, +} + +#[unsafe_destructor] +impl Drop for MapChainFrame { + fn drop(&mut self) { + // make sure that syntax extension dtors run before we drop the libs + self.map.clear(); + } } // Only generic to make it easy to test -pub struct MapChain<K, V> { - priv chain: ~[MapChainFrame<K, V>], +pub struct SyntaxEnv { + priv chain: ~[MapChainFrame], } -impl<K: Hash+Eq, V> MapChain<K, V> { - pub fn new() -> MapChain<K, V> { - let mut map = MapChain { chain: ~[] }; +impl SyntaxEnv { + pub fn new() -> SyntaxEnv { + let mut map = SyntaxEnv { chain: ~[] }; map.push_frame(); map } @@ -486,7 +507,7 @@ impl<K: Hash+Eq, V> MapChain<K, V> { self.chain.pop(); } - fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame<K, V> { + fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame { for (i, frame) in self.chain.mut_iter().enumerate().invert() { if !frame.info.macros_escape || i == 0 { return frame @@ -495,7 +516,7 @@ impl<K: Hash+Eq, V> MapChain<K, V> { unreachable!() } - pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> { + pub fn find<'a>(&'a self, k: &Name) -> Option<&'a SyntaxExtension> { for frame in self.chain.iter().invert() { match frame.map.find(k) { Some(v) => return Some(v), @@ -505,49 +526,15 @@ impl<K: Hash+Eq, V> MapChain<K, V> { None } - pub fn insert(&mut self, k: K, v: V) { + pub fn insert(&mut self, k: Name, v: SyntaxExtension) { self.find_escape_frame().map.insert(k, v); } - pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo { - &mut self.chain[self.chain.len()-1].info + pub fn insert_macro_crate(&mut self, lib: DynamicLibrary) { + self.find_escape_frame().info.macro_crates.push(lib); } -} -#[cfg(test)] -mod test { - use super::MapChain; - - #[test] - fn testenv() { - let mut m = MapChain::new(); - let (a,b,c,d) = ("a", "b", "c", "d"); - m.insert(1, a); - assert_eq!(Some(&a), m.find(&1)); - - m.push_frame(); - m.info().macros_escape = true; - m.insert(2, b); - assert_eq!(Some(&a), m.find(&1)); - assert_eq!(Some(&b), m.find(&2)); - m.pop_frame(); - - assert_eq!(Some(&a), m.find(&1)); - assert_eq!(Some(&b), m.find(&2)); - - m.push_frame(); - m.push_frame(); - m.info().macros_escape = true; - m.insert(3, c); - assert_eq!(Some(&c), m.find(&3)); - m.pop_frame(); - assert_eq!(Some(&c), m.find(&3)); - m.pop_frame(); - assert_eq!(None, m.find(&3)); - - m.push_frame(); - m.insert(4, d); - m.pop_frame(); - assert_eq!(None, m.find(&4)); + pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo { + &mut self.chain[self.chain.len()-1].info } } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 85cda0bd1ae..d702ee3ead4 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -236,7 +236,7 @@ pub trait AstBuilder { vis: ast::Visibility, path: ~[ast::Ident]) -> ast::ViewItem; } -impl AstBuilder for ExtCtxt { +impl<'a> AstBuilder for ExtCtxt<'a> { fn path(&self, span: Span, strs: ~[ast::Ident]) -> ast::Path { self.path_all(span, false, strs, opt_vec::Empty, ~[]) } @@ -896,7 +896,7 @@ impl AstBuilder for ExtCtxt { } struct Duplicator<'a> { - cx: &'a ExtCtxt, + cx: &'a ExtCtxt<'a>, } impl<'a> Folder for Duplicator<'a> { diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 826d5381d47..202746acddc 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -190,7 +190,7 @@ mod ty; pub struct TraitDef<'a> { /// The extension context - cx: &'a ExtCtxt, + cx: &'a ExtCtxt<'a>, /// The span for the current #[deriving(Foo)] header. span: Span, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 303277afbe8..b1b38d6dc90 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -30,6 +30,7 @@ use visit::Visitor; use util::small_vector::SmallVector; use std::vec; +use std::unstable::dynamic_lib::DynamicLibrary; pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { match e.node { @@ -365,13 +366,79 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander) // yikes... no idea how to apply the mark to this. I'm afraid // we're going to have to wait-and-see on this one. fld.extsbox.insert(intern(name), ext); - SmallVector::zero() + if attr::contains_name(it.attrs, "macro_export") { + SmallVector::one(it) + } else { + SmallVector::zero() + } } }; fld.cx.bt_pop(); return items; } +// load macros from syntax-phase crates +pub fn expand_view_item(vi: &ast::ViewItem, + fld: &mut MacroExpander) + -> ast::ViewItem { + let should_load = vi.attrs.iter().any(|attr| { + "phase" == attr.name() && + attr.meta_item_list().map_or(false, |phases| { + attr::contains_name(phases, "syntax") + }) + }); + + if should_load { + load_extern_macros(vi, fld); + } + + noop_fold_view_item(vi, fld) +} + +fn load_extern_macros(crate: &ast::ViewItem, fld: &mut MacroExpander) { + let MacroCrate { lib, cnum } = fld.cx.loader.load_crate(crate); + + let exported_macros = fld.cx.loader.get_exported_macros(cnum); + for &it in exported_macros.iter() { + expand_item_mac(it, fld); + } + + let path = match lib { + Some(path) => path, + None => return + }; + // Make sure the path contains a / or the linker will search for it. + // If path is already absolute this is a no-op. + let path = Path::new(".").join(path); + + let registrar = match fld.cx.loader.get_registrar_symbol(cnum) { + Some(registrar) => registrar, + None => return + }; + + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(err) => fld.cx.span_fatal(crate.span, err) + }; + + unsafe { + let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) { + Ok(registrar) => registrar, + Err(err) => fld.cx.span_fatal(crate.span, err) + }; + registrar(|name, extension| { + let extension = match extension { + NormalTT(ext, _) => NormalTT(ext, Some(crate.span)), + IdentTT(ext, _) => IdentTT(ext, Some(crate.span)), + ItemDecorator(ext) => ItemDecorator(ext), + }; + fld.extsbox.insert(name, extension); + }); + } + + fld.extsbox.insert_macro_crate(lib); +} + // expand a stmt pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> { // why the copying here and not in expand_expr? @@ -878,7 +945,7 @@ pub fn inject_std_macros(parse_sess: @parse::ParseSess, pub struct MacroExpander<'a> { extsbox: SyntaxEnv, - cx: &'a mut ExtCtxt, + cx: &'a mut ExtCtxt<'a>, } impl<'a> Folder for MacroExpander<'a> { @@ -894,6 +961,10 @@ impl<'a> Folder for MacroExpander<'a> { expand_item(item, self) } + fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem { + expand_view_item(vi, self) + } + fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> { expand_stmt(stmt, self) } @@ -908,9 +979,10 @@ impl<'a> Folder for MacroExpander<'a> { } pub fn expand_crate(parse_sess: @parse::ParseSess, + loader: &mut CrateLoader, cfg: ast::CrateConfig, c: Crate) -> Crate { - let mut cx = ExtCtxt::new(parse_sess, cfg.clone()); + let mut cx = ExtCtxt::new(parse_sess, cfg.clone(), loader); let mut expander = MacroExpander { extsbox: syntax_expander_table(), cx: &mut cx, @@ -1076,6 +1148,7 @@ mod test { use codemap::Spanned; use fold; use fold::*; + use ext::base::{CrateLoader, MacroCrate}; use parse; use parse::token::{fresh_mark, gensym, intern, ident_to_str}; use parse::token; @@ -1119,6 +1192,22 @@ mod test { } } + struct ErrLoader; + + impl CrateLoader for ErrLoader { + fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate { + fail!("lolwut") + } + + fn get_exported_macros(&mut self, _: ast::CrateNum) -> ~[@ast::Item] { + fail!("lolwut") + } + + fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> { + fail!("lolwut") + } + } + // make sure that fail! is present #[test] fn fail_exists_test () { let src = @"fn main() { fail!(\"something appropriately gloomy\");}"; @@ -1129,7 +1218,8 @@ mod test { ~[],sess); let crate_ast = inject_std_macros(sess, ~[], crate_ast); // don't bother with striping, doesn't affect fail!. - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // these following tests are quite fragile, in that they don't test what @@ -1146,7 +1236,8 @@ mod test { src, ~[],sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // make sure that macros can leave scope for modules @@ -1160,7 +1251,8 @@ mod test { src, ~[],sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // macro_escape modules shouldn't cause macros to leave scope @@ -1173,7 +1265,8 @@ mod test { src, ~[], sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } #[test] fn std_macros_must_parse () { @@ -1281,7 +1374,8 @@ mod test { fn expand_crate_str(crate_str: @str) -> ast::Crate { let (crate_ast,ps) = string_to_crate_and_sess(crate_str); // the cfg argument actually does matter, here... - expand_crate(ps,~[],crate_ast) + let mut loader = ErrLoader; + expand_crate(ps,&mut loader,~[],crate_ast) } //fn expand_and_resolve(crate_str: @str) -> ast::crate { diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index a4b8dd78403..9ae13ddeb02 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -34,7 +34,7 @@ enum Position { } struct Context<'a> { - ecx: &'a mut ExtCtxt, + ecx: &'a mut ExtCtxt<'a>, fmtsp: Span, // Parsed argument expressions and the types that we've found so far for diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index e66e394d639..27f41356a4b 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -243,7 +243,7 @@ pub mod rt { fn parse_tts(&self, s: @str) -> ~[ast::TokenTree]; } - impl ExtParseUtils for ExtCtxt { + impl<'a> ExtParseUtils for ExtCtxt<'a> { fn parse_item(&self, s: @str) -> @ast::Item { let res = parse::parse_item_from_source_str( diff --git a/src/libsyntax/ext/registrar.rs b/src/libsyntax/ext/registrar.rs new file mode 100644 index 00000000000..265dd91d7f4 --- /dev/null +++ b/src/libsyntax/ext/registrar.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2013 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 attr; +use codemap::Span; +use diagnostic; +use visit; +use visit::Visitor; + +struct MacroRegistrarContext { + registrars: ~[(ast::NodeId, Span)], +} + +impl Visitor<()> for MacroRegistrarContext { + fn visit_item(&mut self, item: &ast::Item, _: ()) { + match item.node { + ast::ItemFn(..) => { + if attr::contains_name(item.attrs, "macro_registrar") { + self.registrars.push((item.id, item.span)); + } + } + _ => {} + } + + visit::walk_item(self, item, ()); + } +} + +pub fn find_macro_registrar(diagnostic: @diagnostic::SpanHandler, + crate: &ast::Crate) -> Option<ast::DefId> { + let mut ctx = MacroRegistrarContext { registrars: ~[] }; + visit::walk_crate(&mut ctx, crate, ()); + + match ctx.registrars.len() { + 0 => None, + 1 => { + let (node_id, _) = ctx.registrars.pop(); + Some(ast::DefId { + crate: ast::LOCAL_CRATE, + node: node_id + }) + }, + _ => { + diagnostic.handler().err("Multiple macro registration functions found"); + for &(_, span) in ctx.registrars.iter() { + diagnostic.span_note(span, "one is here"); + } + diagnostic.handler().abort_if_errors(); + unreachable!(); + } + } +} |
