Skip to content

Commit 6a2d2c9

Browse files
committed
Adds a ProcMacro form of syntax extension
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.
1 parent c772948 commit 6a2d2c9

File tree

9 files changed

+422
-18
lines changed

9 files changed

+422
-18
lines changed

src/librustc_plugin/registry.rs

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ impl<'a> Registry<'a> {
111111
}
112112
MultiDecorator(ext) => MultiDecorator(ext),
113113
MultiModifier(ext) => MultiModifier(ext),
114+
SyntaxExtension::ProcMacro(ext) => SyntaxExtension::ProcMacro(ext),
115+
SyntaxExtension::AttrProcMacro(ext) => SyntaxExtension::AttrProcMacro(ext),
114116
}));
115117
}
116118

src/libsyntax/ext/base.rs

+195-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
pub use self::SyntaxExtension::*;
11+
pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT, MacroRulesTT};
1212

1313
use ast::{self, Attribute, Name, PatKind};
1414
use attr::HasAttrs;
@@ -19,7 +19,7 @@ use ext::expand::{self, Invocation, Expansion};
1919
use ext::hygiene::Mark;
2020
use ext::tt::macro_rules;
2121
use parse;
22-
use parse::parser;
22+
use parse::parser::{self, Parser};
2323
use parse::token;
2424
use parse::token::{InternedString, str_to_ident};
2525
use ptr::P;
@@ -31,7 +31,8 @@ use feature_gate;
3131
use std::collections::HashMap;
3232
use std::path::PathBuf;
3333
use std::rc::Rc;
34-
use tokenstream;
34+
use std::default::Default;
35+
use tokenstream::{self, TokenStream};
3536

3637

3738
#[derive(Debug,Clone)]
@@ -60,6 +61,14 @@ impl HasAttrs for Annotatable {
6061
}
6162

