Skip to content

Commit 3541abe

Browse files
committed
Add quasiquote for matchers and attributes
1 parent 1576142 commit 3541abe

File tree

10 files changed

+268
-44
lines changed

10 files changed

+268
-44
lines changed

src/doc/reference.md

+8
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,15 @@ usually in [procedural macros](book/plugins.html#syntax-extensions):
764764
* `quote_pat!`
765765
* `quote_stmt!`
766766
* `quote_tokens!`
767+
* `quote_matcher!`
767768
* `quote_ty!`
769+
* `quote_attr!`
770+
771+
Keep in mind that when `$name : ident` appears in the input to
772+
`quote_tokens!`, the result contains unquoted `name` followed by two tokens.
773+
However, input of the same form passed to `quote_matcher!` becomes a
774+
quasiquoted MBE-matcher of a nonterminal. No unquotation happens. Otherwise
775+
the result of `quote_matcher!` is identical to that of `quote_tokens!`.
768776

769777
Documentation is very limited at the moment.
770778

src/libsyntax/ast.rs

+15
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ pub use self::PathParameters::*;
5858
use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
5959
use abi::Abi;
6060
use ast_util;
61+
use ext::base;
62+
use ext::tt::macro_parser;
6163
use owned_slice::OwnedSlice;
6264
use parse::token::{InternedString, str_to_ident};
6365
use parse::token;
66+
use parse::lexer;
6467
use ptr::P;
6568

6669
use std::fmt;
@@ -960,6 +963,18 @@ impl TokenTree {
960963
TtSequence(span, _) => span,
961964
}
962965
}
966+
967+
/// Use this token tree as a matcher to parse given tts.
968+
pub fn parse(cx: &base::ExtCtxt, mtch: &[TokenTree], tts: &[TokenTree])
969+
-> macro_parser::NamedParseResult {
970+
// `None` is because we're not interpolating
971+
let arg_rdr = lexer::new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
972+
None,
973+
None,
974+
tts.iter().cloned().collect(),
975+
true);
976+
macro_parser::parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtch)
977+
}
963978
}
964979

965980
pub type Mac = Spanned<Mac_>;

src/libsyntax/ext/base.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -519,6 +519,12 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
519519
syntax_expanders.insert(intern("quote_stmt"),
520520
builtin_normal_expander(
521521
ext::quote::expand_quote_stmt));
522+
syntax_expanders.insert(intern("quote_matcher"),
523+
builtin_normal_expander(
524+
ext::quote::expand_quote_matcher));
525+
syntax_expanders.insert(intern("quote_attr"),
526+
builtin_normal_expander(
527+
ext::quote::expand_quote_attr));
522528
}
523529

524530
syntax_expanders.insert(intern("line"),

src/libsyntax/ext/quote.rs

+127-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -401,7 +401,7 @@ pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt,
401401
tts: &[ast::TokenTree])
402402
-> Box<base::MacResult+'cx> {
403403
let (cx_expr, expr) = expand_tts(cx, sp, tts);
404-
let expanded = expand_wrapper(cx, sp, cx_expr, expr);
404+
let expanded = expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"]]);
405405
base::MacEager::expr(expanded)
406406
}
407407

@@ -465,6 +465,32 @@ pub fn expand_quote_stmt(cx: &mut ExtCtxt,
465465
base::MacEager::expr(expanded)
466466
}
467467

468+
pub fn expand_quote_attr(cx: &mut ExtCtxt,
469+
sp: Span,
470+
tts: &[ast::TokenTree])
471+
-> Box<base::MacResult+'static> {
472+
let expanded = expand_parse_call(cx, sp, "parse_attribute",
473+
vec!(cx.expr_bool(sp, true)), tts);
474+
475+
base::MacEager::expr(expanded)
476+
}
477+
478+
pub fn expand_quote_matcher(cx: &mut ExtCtxt,
479+
sp: Span,
480+
tts: &[ast::TokenTree])
481+
-> Box<base::MacResult+'static> {
482+
let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
483+
let mut vector = mk_stmts_let(cx, sp);
484+
vector.extend(statements_mk_tts(cx, &tts[..], true).into_iter());
485+
let block = cx.expr_block(
486+
cx.block_all(sp,
487+
vector,
488+
Some(cx.expr_ident(sp, id_ext("tt")))));
489+
490+
let expanded = expand_wrapper(cx, sp, cx_expr, block, &[&["syntax", "ext", "quote", "rt"]]);
491+
base::MacEager::expr(expanded)
492+
}
493+
468494
fn ids_ext(strs: Vec<String> ) -> Vec<ast::Ident> {
469495
strs.iter().map(|str| str_to_ident(&(*str))).collect()
470496
}
@@ -527,7 +553,7 @@ fn mk_delim(cx: &ExtCtxt, sp: Span, delim: token::DelimToken) -> P<ast::Expr> {
527553
}
528554

