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 rust-lang#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.