6263
impl Annotatable {
64+
pub fn span(&self) -> Span {
65+
match *self {
66+
Annotatable::Item(ref item) => item.span,
67+
Annotatable::TraitItem(ref trait_item) => trait_item.span,
68+
Annotatable::ImplItem(ref impl_item) => impl_item.span,
69+
}
70+
}
71+
6372
pub fn expect_item(self) -> P<ast::Item> {
6473
match self {
6574
Annotatable::Item(i) => i,
@@ -146,6 +155,173 @@ impl Into<Vec<Annotatable>> for Annotatable {
146155
}
147156
}
148157

158+
pub trait ProcMacro {
159+
fn expand<'cx>(&self,
160+
ecx: &'cx mut ExtCtxt,
161+
span: Span,
162+
ts: TokenStream)
163+
-> Box<MacResult+'cx>;
164+
}
165+
166+
impl<F> ProcMacro for F
167+
where F: Fn(TokenStream) -> TokenStream
168+
{
169+
fn expand<'cx>(&self,
170+
ecx: &'cx mut ExtCtxt,
171+
span: Span,
172+
ts: TokenStream)
173+
-> Box<MacResult+'cx> {
174+
let result = (*self)(ts);
175+
// FIXME setup implicit context in TLS before calling self.
176+
let parser = ecx.new_parser_from_tts(&result.to_tts());
177+
Box::new(TokResult { parser: parser, span: span })
178+
}
179+
}
180+
181+
pub trait AttrProcMacro {
182+
fn expand<'cx>(&self,
183+
ecx: &'cx mut ExtCtxt,
184+
span: Span,
185+
annotation: TokenStream,
186+
annotated: TokenStream)
187+
-> Box<MacResult+'cx>;
188+
}
189+
190+
impl<F> AttrProcMacro for F
191+
where F: Fn(TokenStream, TokenStream) -> TokenStream
192+
{
193+
fn expand<'cx>(&self,
194+
ecx: &'cx mut ExtCtxt,
195+
span: Span,
196+
annotation: TokenStream,
197+
annotated: TokenStream)
198+
-> Box<MacResult+'cx> {
199+
// FIXME setup implicit context in TLS before calling self.
200+
let parser = ecx.new_parser_from_tts(&(*self)(annotation, annotated).to_tts());
201+
Box::new(TokResult { parser: parser, span: span })
202+
}
203+
}
204+
205+
struct TokResult<'a> {
206+
parser: Parser<'a>,
207+
span: Span,
208+
}
209+
210+
impl<'a> MacResult for TokResult<'a> {
211+
fn make_items(mut self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
212+
if self.parser.sess.span_diagnostic.has_errors() {
213+
return None;
214+
}
215+
216+
let mut items = SmallVector::zero();
217+
loop {
218+
match self.parser.parse_item() {
219+
Ok(Some(item)) => {
220+
// FIXME better span info.
221+
let mut item = item.unwrap();
222+
item.span = self.span;
223+
items.push(P(item));
224+
}
225+
Ok(None) => {
226+
return Some(items);
227+
}
228+
Err(mut e) => {
229+
e.emit();
230+
return None;
231+
}
232+
}
233+
}
234+
}
235+
236+
fn make_impl_items(mut self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> {
237+
let mut items = SmallVector::zero();
238+
loop {
239+
match self.parser.parse_impl_item() {
240+
Ok(mut item) => {
241+
// FIXME better span info.
242+
item.span = self.span;
243+
items.push(item);
244+
245+
return Some(items);
246+
}
247+
Err(mut e) => {
248+
e.emit();
249+
return None;
250+
}
251+
}
252+
}
253+
}
254+
255+
fn make_trait_items(mut self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
256+
let mut items = SmallVector::zero();
257+
loop {
258+
match self.parser.parse_trait_item() {
259+
Ok(mut item) => {
260+
// FIXME better span info.
261+
item.span = self.span;
262+
items.push(item);
263+
264+
return Some(items);
265+
}
266+
Err(mut e) => {
267+
e.emit();
268+
return None;
269+
}
270+
}
271+
}
272+
}
273+
274+
fn make_expr(mut self: Box<Self>) -> Option<P<ast::Expr>> {
275+
match self.parser.parse_expr() {
276+
Ok(e) => Some(e),
277+
Err(mut e) => {
278+
e.emit();
279+
return None;
280+
}
281+
}
282+
}
283+
284+
fn make_pat(mut self: Box<Self>) -> Option<P<ast::Pat>> {
285+
match self.parser.parse_pat() {
286+
Ok(e) => Some(e),
287+
Err(mut e) => {
288+
e.emit();
289+
return None;
290+
}
291+
}
292+
}
293+
294+
fn make_stmts(mut self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
295+
let mut stmts = SmallVector::zero();
296+
loop {
297+
if self.parser.token == token::Eof {
298+
return Some(stmts);
299+
}
300+
match self.parser.parse_full_stmt(true) {
301+
Ok(Some(mut stmt)) => {
302+
stmt.span = self.span;
303+
stmts.push(stmt);
304+
}
305+
Ok(None) => { /* continue */ }
306+
Err(mut e) => {
307+
e.emit();
308+
return None;
309+
}
310+
}
311+
}
312+
}
313+
314+
fn make_ty(mut self: Box<Self>) -> Option<P<ast::Ty>> {
315+
match self.parser.parse_ty() {
316+
Ok(e) => Some(e),
317+
Err(mut e) => {
318+
e.emit();
319+
return None;
320+
}
321+
}
322+
}
323+
}
324+
149325
/// Represents a thing that maps token trees to Macro Results
150326
pub trait TTMacroExpander {
151327
fn expand<'cx>(&self,
@@ -439,24 +615,35 @@ pub enum SyntaxExtension {
439615
/// based upon it.
440616
///
441617
/// `#[derive(...)]` is a `MultiItemDecorator`.
442-
MultiDecorator(Box<MultiItemDecorator + 'static>),
618+
///
619+
/// Prefer ProcMacro or MultiModifier since they are more flexible.
620+
MultiDecorator(Box<MultiItemDecorator>),
443621

444622
/// A syntax extension that is attached to an item and modifies it
445-
/// in-place. More flexible version than Modifier.
446-
MultiModifier(Box<MultiItemModifier + 'static>),
623+
/// in-place. Also allows decoration, i.e., creating new items.
624+
MultiModifier(Box<MultiItemModifier>),
625+
626+
/// A function-like procedural macro. TokenStream -> TokenStream.
627+
ProcMacro(Box<ProcMacro>),
628+
629+
/// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
630+
/// The first TokenSteam is the attribute, the second is the annotated item.
631+
/// Allows modification of the input items and adding new items, similar to
632+
/// MultiModifier, but uses TokenStreams, rather than AST nodes.
633+
AttrProcMacro(Box<AttrProcMacro>),
447634

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

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

462649
pub type NamedSyntaxExtension = (Name, SyntaxExtension);

src/libsyntax/ext/expand.rs

+51-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use feature_gate::{self, Features};
2222
use fold;
2323
use fold::*;
2424
use parse::token::{intern, keywords};
25+
use parse::span_to_tts;
2526
use ptr::P;
26-
use tokenstream::TokenTree;
27+
use tokenstream::{TokenTree, TokenStream};
2728
use util::small_vector::SmallVector;
2829
use visit::Visitor;
2930

@@ -308,6 +309,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
308309
items.push(item);
309310
kind.expect_from_annotatables(items)
310311
}
312+
SyntaxExtension::AttrProcMacro(ref mac) => {
313+
let attr_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
314+
attr.span));
315+
let item_toks = TokenStream::from_tts(span_to_tts(&fld.cx.parse_sess,
316+
item.span()));
317+
let result = mac.expand(self.cx, attr.span, attr_toks, item_toks);
318+
let items = match item {
319+
Annotatable::Item(_) => result.make_items()
320+
.unwrap_or(SmallVector::zero())
321+
.into_iter()
322+
.map(|i| Annotatable::Item(i))
323+
.collect(),
324+
Annotatable::TraitItem(_) => result.make_trait_items()
325+
.unwrap_or(SmallVector::zero())
326+
.into_iter()
327+
.map(|i| Annotatable::TraitItem(P(i)))
328+
.collect(),
329+
Annotatable::ImplItem(_) => result.make_impl_items()
330+
.unwrap_or(SmallVector::zero())
331+
.into_iter()
332+
.map(|i| Annotatable::ImplItem(P(i)))
333+
.collect(),
334+
};
335+
kind.expect_from_annotatables(items)
336+
}
311337
_ => unreachable!(),
312338
}
313339
}
@@ -377,11 +403,34 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
377403
kind.make_from(expander.expand(self.cx, span, ident, marked_tts, attrs))
378404
}
379405