529555
#[allow(non_upper_case_globals)]
530-
fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
556+
fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
531557
macro_rules! mk_lit {
532558
($name: expr, $suffix: expr, $($args: expr),*) => {{
533559
let inner = cx.expr_call(sp, mk_token_path(cx, sp, $name), vec![$($args),*]);
@@ -606,6 +632,21 @@ fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
606632
vec!(mk_name(cx, sp, ident.ident())));
607633
}
608634

635+
token::MatchNt(name, kind, namep, kindp) => {
636+
return cx.expr_call(sp,
637+
mk_token_path(cx, sp, "MatchNt"),
638+
vec!(mk_ident(cx, sp, name),
639+
mk_ident(cx, sp, kind),
640+
match namep {
641+
ModName => mk_token_path(cx, sp, "ModName"),
642+
Plain => mk_token_path(cx, sp, "Plain"),
643+
},
644+
match kindp {
645+
ModName => mk_token_path(cx, sp, "ModName"),
646+
Plain => mk_token_path(cx, sp, "Plain"),
647+
}));
648+
}
649+
609650
token::Interpolated(_) => panic!("quote! with interpolated token"),
610651

611652
_ => ()
@@ -642,7 +683,7 @@ fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
642683
mk_token_path(cx, sp, name)
643684
}
644685

