Skip to content

Commit b012f0d

Browse files
committed
syntax: Repeating fragments in quasiquotation
1 parent 9266d59 commit b012f0d

File tree

2 files changed

+246
-80
lines changed

2 files changed

+246
-80
lines changed

src/libsyntax/ext/quote.rs

Lines changed: 209 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@ pub mod rt {
3535

3636
use ast::{TokenTree, Generics, Expr};
3737

38+
use std::iter;
39+
3840
pub use parse::new_parser_from_tts;
3941
pub use codemap::{BytePos, Span, dummy_spanned};
42+
pub use std::iter::IntoIterator;
43+
pub use parse::attr::ParserAttr;
4044

4145
pub trait ToTokens {
4246
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
@@ -310,23 +314,6 @@ pub mod rt {
310314
)
311315
}
312316

313-
impl_to_tokens! { ast::Ident }
314-
impl_to_tokens! { ast::Path }
315-
impl_to_tokens! { P<ast::Item> }
316-
impl_to_tokens! { P<ast::ImplItem> }
317-
impl_to_tokens! { P<ast::TraitItem> }
318-
impl_to_tokens! { P<ast::Pat> }
319-
impl_to_tokens! { ast::Arm }
320-
impl_to_tokens_lifetime! { &'a [P<ast::Item>] }
321-
impl_to_tokens! { ast::Ty }
322-
impl_to_tokens_lifetime! { &'a [ast::Ty] }
323-
impl_to_tokens! { Generics }
324-
impl_to_tokens! { ast::WhereClause }
325-
impl_to_tokens! { P<ast::Stmt> }
326-
impl_to_tokens! { P<ast::Expr> }
327-
impl_to_tokens! { ast::Block }
328-
impl_to_tokens! { ast::Arg }
329-
impl_to_tokens! { ast::Attribute_ }
330317
impl_to_tokens_lifetime! { &'a str }
331318
impl_to_tokens! { () }
332319
impl_to_tokens! { char }
@@ -399,14 +386,94 @@ pub mod rt {
399386

400387
}
401388

389+
pub struct IterWrapper<I> {
390+
inner: I,
391+
empty: bool,
392+
}
393+
394+
impl<T, I: Iterator<Item=T>> Iterator for IterWrapper<I> {
395+
type Item = T;
396+
fn next(&mut self) -> Option<T> {
397+
match self.inner.next() {
398+
Some(elem) => {
399+
self.empty = false;
400+
Some(elem)
401+
}
402+
None => {
403+
assert!(!self.empty, "a fragment must repeat at least once in quasiquotation");
404+
None
405+
}
406+
}
407+
}
408+
}
409+
410+
pub trait IntoWrappedIter {
411+
type Item;
412+
type IntoIter: Iterator<Item=Self::Item>;
413+
fn into_wrapped_iter(self, one_or_more: bool) -> IterWrapper<Self::IntoIter>;
414+
}
415+
416+
impl<I: IntoIterator> IntoWrappedIter for I {
417+
type Item = I::Item;
418+
type IntoIter = I::IntoIter;
419+
fn into_wrapped_iter(self, one_or_more: bool) -> IterWrapper<I::IntoIter> {
420+
IterWrapper { empty: one_or_more, inner: self.into_iter() }
421+
}
422+
}
423+
424+
impl<T: Clone> IntoWrappedIter for Spanned<T> {
425+
type Item = Spanned<T>;
426+
type IntoIter = iter::Repeat<Spanned<T>>;
427+
fn into_wrapped_iter(self, one_or_more: bool) -> IterWrapper<iter::Repeat<Spanned<T>>> {
428+
IterWrapper { empty: one_or_more, inner: iter::repeat(self) }
429+
}
430+
}
431+
432+
impl IntoWrappedIter for TokenTree {
433+
type Item = TokenTree;
434+
type IntoIter = iter::Repeat<TokenTree>;
435+
fn into_wrapped_iter(self, one_or_more: bool) -> IterWrapper<iter::Repeat<TokenTree>> {
436+
IterWrapper { empty: one_or_more, inner: iter::repeat(self) }
437+
}
438+
}
439+
440+
macro_rules! impl_to_tokens_local {
441+
($t:ty) => (
442+
impl_to_tokens!($t);
443+
444+
impl IntoWrappedIter for $t {
445+
type Item = $t;
446+
type IntoIter = iter::Repeat<$t>;
447+
fn into_wrapped_iter(self, one_or_more: bool) -> IterWrapper<iter::Repeat<$t>> {
448+
IterWrapper { empty: one_or_more, inner: iter::repeat(self) }
449+
}
450+
}
451+
)
452+
}
453+
454+
impl_to_tokens_local! { ast::Ident }
455+
impl_to_tokens_local! { ast::Path }
456+
impl_to_tokens_local! { P<ast::Item> }
457+
impl_to_tokens_local! { P<ast::ImplItem> }
458+
impl_to_tokens_local! { P<ast::TraitItem> }
459+
impl_to_tokens_local! { P<ast::Pat> }
460+
impl_to_tokens_local! { ast::Arm }
461+
impl_to_tokens_local! { ast::Ty }
462+
impl_to_tokens_local! { Generics }
463+
impl_to_tokens_local! { ast::WhereClause }
464+
impl_to_tokens_local! { P<ast::Stmt> }
465+
impl_to_tokens_local! { P<ast::Expr> }
466+
impl_to_tokens_local! { ast::Block }
467+
impl_to_tokens_local! { ast::Arg }
468+
impl_to_tokens_local! { ast::Attribute_ }
402469
}
403470

404471
pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt,
405472
sp: Span,
406473
tts: &[ast::TokenTree])
407474
-> Box<base::MacResult+'cx> {
408475
let (cx_expr, expr) = expand_tts(cx, sp, tts);
409-
let expanded = expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"]]);
476+
let expanded = expand_wrapper(cx, sp, cx_expr, expr);
410477
base::MacEager::expr(expanded)
411478
}
412479

@@ -474,13 +541,13 @@ pub fn expand_quote_matcher(cx: &mut ExtCtxt,
474541
-> Box<base::MacResult+'static> {
475542
let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
476543
let mut vector = mk_stmts_let(cx, sp);
477-
vector.extend(statements_mk_tts(cx, &tts[..], true).into_iter());
544+
vector.extend(statements_mk_tts(cx, &tts[..], true).0.into_iter());
478545
let block = cx.expr_block(
479546
cx.block_all(sp,
480547
vector,
481548
Some(cx.expr_ident(sp, id_ext("tt")))));
482549

483-
let expanded = expand_wrapper(cx, sp, cx_expr, block, &[&["syntax", "ext", "quote", "rt"]]);
550+
let expanded = expand_wrapper(cx, sp, cx_expr, block);
484551
base::MacEager::expr(expanded)
485552
}
486553

@@ -677,7 +744,8 @@ fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
677744
mk_token_path(cx, sp, name)
678745
}
679746

