Skip to content

Commit

Permalink
Adds a ProcMacro form of syntax extension
Browse files Browse the repository at this point in the history
This commit adds syntax extension forms matching the types for procedural macros 2.0 (RFC #1566), these still require the usual syntax extension boiler plate, but this is a first step towards proper implementation and should be useful for macros 1.1 stuff too.

Supports both attribute-like and function-like macros.
  • Loading branch information
nrc committed Sep 21, 2016
1 parent c772948 commit 6a2d2c9
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 18 deletions.
2 changes: 2 additions & 0 deletions src/librustc_plugin/registry.rs
Expand Up @@ -111,6 +111,8 @@ impl<'a> Registry<'a> {
}
MultiDecorator(ext) => MultiDecorator(ext),
MultiModifier(ext) => MultiModifier(ext),
SyntaxExtension::ProcMacro(ext) => SyntaxExtension::ProcMacro(ext),
SyntaxExtension::AttrProcMacro(ext) => SyntaxExtension::AttrProcMacro(ext),
}));
}

Expand Down
203 changes: 195 additions & 8 deletions src/libsyntax/ext/base.rs
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::SyntaxExtension::*;
pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT, MacroRulesTT};

use ast::{self, Attribute, Name, PatKind};
use attr::HasAttrs;
Expand All @@ -19,7 +19,7 @@ use ext::expand::{self, Invocation, Expansion};
use ext::hygiene::Mark;
use ext::tt::macro_rules;
use parse;
use parse::parser;
use parse::parser::{self, Parser};
use parse::token;
use parse::token::{InternedString, str_to_ident};
use ptr::P;
Expand All @@ -31,7 +31,8 @@ use feature_gate;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use tokenstream;
use std::default::Default;
use tokenstream::{self, TokenStream};


#[derive(Debug,Clone)]
Expand Down Expand Up @@ -60,6 +61,14 @@ impl HasAttrs for Annotatable {
}

impl Annotatable {
pub fn span(&self) -> Span {
match *self {
Annotatable::Item(ref item) => item.span,
Annotatable::TraitItem(ref trait_item) => trait_item.span,
Annotatable::ImplItem(ref impl_item) => impl_item.span,
}
}

pub fn expect_item(self) -> P<ast::Item> {
match self {
Annotatable::Item(i) => i,
Expand Down Expand Up @@ -146,6 +155,173 @@ impl Into<Vec<Annotatable>> for Annotatable {
}
}

pub trait ProcMacro {
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
ts: TokenStream)
-> Box<MacResult+'cx>;
}

impl<F> ProcMacro for F
where F: Fn(TokenStream) -> TokenStream
{
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
ts: TokenStream)
-> Box<MacResult+'cx> {
let result = (*self)(ts);
// FIXME setup implicit context in TLS before calling self.
let parser = ecx.new_parser_from_tts(&result.to_tts());
Box::new(TokResult { parser: parser, span: span })
}
}

pub trait AttrProcMacro {
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
annotation: TokenStream,
annotated: TokenStream)
-> Box<MacResult+'cx>;
}

impl<F> AttrProcMacro for F
where F: Fn(TokenStream, TokenStream) -> TokenStream
{
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
annotation: TokenStream,
annotated: TokenStream)
-> Box<MacResult+'cx> {
// FIXME setup implicit context in TLS before calling self.
let parser = ecx.new_parser_from_tts(&(*self)(annotation, annotated).to_tts());
Box::new(TokResult { parser: parser, span: span })
}
}

struct TokResult<'a> {
parser: Parser<'a>,
span: Span,
}