645-
fn mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree) -> Vec<P<ast::Stmt>> {
686+
fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> Vec<P<ast::Stmt>> {
646687
match *tt {
647688
ast::TtToken(sp, SubstNt(ident, _)) => {
648689
// tt.extend($ident.to_tokens(ext_cx).into_iter())
@@ -663,18 +704,18 @@ fn mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree) -> Vec<P<ast::Stmt>> {
663704

664705
vec!(cx.stmt_expr(e_push))
665706
}
666-
ref tt @ ast::TtToken(_, MatchNt(..)) => {
707+
ref tt @ ast::TtToken(_, MatchNt(..)) if !matcher => {
667708
let mut seq = vec![];
668709
for i in 0..tt.len() {
669710
seq.push(tt.get_tt(i));
670711
}
671-
mk_tts(cx, &seq[..])
712+
statements_mk_tts(cx, &seq[..], matcher)
672713
}
673714
ast::TtToken(sp, ref tok) => {
674715
let e_sp = cx.expr_ident(sp, id_ext("_sp"));
675716
let e_tok = cx.expr_call(sp,
676717
mk_ast_path(cx, sp, "TtToken"),
677-
vec!(e_sp, mk_token(cx, sp, tok)));
718+
vec!(e_sp, expr_mk_token(cx, sp, tok)));
678719
let e_push =
679720
cx.expr_method_call(sp,
680721
cx.expr_ident(sp, id_ext("tt")),
@@ -683,27 +724,61 @@ fn mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree) -> Vec<P<ast::Stmt>> {
683724
vec!(cx.stmt_expr(e_push))
684725
},
685726
ast::TtDelimited(_, ref delimed) => {
686-
mk_tt(cx, &delimed.open_tt()).into_iter()
687-
.chain(delimed.tts.iter().flat_map(|tt| mk_tt(cx, tt).into_iter()))
688-
.chain(mk_tt(cx, &delimed.close_tt()).into_iter())
727+
statements_mk_tt(cx, &delimed.open_tt(), matcher).into_iter()
728+
.chain(delimed.tts.iter()
729+
.flat_map(|tt| statements_mk_tt(cx, tt, matcher).into_iter()))
730+
.chain(statements_mk_tt(cx, &delimed.close_tt(), matcher).into_iter())
689731
.collect()
690732
},
691-
ast::TtSequence(..) => panic!("TtSequence in quote!"),
692-
}
693-
}
733+
ast::TtSequence(sp, ref seq) => {
734+
if !matcher {
735+
panic!("TtSequence in quote!");
736+
}
694737

695-
fn mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> Vec<P<ast::Stmt>> {
696-
let mut ss = Vec::new();
697-
for tt in tts {
698-
ss.extend(mk_tt(cx, tt).into_iter());
738+
let e_sp = cx.expr_ident(sp, id_ext("_sp"));
739+
740+
let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
741+
let mut tts_stmts = vec![stmt_let_tt];
742+
tts_stmts.extend(statements_mk_tts(cx, &seq.tts[..], matcher).into_iter());
743+
let e_tts = cx.expr_block(cx.block(sp, tts_stmts,
744+
Some(cx.expr_ident(sp, id_ext("tt")))));
745+
let e_separator = match seq.separator {
746+
Some(ref sep) => cx.expr_some(sp, expr_mk_token(cx, sp, sep)),
747+
None => cx.expr_none(sp),
748+
};
749+
let e_op = match seq.op {
750+
ast::ZeroOrMore => mk_ast_path(cx, sp, "ZeroOrMore"),
751+
ast::OneOrMore => mk_ast_path(cx, sp, "OneOrMore"),
752+
};
753+
let fields = vec![cx.field_imm(sp, id_ext("tts"), e_tts),
754+
cx.field_imm(sp, id_ext("separator"), e_separator),
755+
cx.field_imm(sp, id_ext("op"), e_op),
756+
cx.field_imm(sp, id_ext("num_captures"),
757+
cx.expr_usize(sp, seq.num_captures))];
758+
let seq_path = vec![id_ext("syntax"), id_ext("ast"), id_ext("SequenceRepetition")];
759+
let e_seq_struct = cx.expr_struct(sp, cx.path_global(sp, seq_path), fields);
760+
let e_rc_new = cx.expr_call_global(sp, vec![id_ext("std"),
761+
id_ext("rc"),
762+
id_ext("Rc"),
763+
id_ext("new")],
764+
vec![e_seq_struct]);
765+
let e_tok = cx.expr_call(sp,
766+
mk_ast_path(cx, sp, "TtSequence"),
767+
vec!(e_sp, e_rc_new));
768+
let e_push =
769+
cx.expr_method_call(sp,
770+
cx.expr_ident(sp, id_ext("tt")),
771+
id_ext("push"),
772+
vec!(e_tok));
773+
vec!(cx.stmt_expr(e_push))
774+
}
699775
}
700-
ss
701776
}
702777

703-
fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
704-
-> (P<ast::Expr>, P<ast::Expr>) {
778+
fn parse_arguments_to_quote(cx: &ExtCtxt, tts: &[ast::TokenTree])
779+
-> (P<ast::Expr>, Vec<ast::TokenTree>) {
705780
// NB: It appears that the main parser loses its mind if we consider
706-
// $foo as a TtNonterminal during the main parse, so we have to re-parse
781+
// $foo as a SubstNt during the main parse, so we have to re-parse
707782
// under quote_depth > 0. This is silly and should go away; the _guess_ is
708783
// it has to do with transition away from supporting old-style macros, so
709784
// try removing it when enough of them are gone.
@@ -719,6 +794,10 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
719794
let tts = p.parse_all_token_trees();
720795
p.abort_if_errors();
721796

797+
(cx_expr, tts)
798+
}
799+
800+
fn mk_stmts_let(cx: &ExtCtxt, sp: Span) -> Vec<P<ast::Stmt>> {
722801
// We also bind a single value, sp, to ext_cx.call_site()
723802
//
724803
// This causes every span in a token-tree quote to be attributed to the
@@ -756,8 +835,23 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
756835

757836
let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
758837

759-
let mut vector = vec!(stmt_let_sp, stmt_let_tt);
760-
vector.extend(mk_tts(cx, &tts[..]).into_iter());
838+
vec!(stmt_let_sp, stmt_let_tt)
839+
}
840+
841+
fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> Vec<P<ast::Stmt>> {
842+
let mut ss = Vec::new();
843+
for tt in tts {
844+
ss.extend(statements_mk_tt(cx, tt, matcher).into_iter());
845+
}
846+
ss
847+
}
848+
849+
fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
850+
-> (P<ast::Expr>, P<ast::Expr>) {
851+
let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
852+
853+
let mut vector = mk_stmts_let(cx, sp);
854+
vector.extend(statements_mk_tts(cx, &tts[..], false).into_iter());
761855
let block = cx.expr_block(
762856
cx.block_all(sp,
763857
vector,
@@ -769,14 +863,14 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
769863
fn expand_wrapper(cx: &ExtCtxt,
770864
sp: Span,
771865
cx_expr: P<ast::Expr>,
772-
expr: P<ast::Expr>) -> P<ast::Expr> {
866+
expr: P<ast::Expr>,
867+
imports: &[&[&str]]) -> P<ast::Expr> {
773868
// Explicitly borrow to avoid moving from the invoker (#16992)
774869
let cx_expr_borrow = cx.expr_addr_of(sp, cx.expr_deref(sp, cx_expr));
775870
let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr_borrow);
776871

777-
let stmts = [
778-
&["syntax", "ext", "quote", "rt"],
779-
].iter().map(|path| {
872+
let stmts = imports.iter().map(|path| {
873+
// make item: `use ...;`
780874
let path = path.iter().map(|s| s.to_string()).collect();
781875
cx.stmt_item(sp, cx.item_use_glob(sp, ast::Inherited, ids_ext(path)))
782876
}).chain(Some(stmt_let_ext_cx).into_iter()).collect();
@@ -807,5 +901,10 @@ fn expand_parse_call(cx: &ExtCtxt,
807901
let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
808902
arg_exprs);
809903

810-
expand_wrapper(cx, sp, cx_expr, expr)
904+
if parse_method == "parse_attribute" {
905+
expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"],
906+
&["syntax", "parse", "attr"]])
907+
} else {
908+
expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"]])
909+
}
811910
}

src/libsyntax/ext/tt/macro_parser.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,15 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
243243
ret_val
244244
}
245245

246-
pub enum ParseResult {
247-
Success(HashMap<Ident, Rc<NamedMatch>>),
246+
pub enum ParseResult<T> {
247+
Success(T),
248248
Failure(codemap::Span, String),
249249
Error(codemap::Span, String)
250250
}
251251

252+
pub type NamedParseResult = ParseResult<HashMap<Ident, Rc<NamedMatch>>>;
253+
pub type PositionalParseResult = ParseResult<Vec<Rc<NamedMatch>>>;
254+
252255
pub fn parse_or_else(sess: &ParseSess,
253256
cfg: ast::CrateConfig,
254257
rdr: TtReader,
@@ -280,7 +283,7 @@ pub fn parse(sess: &ParseSess,
280283
cfg: ast::CrateConfig,
281284
mut rdr: TtReader,
282285
ms: &[TokenTree])
283-
-> ParseResult {
286+
-> NamedParseResult {
284287
let mut cur_eis = Vec::new();
285288
cur_eis.push(initial_matcher_pos(Rc::new(ms.iter()
286289
.cloned()

src/libsyntax/ext/tt/macro_rules.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ext::base::{NormalTT, TTMacroExpander};
1515
use ext::tt::macro_parser::{Success, Error, Failure};
1616
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
1717
use ext::tt::macro_parser::{parse, parse_or_else};
18-
use parse::lexer::{new_tt_reader, new_tt_reader_with_doc_flag};
18+
use parse::lexer::new_tt_reader;
1919
use parse::parser::Parser;
2020
use parse::attr::ParserAttr;
2121
use parse::token::{self, special_idents, gensym_ident, NtTT, Token};
@@ -154,15 +154,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
154154
TtDelimited(_, ref delim) => &delim.tts[..],
155155
_ => cx.span_fatal(sp, "malformed macro lhs")
156156
};
157-
// `None` is because we're not interpolating
158-
let arg_rdr = new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
159-
None,
160-
None,
161-
arg.iter()
162-
.cloned()
163-
.collect(),
164-
true);
165-
match parse(cx.parse_sess(), cx.cfg(), arg_rdr, lhs_tt) {
157+
158+
match TokenTree::parse(cx, lhs_tt, arg) {
166159
Success(named_matches) => {
167160
let rhs = match *rhses[i] {
168161
// okay, what's your transcriber?

0 commit comments

Comments
 (0)