Skip to content

Commit e7444cb

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

File tree

2 files changed

+251
-80
lines changed

2 files changed

+251
-80
lines changed

src/libsyntax/ext/quote.rs

+214-80
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,122 @@ 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), c...) in a.zip(b).zip(c)... {
837+
// // (quasiquotation with $a, $b, $c, ...)
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_call_ident(sp, id_ext("unquote_wrap"),
850+
// vec![zipped,
851+
// one_or_more]);
852+
zipped = cx.expr_method_call(sp, zipped, id_ext("into_wrapped_iter"),
853+
vec![one_or_more.clone()]);
854+
let mut pat = cx.pat_ident(sp, first);
855+
for ident in iter {
856+
// Assertion: zipped iterators must have at least one element.
857+
let expr_ident = cx.expr_ident(sp, ident);
858+
// let expr = cx.expr_call_ident(sp, id_ext("iter_wrap"),
859+
// vec![expr_ident,
860+
// one_or_more]);
861+
let expr = cx.expr_method_call(sp, expr_ident,
862+
id_ext("into_wrapped_iter"),
863+
vec![one_or_more.clone()]);
864+
zipped = cx.expr_method_call(sp, zipped, id_ext("zip"), vec![expr]);
865+
pat = cx.pat_tuple(sp, vec!(pat, cx.pat_ident(sp, ident)));
866+
}
733867

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))
868+
match seq.separator {
869+
Some(ref tok) => {
870+
// Intersperse the separator
871+
stmts.extend(statements_mk_tt(cx, &ast::TtToken(sp, tok.clone()),
872+
false).0.into_iter());
873+
}
874+
None => {}
875+
}
876+
877+
let stmt_for = cx.stmt_expr(P(ast::Expr {
878+
id: ast::DUMMY_NODE_ID,
879+
node: ast::ExprForLoop(pat, zipped, cx.block(sp, stmts, None), None),
880+
span: sp,
881+
}));
882+
883+
let stmts_for = if seq.separator.is_some() {
884+
// Pop the last occurence of the separator
885+
let tt = cx.expr_ident(sp, id_ext("tt"));
886+
let len = cx.expr_method_call(sp, tt.clone(), id_ext("len"), vec!());
887+
let _len_ident = gensym_ident("_len");
888+
let _len = cx.expr_ident(sp, _len_ident);
889+
let cond = cx.expr_binary(sp, ast::BiNe, len.clone(), _len);
890+
let then = cx.expr_method_call(sp, tt, id_ext("pop"), vec!());
891+
let then = cx.expr_block(cx.block(sp, vec![cx.stmt_expr(then)], None));
892+
let if_len_eq = cx.expr_if(sp, cond, then, None);
893+
vec![cx.stmt_let(sp, false, _len_ident, len),
894+
stmt_for,
895+
cx.stmt_expr(if_len_eq)]
896+
} else {
897+
vec![stmt_for]
898+
};
899+
900+
(stmts_for, idents)
901+
}
768902
}
769903
}
770904
}
@@ -832,20 +966,24 @@ fn mk_stmts_let(cx: &ExtCtxt, sp: Span) -> Vec<P<ast::Stmt>> {
832966
vec!(stmt_let_sp, stmt_let_tt)
833967
}
834968

835-
fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> Vec<P<ast::Stmt>> {
836-
let mut ss = Vec::new();
969+
fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> (Vec<P<ast::Stmt>>,
970+
Vec<ast::Ident>) {
971+
let mut stmts = Vec::new();
972+
let mut idents = Vec::new();
837973
for tt in tts {
838-
ss.extend(statements_mk_tt(cx, tt, matcher).into_iter());
974+
let (ss, is) = statements_mk_tt(cx, tt, matcher);
975+
stmts.extend(ss.into_iter());
976+
idents.extend(is.into_iter());
839977
}
840-
ss
978+
(stmts, idents)
841979
}
842980

843981
fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
844982
-> (P<ast::Expr>, P<ast::Expr>) {
845983
let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
846984

847985
let mut vector = mk_stmts_let(cx, sp);
848-
vector.extend(statements_mk_tts(cx, &tts[..], false).into_iter());
986+
vector.extend(statements_mk_tts(cx, &tts[..], false).0.into_iter());
849987
let block = cx.expr_block(
850988
cx.block_all(sp,
851989
vector,
@@ -857,13 +995,14 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
857995
fn expand_wrapper(cx: &ExtCtxt,
858996
sp: Span,
859997
cx_expr: P<ast::Expr>,
860-
expr: P<ast::Expr>,
861-
imports: &[&[&str]]) -> P<ast::Expr> {
998+
expr: P<ast::Expr>) -> P<ast::Expr> {
862999
// Explicitly borrow to avoid moving from the invoker (#16992)
8631000
let cx_expr_borrow = cx.expr_addr_of(sp, cx.expr_deref(sp, cx_expr));
8641001
let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr_borrow);
8651002

866-
let stmts = imports.iter().map(|path| {
1003+
let stmts = [
1004+
&["syntax", "ext", "quote", "rt"]
1005+
].iter().map(|path| {
8671006
// make item: `use ...;`
8681007
let path = path.iter().map(|s| s.to_string()).collect();
8691008
cx.stmt_item(sp, cx.item_use_glob(sp, ast::Inherited, ids_ext(path)))
@@ -895,10 +1034,5 @@ fn expand_parse_call(cx: &ExtCtxt,
8951034
let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
8961035
arg_exprs);
8971036

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-
}
1037+
expand_wrapper(cx, sp, cx_expr, expr)
9041038
}

0 commit comments

Comments
 (0)