680-
fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> Vec<P<ast::Stmt>> {
747+
fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> (Vec<P<ast::Stmt>>,
748+
Vec<ast::Ident>) {
681749
match *tt {
682750
ast::TtToken(sp, SubstNt(ident, _)) => {
683751
// tt.extend($ident.to_tokens(ext_cx).into_iter())
@@ -696,7 +764,7 @@ fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> Vec<P<a
696764
id_ext("extend"),
697765
vec!(e_to_toks));
698766

699-
vec!(cx.stmt_expr(e_push))
767+
(vec![cx.stmt_expr(e_push)], vec![ident])
700768
}
701769
ref tt @ ast::TtToken(_, MatchNt(..)) if !matcher => {
702770
let mut seq = vec![];
@@ -715,56 +783,117 @@ fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> Vec<P<a
715783
cx.expr_ident(sp, id_ext("tt")),
716784
id_ext("push"),
717785
vec!(e_tok));
718-
vec!(cx.stmt_expr(e_push))
786+
(vec![cx.stmt_expr(e_push)], vec![])
719787
},
720788
ast::TtDelimited(_, ref delimed) => {
721-
statements_mk_tt(cx, &delimed.open_tt(), matcher).into_iter()
722-
.chain(delimed.tts.iter()
723-
.flat_map(|tt| statements_mk_tt(cx, tt, matcher).into_iter()))
724-
.chain(statements_mk_tt(cx, &delimed.close_tt(), matcher).into_iter())
725-
.collect()
789+
let (stmts, idents) = statements_mk_tts(cx, &delimed.tts[..], matcher);
790+
(statements_mk_tt(cx, &delimed.open_tt(), matcher).0.into_iter()
791+
.chain(stmts.into_iter())
792+
.chain(statements_mk_tt(cx, &delimed.close_tt(), matcher).0.into_iter())
793+
.collect(),
794+
idents)
726795
},
727796
ast::TtSequence(sp, ref seq) => {
728-
if !matcher {
729-
panic!("TtSequence in quote!");
730-
}
797+
if matcher {
798+
let e_sp = cx.expr_ident(sp, id_ext("_sp"));
799+
800+
let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
801+
let mut tts_stmts = vec![stmt_let_tt];
802+
tts_stmts.extend(statements_mk_tts(cx, &seq.tts[..], matcher).0.into_iter());
803+
let e_tts = cx.expr_block(cx.block(sp, tts_stmts,
804+
Some(cx.expr_ident(sp, id_ext("tt")))));
805+
let e_separator = match seq.separator {
806+
Some(ref sep) => cx.expr_some(sp, expr_mk_token(cx, sp, sep)),
807+
None => cx.expr_none(sp),
808+
};
809+
let e_op = match seq.op {
810+
ast::ZeroOrMore => mk_ast_path(cx, sp, "ZeroOrMore"),
811+
ast::OneOrMore => mk_ast_path(cx, sp, "OneOrMore"),
812+
};
813+
let fields = vec![cx.field_imm(sp, id_ext("tts"), e_tts),
814+
cx.field_imm(sp, id_ext("separator"), e_separator),
815+
cx.field_imm(sp, id_ext("op"), e_op),
816+
cx.field_imm(sp, id_ext("num_captures"),
817+
cx.expr_usize(sp, seq.num_captures))];
818+
let seq_path = vec![id_ext("syntax"), id_ext("ast"), id_ext("SequenceRepetition")];
819+
let e_seq_struct = cx.expr_struct(sp, cx.path_global(sp, seq_path), fields);
820+
let e_rc_new = cx.expr_call_global(sp, vec![id_ext("std"),
821+
id_ext("rc"),
822+
id_ext("Rc"),
823+
id_ext("new")],
824+
vec![e_seq_struct]);
825+
let e_tok = cx.expr_call(sp,
826+
mk_ast_path(cx, sp, "TtSequence"),
827+
vec!(e_sp, e_rc_new));
828+
let e_push =
829+
cx.expr_method_call(sp,
830+
cx.expr_ident(sp, id_ext("tt")),
831+
id_ext("push"),
832+
vec!(e_tok));
833+
(vec![cx.stmt_expr(e_push)], vec![])
834+
} else {
835+
// Repeating fragments in a loop:
836+
// for (...(a, b), ...) in a.into_wrapped_iter().zip(b.into_wrapped_iter())... {
837+
// // (quasiquotation with $a, $b, ...)
838+
//}
839+
let (mut stmts, idents) = statements_mk_tts(cx, &seq.tts[..], matcher);
840+
if idents.is_empty() {
841+
cx.span_fatal(sp, "attempted to repeat an expression containing \
842+
no syntax variables matched as repeating at this depth");
843+
}
731844

732-
let e_sp = cx.expr_ident(sp, id_ext("_sp"));
845+
let mut iter = idents.clone().into_iter();
846+
let first = iter.next().unwrap();
847+
let mut zipped = cx.expr_ident(sp, first);
848+
let one_or_more = cx.expr_bool(sp, seq.op == ast::OneOrMore);
849+
zipped = cx.expr_method_call(sp, zipped, id_ext("into_wrapped_iter"),
850+
vec![one_or_more.clone()]);
851+
let mut pat = cx.pat_ident(sp, first);
852+
for ident in iter {
853+
// Assertion: zipped iterators must have at least one element
854+
// if one_or_more == `true`.
855+
let expr_ident = cx.expr_ident(sp, ident);
856+
let expr = cx.expr_method_call(sp, expr_ident,
857+
id_ext("into_wrapped_iter"),
858+
vec![one_or_more.clone()]);
859+
zipped = cx.expr_method_call(sp, zipped, id_ext("zip"), vec![expr]);
860+
pat = cx.pat_tuple(sp, vec!(pat, cx.pat_ident(sp, ident)));
861+
}
733862

734-
let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
735-
let mut tts_stmts = vec![stmt_let_tt];
736-
tts_stmts.extend(statements_mk_tts(cx, &seq.tts[..], matcher).into_iter());
737-
let e_tts = cx.expr_block(cx.block(sp, tts_stmts,
738-
Some(cx.expr_ident(sp, id_ext("tt")))));
739-
let e_separator = match seq.separator {
740-
Some(ref sep) => cx.expr_some(sp, expr_mk_token(cx, sp, sep)),
741-
None => cx.expr_none(sp),
742-
};
743-
let e_op = match seq.op {
744-
ast::ZeroOrMore => mk_ast_path(cx, sp, "ZeroOrMore"),
745-
ast::OneOrMore => mk_ast_path(cx, sp, "OneOrMore"),
746-
};
747-
let fields = vec![cx.field_imm(sp, id_ext("tts"), e_tts),
748-
cx.field_imm(sp, id_ext("separator"), e_separator),
749-
cx.field_imm(sp, id_ext("op"), e_op),
750-
cx.field_imm(sp, id_ext("num_captures"),
751-
cx.expr_usize(sp, seq.num_captures))];
752-
let seq_path = vec![id_ext("syntax"), id_ext("ast"), id_ext("SequenceRepetition")];
753-
let e_seq_struct = cx.expr_struct(sp, cx.path_global(sp, seq_path), fields);
754-
let e_rc_new = cx.expr_call_global(sp, vec![id_ext("std"),
755-
id_ext("rc"),
756-
id_ext("Rc"),
757-
id_ext("new")],
758-
vec![e_seq_struct]);
759-
let e_tok = cx.expr_call(sp,
760-
mk_ast_path(cx, sp, "TtSequence"),
761-
vec!(e_sp, e_rc_new));
762-
let e_push =
763-
cx.expr_method_call(sp,
764-
cx.expr_ident(sp, id_ext("tt")),
765-
id_ext("push"),
766-
vec!(e_tok));
767-
vec!(cx.stmt_expr(e_push))
863+
match seq.separator {
864+
Some(ref tok) => {
865+
// Intersperse the separator
866+
stmts.extend(statements_mk_tt(cx, &ast::TtToken(sp, tok.clone()),
867+
false).0.into_iter());
868+
}
869+
None => {}
870+
}
871+
872+
let stmt_for = cx.stmt_expr(P(ast::Expr {
873+
id: ast::DUMMY_NODE_ID,
874+
node: ast::ExprForLoop(pat, zipped, cx.block(sp, stmts, None), None),
875+
span: sp,
876+
}));
877+
878+
let stmts_for = if seq.separator.is_some() {
879+
// Pop the last occurence of the separator
880+
let tt = cx.expr_ident(sp, id_ext("tt"));
881+
let len = cx.expr_method_call(sp, tt.clone(), id_ext("len"), vec!());
882+
let _len_ident = gensym_ident("_len");
883+
let _len = cx.expr_ident(sp, _len_ident);
884+
let cond = cx.expr_binary(sp, ast::BiNe, len.clone(), _len);
885+
let then = cx.expr_method_call(sp, tt, id_ext("pop"), vec!());
886+
let then = cx.expr_block(cx.block(sp, vec![cx.stmt_expr(then)], None));
887+
let if_len_eq = cx.expr_if(sp, cond, then, None);
888+
vec![cx.stmt_let(sp, false, _len_ident, len),
889+
stmt_for,
890+
cx.stmt_expr(if_len_eq)]
891+
} else {
892+
vec![stmt_for]
893+
};
894+
895+
(stmts_for, idents)
896+
}
768897
}
769898
}
770899
}
@@ -832,20 +961,24 @@ fn mk_stmts_let(cx: &ExtCtxt, sp: Span) -> Vec<P<ast::Stmt>> {
832961
vec!(stmt_let_sp, stmt_let_tt)
833962
}
834963