380-
MultiDecorator(..) | MultiModifier(..) => {
406+
MultiDecorator(..) | MultiModifier(..) | SyntaxExtension::AttrProcMacro(..) => {
381407
self.cx.span_err(path.span,
382408
&format!("`{}` can only be used in attributes", extname));
383409
return kind.dummy(span);
384410
}
411+
412+
SyntaxExtension::ProcMacro(ref expandfun) => {
413+
if ident.name != keywords::Invalid.name() {
414+
let msg =
415+
format!("macro {}! expects no ident argument, given '{}'", extname, ident);
416+
fld.cx.span_err(path.span, &msg);
417+
return None;
418+
}
419+
420+
fld.cx.bt_push(ExpnInfo {
421+
call_site: call_site,
422+
callee: NameAndSpan {
423+
format: MacroBang(extname),
424+
// FIXME procedural macros do not have proper span info
425+
// yet, when they do, we should use it here.
426+
span: None,
427+
// FIXME probably want to follow macro_rules macros here.
428+
allow_internal_unstable: false,
429+
},
430+
});
431+
432+
Some(expandfun.expand(fld.cx, call_site, TokenStream::from_tts(marked_tts)))
433+
}
385434
};
386435

387436
let expanded = if let Some(expanded) = opt_expanded {

src/libsyntax/ext/proc_macro_shim.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use ext::base::*;
2424

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

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

6264
pub mod prelude {
63-
pub use ext::proc_macro_shim::build_block_emitter;
65+
pub use super::build_block_emitter;
6466
pub use ast::Ident;
6567
pub use codemap::{DUMMY_SP, Span};
6668
pub use ext::base::{ExtCtxt, MacResult};

0 commit comments

Comments
 (0)