impl<'a> MacResult for TokResult<'a> {
fn make_items(mut self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
if self.parser.sess.span_diagnostic.has_errors() {
return None;
}

let mut items = SmallVector::zero();
loop {
match self.parser.parse_item() {
Ok(Some(item)) => {
// FIXME better span info.
let mut item = item.unwrap();
item.span = self.span;
items.push(P(item));
}
Ok(None) => {
return Some(items);
}
Err(mut e) => {
e.emit();
return None;
}
}
}
}

fn make_impl_items(mut self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> {
let mut items = SmallVector::zero();
loop {
match self.parser.parse_impl_item() {
Ok(mut item) => {
// FIXME better span info.
item.span = self.span;
items.push(item);

return Some(items);
}
Err(mut e) => {
e.emit();
return None;
}
}
}
}

fn make_trait_items(mut self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
let mut items = SmallVector::zero();
loop {
match self.parser.parse_trait_item() {
Ok(mut item) => {
// FIXME better span info.
item.span = self.span;
items.push(item);

return Some(items);
}
Err(mut e) => {
e.emit();
return None;
}
}
}
}

fn make_expr(mut self: Box<Self>) -> Option<P<ast::Expr>> {
match self.parser.parse_expr() {
Ok(e) => Some(e),
Err(mut e) => {
e.emit();
return None;
}
}
}

fn make_pat(mut self: Box<Self>) -> Option<P<ast::Pat>> {
match self.parser.parse_pat() {
Ok(e) => Some(e),
Err(mut e) => {
e.emit();
return None;
}
}
}

fn make_stmts(mut self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
let mut stmts = SmallVector::zero();
loop {
if self.parser.token == token::Eof {
return Some(stmts);
}
match self.parser.parse_full_stmt(true) {
Ok(Some(mut stmt)) => {
stmt.span = self.span;
stmts.push(stmt);
}
Ok(None) => { /* continue */ }
Err(mut e) => {
e.emit();
return None;
}
}
}
}

fn make_ty(mut self: Box<Self>) -> Option<P<ast::Ty>> {
match self.parser.parse_ty() {
Ok(e) => Some(e),
Err(mut e) => {
e.emit();
return None;
}
}
}
}

/// Represents a thing that maps token trees to Macro Results
pub trait TTMacroExpander {
fn expand<'cx>(&self,
Expand Down Expand Up @@ -439,24 +615,35 @@ pub enum SyntaxExtension {
/// based upon it.
///
/// `#[derive(...)]` is a `MultiItemDecorator`.
MultiDecorator(Box<MultiItemDecorator + 'static>),
///
/// Prefer ProcMacro or MultiModifier since they are more flexible.
MultiDecorator(Box<MultiItemDecorator>),

/// A syntax extension that is attached to an item and modifies it
/// in-place. More flexible version than Modifier.
MultiModifier(Box<MultiItemModifier + 'static>),
/// in-place. Also allows decoration, i.e., creating new items.
MultiModifier(Box<MultiItemModifier>),

/// A function-like procedural macro. TokenStream -> TokenStream.
ProcMacro(Box<ProcMacro>),

/// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
/// The first TokenSteam is the attribute, the second is the annotated item.
/// Allows modification of the input items and adding new items, similar to
/// MultiModifier, but uses TokenStreams, rather than AST nodes.
AttrProcMacro(Box<AttrProcMacro>),

/// A normal, function-like syntax extension.
///
/// `bytes!` is a `NormalTT`.
///
/// The `bool` dictates whether the contents of the macro can
/// directly use `#[unstable]` things (true == yes).
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
NormalTT(Box<TTMacroExpander>, Option<Span>, bool),

/// A function-like syntax extension that has an extra ident before
/// the block.
///
IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool),
IdentTT(Box<IdentMacroExpander>, Option<Span>, bool),
}

pub type NamedSyntaxExtension = (Name, SyntaxExtension);
Expand Down
53 changes: 51 additions & 2 deletions src/libsyntax/ext/expand.rs
Expand Up @@ -22,8 +22,9 @@ use feature_gate::{self, Features};
use fold;
use fold::*;
use parse::token::{intern, keywords};
use parse::span_to_tts;
use ptr::P;
use tokenstream::TokenTree;
use tokenstream::{TokenTree, TokenStream};
use util::small_vector::SmallVector;
use visit::Visitor;

Expand Down Expand Up @@ -308,6 +309,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
items.push(item);
kind.expect_from_annotatables(items)
}
SyntaxExtension::AttrProcMacro(ref mac) => {
let attr_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
attr.span));
let item_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
item.span()));
let result = mac.expand(self.cx, attr.span, attr_toks, item_toks);
let items = match item {
Annotatable::Item(_) => result.make_items()
.unwrap_or(SmallVector::zero())
.into_iter()
.map(|i| Annotatable::Item(i))
.collect(),
Annotatable::TraitItem(_) => result.make_trait_items()
.unwrap_or(SmallVector::zero())
.into_iter()
.map(|i| Annotatable::TraitItem(P(i)))
.collect(),
Annotatable::ImplItem(_) => result.make_impl_items()
.unwrap_or(SmallVector::zero())
.into_iter()
.map(|i| Annotatable::ImplItem(P(i)))
.collect(),
};
kind.expect_from_annotatables(items)
}
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -377,11 +403,34 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
kind.make_from(expander.expand(self.cx, span, ident, marked_tts, attrs))
}

MultiDecorator(..) | MultiModifier(..) => {
MultiDecorator(..) | MultiModifier(..) | SyntaxExtension::AttrProcMacro(..) => {
self.cx.span_err(path.span,
&format!("`{}` can only be used in attributes", extname));
return kind.dummy(span);
}

SyntaxExtension::ProcMacro(ref expandfun) => {
if ident.name != keywords::Invalid.name() {
let msg =
format!("macro {}! expects no ident argument, given '{}'", extname, ident);
fld.cx.span_err(path.span, &msg);
return None;
}

fld.cx.bt_push(ExpnInfo {
call_site: call_site,
callee: NameAndSpan {
format: MacroBang(extname),
// FIXME procedural macros do not have proper span info
// yet, when they do, we should use it here.
span: None,
// FIXME probably want to follow macro_rules macros here.
allow_internal_unstable: false,
},
});

Some(expandfun.expand(fld.cx, call_site, TokenStream::from_tts(marked_tts)))
}
};

let expanded = if let Some(expanded) = opt_expanded {
Expand Down
6 changes: 4 additions & 2 deletions src/libsyntax/ext/proc_macro_shim.rs
Expand Up @@ -24,7 +24,9 @@ use ext::base::*;

/// Take a `ExtCtxt`, `Span`, and `TokenStream`, and produce a Macro Result that parses
/// the TokenStream as a block and returns it as an `Expr`.
pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt, sp: Span, output: TokenStream)
pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt,
sp: Span,
output: TokenStream)
-> Box<MacResult + 'cx> {
let parser = cx.new_parser_from_tts(&output.to_tts());

Expand Down Expand Up @@ -60,7 +62,7 @@ pub fn build_block_emitter<'cx>(cx: &'cx mut ExtCtxt, sp: Span, output: TokenStr
}

pub mod prelude {
pub use ext::proc_macro_shim::build_block_emitter;
pub use super::build_block_emitter;
pub use ast::Ident;
pub use codemap::{DUMMY_SP, Span};
pub use ext::base::{ExtCtxt, MacResult};
Expand Down

0 comments on commit 6a2d2c9

Please sign in to comment.