835-
fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> Vec<P<ast::Stmt>> {
836-
let mut ss = Vec::new();
964+
fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> (Vec<P<ast::Stmt>>,
965+
Vec<ast::Ident>) {
966+
let mut stmts = Vec::new();
967+
let mut idents = Vec::new();
837968
for tt in tts {
838-
ss.extend(statements_mk_tt(cx, tt, matcher).into_iter());
969+
let (ss, is) = statements_mk_tt(cx, tt, matcher);
970+
stmts.extend(ss.into_iter());
971+
idents.extend(is.into_iter());
839972
}
840-
ss
973+
(stmts, idents)
841974
}
842975

843976
fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
844977
-> (P<ast::Expr>, P<ast::Expr>) {
845978
let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
846979

847980
let mut vector = mk_stmts_let(cx, sp);
848-
vector.extend(statements_mk_tts(cx, &tts[..], false).into_iter());
981+
vector.extend(statements_mk_tts(cx, &tts[..], false).0.into_iter());
849982
let block = cx.expr_block(
850983
cx.block_all(sp,
851984
vector,
@@ -857,13 +990,14 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
857990
fn expand_wrapper(cx: &ExtCtxt,
858991
sp: Span,
859992
cx_expr: P<ast::Expr>,
860-
expr: P<ast::Expr>,
861-
imports: &[&[&str]]) -> P<ast::Expr> {
993+
expr: P<ast::Expr>) -> P<ast::Expr> {
862994
// Explicitly borrow to avoid moving from the invoker (#16992)
863995
let cx_expr_borrow = cx.expr_addr_of(sp, cx.expr_deref(sp, cx_expr));
864996
let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr_borrow);
865997

866-
let stmts = imports.iter().map(|path| {
998+
let stmts = [
999+
&["syntax", "ext", "quote", "rt"]
1000+
].iter().map(|path| {
8671001
// make item: `use ...;`
8681002
let path = path.iter().map(|s| s.to_string()).collect();
8691003
cx.stmt_item(sp, cx.item_use_glob(sp, ast::Inherited, ids_ext(path)))
@@ -895,10 +1029,5 @@ fn expand_parse_call(cx: &ExtCtxt,
8951029
let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
8961030
arg_exprs);
8971031

898-
if parse_method == "parse_attribute" {
899-
expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"],
900-
&["syntax", "parse", "attr"]])
901-
} else {
902-
expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"]])
903-
}
1032+
expand_wrapper(cx, sp, cx_expr, expr)
9041033
}

0 commit comments

Comments
 (0)