]> git.proxmox.com Git - rustc.git/blob - src/libsyntax/ext/quote.rs
Imported Upstream version 1.0.0~beta
[rustc.git] / src / libsyntax / ext / quote.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast;
12 use codemap::Span;
13 use ext::base::ExtCtxt;
14 use ext::base;
15 use ext::build::AstBuilder;
16 use parse::token::*;
17 use parse::token;
18 use ptr::P;
19
20 /// Quasiquoting works via token trees.
21 ///
22 /// This is registered as a set of expression syntax extension called quote!
23 /// that lifts its argument token-tree to an AST representing the
24 /// construction of the same token tree, with token::SubstNt interpreted
25 /// as antiquotes (splices).
26
27 pub mod rt {
28 use ast;
29 use codemap::Spanned;
30 use ext::base::ExtCtxt;
31 use parse::token;
32 use parse;
33 use print::pprust;
34 use ptr::P;
35
36 use ast::{TokenTree, Generics, Expr};
37
38 pub use parse::new_parser_from_tts;
39 pub use codemap::{BytePos, Span, dummy_spanned};
40
41 pub trait ToTokens {
42 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
43 }
44
45 impl ToTokens for TokenTree {
46 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
47 vec!(self.clone())
48 }
49 }
50
51 impl<T: ToTokens> ToTokens for Vec<T> {
52 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
53 self.iter().flat_map(|t| t.to_tokens(cx).into_iter()).collect()
54 }
55 }
56
57 impl<T: ToTokens> ToTokens for Spanned<T> {
58 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
59 // FIXME: use the span?
60 self.node.to_tokens(cx)
61 }
62 }
63
64 impl<T: ToTokens> ToTokens for Option<T> {
65 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
66 match self {
67 &Some(ref t) => t.to_tokens(cx),
68 &None => Vec::new(),
69 }
70 }
71 }
72
73 /* Should be (when bugs in default methods are fixed):
74
75 trait ToSource : ToTokens {
76 // Takes a thing and generates a string containing rust code for it.
77 pub fn to_source() -> String;
78
79 // If you can make source, you can definitely make tokens.
80 pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
81 cx.parse_tts(self.to_source())
82 }
83 }
84
85 */
86
87 // FIXME: Move this trait to pprust and get rid of *_to_str?
88 pub trait ToSource {
89 // Takes a thing and generates a string containing rust code for it.
90 fn to_source(&self) -> String;
91 }
92
93 // FIXME (Issue #16472): This should go away after ToToken impls
94 // are revised to go directly to token-trees.
95 trait ToSourceWithHygiene : ToSource {
96 // Takes a thing and generates a string containing rust code
97 // for it, encoding Idents as special byte sequences to
98 // maintain hygiene across serialization and deserialization.
99 fn to_source_with_hygiene(&self) -> String;
100 }
101
102 macro_rules! impl_to_source {
103 (P<$t:ty>, $pp:ident) => (
104 impl ToSource for P<$t> {
105 fn to_source(&self) -> String {
106 pprust::$pp(&**self)
107 }
108 }
109 impl ToSourceWithHygiene for P<$t> {
110 fn to_source_with_hygiene(&self) -> String {
111 pprust::with_hygiene::$pp(&**self)
112 }
113 }
114 );
115 ($t:ty, $pp:ident) => (
116 impl ToSource for $t {
117 fn to_source(&self) -> String {
118 pprust::$pp(self)
119 }
120 }
121 impl ToSourceWithHygiene for $t {
122 fn to_source_with_hygiene(&self) -> String {
123 pprust::with_hygiene::$pp(self)
124 }
125 }
126 );
127 }
128
129 fn slice_to_source<'a, T: ToSource>(sep: &'static str, xs: &'a [T]) -> String {
130 xs.iter()
131 .map(|i| i.to_source())
132 .collect::<Vec<String>>()
133 .connect(sep)
134 .to_string()
135 }
136
137 fn slice_to_source_with_hygiene<'a, T: ToSourceWithHygiene>(
138 sep: &'static str, xs: &'a [T]) -> String {
139 xs.iter()
140 .map(|i| i.to_source_with_hygiene())
141 .collect::<Vec<String>>()
142 .connect(sep)
143 .to_string()
144 }
145
146 macro_rules! impl_to_source_slice {
147 ($t:ty, $sep:expr) => (
148 impl ToSource for [$t] {
149 fn to_source(&self) -> String {
150 slice_to_source($sep, self)
151 }
152 }
153
154 impl ToSourceWithHygiene for [$t] {
155 fn to_source_with_hygiene(&self) -> String {
156 slice_to_source_with_hygiene($sep, self)
157 }
158 }
159 )
160 }
161
162 impl ToSource for ast::Ident {
163 fn to_source(&self) -> String {
164 token::get_ident(*self).to_string()
165 }
166 }
167
168 impl ToSourceWithHygiene for ast::Ident {
169 fn to_source_with_hygiene(&self) -> String {
170 self.encode_with_hygiene()
171 }
172 }
173
174 impl_to_source! { ast::Path, path_to_string }
175 impl_to_source! { ast::Ty, ty_to_string }
176 impl_to_source! { ast::Block, block_to_string }
177 impl_to_source! { ast::Arg, arg_to_string }
178 impl_to_source! { Generics, generics_to_string }
179 impl_to_source! { ast::WhereClause, where_clause_to_string }
180 impl_to_source! { P<ast::Item>, item_to_string }
181 impl_to_source! { P<ast::ImplItem>, impl_item_to_string }
182 impl_to_source! { P<ast::TraitItem>, trait_item_to_string }
183 impl_to_source! { P<ast::Stmt>, stmt_to_string }
184 impl_to_source! { P<ast::Expr>, expr_to_string }
185 impl_to_source! { P<ast::Pat>, pat_to_string }
186 impl_to_source! { ast::Arm, arm_to_string }
187 impl_to_source_slice! { ast::Ty, ", " }
188 impl_to_source_slice! { P<ast::Item>, "\n\n" }
189
190 impl ToSource for ast::Attribute_ {
191 fn to_source(&self) -> String {
192 pprust::attribute_to_string(&dummy_spanned(self.clone()))
193 }
194 }
195 impl ToSourceWithHygiene for ast::Attribute_ {
196 fn to_source_with_hygiene(&self) -> String {
197 self.to_source()
198 }
199 }
200
201 impl ToSource for str {
202 fn to_source(&self) -> String {
203 let lit = dummy_spanned(ast::LitStr(
204 token::intern_and_get_ident(self), ast::CookedStr));
205 pprust::lit_to_string(&lit)
206 }
207 }
208 impl ToSourceWithHygiene for str {
209 fn to_source_with_hygiene(&self) -> String {
210 self.to_source()
211 }
212 }
213
214 impl ToSource for () {
215 fn to_source(&self) -> String {
216 "()".to_string()
217 }
218 }
219 impl ToSourceWithHygiene for () {
220 fn to_source_with_hygiene(&self) -> String {
221 self.to_source()
222 }
223 }
224
225 impl ToSource for bool {
226 fn to_source(&self) -> String {
227 let lit = dummy_spanned(ast::LitBool(*self));
228 pprust::lit_to_string(&lit)
229 }
230 }
231 impl ToSourceWithHygiene for bool {
232 fn to_source_with_hygiene(&self) -> String {
233 self.to_source()
234 }
235 }
236
237 impl ToSource for char {
238 fn to_source(&self) -> String {
239 let lit = dummy_spanned(ast::LitChar(*self));
240 pprust::lit_to_string(&lit)
241 }
242 }
243 impl ToSourceWithHygiene for char {
244 fn to_source_with_hygiene(&self) -> String {
245 self.to_source()
246 }
247 }
248
249 macro_rules! impl_to_source_int {
250 (signed, $t:ty, $tag:expr) => (
251 impl ToSource for $t {
252 fn to_source(&self) -> String {
253 let lit = ast::LitInt(*self as u64, ast::SignedIntLit($tag,
254 ast::Sign::new(*self)));
255 pprust::lit_to_string(&dummy_spanned(lit))
256 }
257 }
258 impl ToSourceWithHygiene for $t {
259 fn to_source_with_hygiene(&self) -> String {
260 self.to_source()
261 }
262 }
263 );
264 (unsigned, $t:ty, $tag:expr) => (
265 impl ToSource for $t {
266 fn to_source(&self) -> String {
267 let lit = ast::LitInt(*self as u64, ast::UnsignedIntLit($tag));
268 pprust::lit_to_string(&dummy_spanned(lit))
269 }
270 }
271 impl ToSourceWithHygiene for $t {
272 fn to_source_with_hygiene(&self) -> String {
273 self.to_source()
274 }
275 }
276 );
277 }
278
279 impl_to_source_int! { signed, isize, ast::TyIs }
280 impl_to_source_int! { signed, i8, ast::TyI8 }
281 impl_to_source_int! { signed, i16, ast::TyI16 }
282 impl_to_source_int! { signed, i32, ast::TyI32 }
283 impl_to_source_int! { signed, i64, ast::TyI64 }
284
285 impl_to_source_int! { unsigned, usize, ast::TyUs }
286 impl_to_source_int! { unsigned, u8, ast::TyU8 }
287 impl_to_source_int! { unsigned, u16, ast::TyU16 }
288 impl_to_source_int! { unsigned, u32, ast::TyU32 }
289 impl_to_source_int! { unsigned, u64, ast::TyU64 }
290
291 // Alas ... we write these out instead. All redundant.
292
293 macro_rules! impl_to_tokens {
294 ($t:ty) => (
295 impl ToTokens for $t {
296 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
297 cx.parse_tts_with_hygiene(self.to_source_with_hygiene())
298 }
299 }
300 )
301 }
302
303 macro_rules! impl_to_tokens_lifetime {
304 ($t:ty) => (
305 impl<'a> ToTokens for $t {
306 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
307 cx.parse_tts_with_hygiene(self.to_source_with_hygiene())
308 }
309 }
310 )
311 }
312
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_ }
330 impl_to_tokens_lifetime! { &'a str }
331 impl_to_tokens! { () }
332 impl_to_tokens! { char }
333 impl_to_tokens! { bool }
334 impl_to_tokens! { isize }
335 impl_to_tokens! { i8 }
336 impl_to_tokens! { i16 }
337 impl_to_tokens! { i32 }
338 impl_to_tokens! { i64 }
339 impl_to_tokens! { usize }
340 impl_to_tokens! { u8 }
341 impl_to_tokens! { u16 }
342 impl_to_tokens! { u32 }
343 impl_to_tokens! { u64 }
344
345 pub trait ExtParseUtils {
346 fn parse_item(&self, s: String) -> P<ast::Item>;
347 fn parse_expr(&self, s: String) -> P<ast::Expr>;
348 fn parse_stmt(&self, s: String) -> P<ast::Stmt>;
349 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree>;
350 }
351
352 trait ExtParseUtilsWithHygiene {
353 // FIXME (Issue #16472): This should go away after ToToken impls
354 // are revised to go directly to token-trees.
355 fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree>;
356 }
357
358 impl<'a> ExtParseUtils for ExtCtxt<'a> {
359
360 fn parse_item(&self, s: String) -> P<ast::Item> {
361 parse::parse_item_from_source_str(
362 "<quote expansion>".to_string(),
363 s,
364 self.cfg(),
365 self.parse_sess()).expect("parse error")
366 }
367
368 fn parse_stmt(&self, s: String) -> P<ast::Stmt> {
369 parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
370 s,
371 self.cfg(),
372 self.parse_sess()).expect("parse error")
373 }
374
375 fn parse_expr(&self, s: String) -> P<ast::Expr> {
376 parse::parse_expr_from_source_str("<quote expansion>".to_string(),
377 s,
378 self.cfg(),
379 self.parse_sess())
380 }
381
382 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
383 parse::parse_tts_from_source_str("<quote expansion>".to_string(),
384 s,
385 self.cfg(),
386 self.parse_sess())
387 }
388 }
389
390 impl<'a> ExtParseUtilsWithHygiene for ExtCtxt<'a> {
391
392 fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree> {
393 use parse::with_hygiene::parse_tts_from_source_str;
394 parse_tts_from_source_str("<quote expansion>".to_string(),
395 s,
396 self.cfg(),
397 self.parse_sess())
398 }
399
400 }
401
402 }
403
404 pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt,
405 sp: Span,
406 tts: &[ast::TokenTree])
407 -> Box<base::MacResult+'cx> {
408 let (cx_expr, expr) = expand_tts(cx, sp, tts);
409 let expanded = expand_wrapper(cx, sp, cx_expr, expr, &[&["syntax", "ext", "quote", "rt"]]);
410 base::MacEager::expr(expanded)
411 }
412
413 pub fn expand_quote_expr<'cx>(cx: &'cx mut ExtCtxt,
414 sp: Span,
415 tts: &[ast::TokenTree])
416 -> Box<base::MacResult+'cx> {
417 let expanded = expand_parse_call(cx, sp, "parse_expr", vec!(), tts);
418 base::MacEager::expr(expanded)
419 }
420
421 pub fn expand_quote_item<'cx>(cx: &mut ExtCtxt,
422 sp: Span,
423 tts: &[ast::TokenTree])
424 -> Box<base::MacResult+'cx> {
425 let expanded = expand_parse_call(cx, sp, "parse_item", vec!(), tts);
426 base::MacEager::expr(expanded)
427 }
428
429 pub fn expand_quote_pat<'cx>(cx: &'cx mut ExtCtxt,
430 sp: Span,
431 tts: &[ast::TokenTree])
432 -> Box<base::MacResult+'cx> {
433 let expanded = expand_parse_call(cx, sp, "parse_pat", vec!(), tts);
434 base::MacEager::expr(expanded)
435 }
436
437 pub fn expand_quote_arm(cx: &mut ExtCtxt,
438 sp: Span,
439 tts: &[ast::TokenTree])
440 -> Box<base::MacResult+'static> {
441 let expanded = expand_parse_call(cx, sp, "parse_arm", vec!(), tts);
442 base::MacEager::expr(expanded)
443 }
444
445 pub fn expand_quote_ty(cx: &mut ExtCtxt,
446 sp: Span,
447 tts: &[ast::TokenTree])
448 -> Box<base::MacResult+'static> {
449 let expanded = expand_parse_call(cx, sp, "parse_ty", vec!(), tts);
450 base::MacEager::expr(expanded)
451 }
452
453 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
454 sp: Span,
455 tts: &[ast::TokenTree])
456 -> Box<base::MacResult+'static> {
457 let expanded = expand_parse_call(cx, sp, "parse_stmt", vec!(), tts);
458 base::MacEager::expr(expanded)
459 }
460
461 pub fn expand_quote_attr(cx: &mut ExtCtxt,
462 sp: Span,
463 tts: &[ast::TokenTree])
464 -> Box<base::MacResult+'static> {
465 let expanded = expand_parse_call(cx, sp, "parse_attribute",
466 vec!(cx.expr_bool(sp, true)), tts);
467
468 base::MacEager::expr(expanded)
469 }
470
471 pub fn expand_quote_matcher(cx: &mut ExtCtxt,
472 sp: Span,
473 tts: &[ast::TokenTree])
474 -> Box<base::MacResult+'static> {
475 let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
476 let mut vector = mk_stmts_let(cx, sp);
477 vector.extend(statements_mk_tts(cx, &tts[..], true).into_iter());
478 let block = cx.expr_block(
479 cx.block_all(sp,
480 vector,
481 Some(cx.expr_ident(sp, id_ext("tt")))));
482
483 let expanded = expand_wrapper(cx, sp, cx_expr, block, &[&["syntax", "ext", "quote", "rt"]]);
484 base::MacEager::expr(expanded)
485 }
486
487 fn ids_ext(strs: Vec<String> ) -> Vec<ast::Ident> {
488 strs.iter().map(|str| str_to_ident(&(*str))).collect()
489 }
490
491 fn id_ext(str: &str) -> ast::Ident {
492 str_to_ident(str)
493 }
494
495 // Lift an ident to the expr that evaluates to that ident.
496 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> P<ast::Expr> {
497 let e_str = cx.expr_str(sp, token::get_ident(ident));
498 cx.expr_method_call(sp,
499 cx.expr_ident(sp, id_ext("ext_cx")),
500 id_ext("ident_of"),
501 vec!(e_str))
502 }
503
504 // Lift a name to the expr that evaluates to that name
505 fn mk_name(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> P<ast::Expr> {
506 let e_str = cx.expr_str(sp, token::get_ident(ident));
507 cx.expr_method_call(sp,
508 cx.expr_ident(sp, id_ext("ext_cx")),
509 id_ext("name_of"),
510 vec!(e_str))
511 }
512
513 fn mk_ast_path(cx: &ExtCtxt, sp: Span, name: &str) -> P<ast::Expr> {
514 let idents = vec!(id_ext("syntax"), id_ext("ast"), id_ext(name));
515 cx.expr_path(cx.path_global(sp, idents))
516 }
517
518 fn mk_token_path(cx: &ExtCtxt, sp: Span, name: &str) -> P<ast::Expr> {
519 let idents = vec!(id_ext("syntax"), id_ext("parse"), id_ext("token"), id_ext(name));
520 cx.expr_path(cx.path_global(sp, idents))
521 }
522
523 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOpToken) -> P<ast::Expr> {
524 let name = match bop {
525 token::Plus => "Plus",
526 token::Minus => "Minus",
527 token::Star => "Star",
528 token::Slash => "Slash",
529 token::Percent => "Percent",
530 token::Caret => "Caret",
531 token::And => "And",
532 token::Or => "Or",
533 token::Shl => "Shl",
534 token::Shr => "Shr"
535 };
536 mk_token_path(cx, sp, name)
537 }
538
539 fn mk_delim(cx: &ExtCtxt, sp: Span, delim: token::DelimToken) -> P<ast::Expr> {
540 let name = match delim {
541 token::Paren => "Paren",
542 token::Bracket => "Bracket",
543 token::Brace => "Brace",
544 };
545 mk_token_path(cx, sp, name)
546 }
547
548 #[allow(non_upper_case_globals)]
549 fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
550 macro_rules! mk_lit {
551 ($name: expr, $suffix: expr, $($args: expr),*) => {{
552 let inner = cx.expr_call(sp, mk_token_path(cx, sp, $name), vec![$($args),*]);
553 let suffix = match $suffix {
554 Some(name) => cx.expr_some(sp, mk_name(cx, sp, ast::Ident::new(name))),
555 None => cx.expr_none(sp)
556 };
557 cx.expr_call(sp, mk_token_path(cx, sp, "Literal"), vec![inner, suffix])
558 }}
559 }
560 match *tok {
561 token::BinOp(binop) => {
562 return cx.expr_call(sp, mk_token_path(cx, sp, "BinOp"), vec!(mk_binop(cx, sp, binop)));
563 }
564 token::BinOpEq(binop) => {
565 return cx.expr_call(sp, mk_token_path(cx, sp, "BinOpEq"),
566 vec!(mk_binop(cx, sp, binop)));
567 }
568
569 token::OpenDelim(delim) => {
570 return cx.expr_call(sp, mk_token_path(cx, sp, "OpenDelim"),
571 vec![mk_delim(cx, sp, delim)]);
572 }
573 token::CloseDelim(delim) => {
574 return cx.expr_call(sp, mk_token_path(cx, sp, "CloseDelim"),
575 vec![mk_delim(cx, sp, delim)]);
576 }
577
578 token::Literal(token::Byte(i), suf) => {
579 let e_byte = mk_name(cx, sp, i.ident());
580 return mk_lit!("Byte", suf, e_byte);
581 }
582
583 token::Literal(token::Char(i), suf) => {
584 let e_char = mk_name(cx, sp, i.ident());
585 return mk_lit!("Char", suf, e_char);
586 }
587
588 token::Literal(token::Integer(i), suf) => {
589 let e_int = mk_name(cx, sp, i.ident());
590 return mk_lit!("Integer", suf, e_int);
591 }
592
593 token::Literal(token::Float(fident), suf) => {
594 let e_fident = mk_name(cx, sp, fident.ident());
595 return mk_lit!("Float", suf, e_fident);
596 }
597
598 token::Literal(token::Str_(ident), suf) => {
599 return mk_lit!("Str_", suf, mk_name(cx, sp, ident.ident()))
600 }
601
602 token::Literal(token::StrRaw(ident, n), suf) => {
603 return mk_lit!("StrRaw", suf, mk_name(cx, sp, ident.ident()), cx.expr_usize(sp, n))
604 }
605
606 token::Ident(ident, style) => {
607 return cx.expr_call(sp,
608 mk_token_path(cx, sp, "Ident"),
609 vec![mk_ident(cx, sp, ident),
610 match style {
611 ModName => mk_token_path(cx, sp, "ModName"),
612 Plain => mk_token_path(cx, sp, "Plain"),
613 }]);
614 }
615
616 token::Lifetime(ident) => {
617 return cx.expr_call(sp,
618 mk_token_path(cx, sp, "Lifetime"),
619 vec!(mk_ident(cx, sp, ident)));
620 }
621
622 token::DocComment(ident) => {
623 return cx.expr_call(sp,
624 mk_token_path(cx, sp, "DocComment"),
625 vec!(mk_name(cx, sp, ident.ident())));
626 }
627
628 token::MatchNt(name, kind, namep, kindp) => {
629 return cx.expr_call(sp,
630 mk_token_path(cx, sp, "MatchNt"),
631 vec!(mk_ident(cx, sp, name),
632 mk_ident(cx, sp, kind),
633 match namep {
634 ModName => mk_token_path(cx, sp, "ModName"),
635 Plain => mk_token_path(cx, sp, "Plain"),
636 },
637 match kindp {
638 ModName => mk_token_path(cx, sp, "ModName"),
639 Plain => mk_token_path(cx, sp, "Plain"),
640 }));
641 }
642
643 token::Interpolated(_) => panic!("quote! with interpolated token"),
644
645 _ => ()
646 }
647
648 let name = match *tok {
649 token::Eq => "Eq",
650 token::Lt => "Lt",
651 token::Le => "Le",
652 token::EqEq => "EqEq",
653 token::Ne => "Ne",
654 token::Ge => "Ge",
655 token::Gt => "Gt",
656 token::AndAnd => "AndAnd",
657 token::OrOr => "OrOr",
658 token::Not => "Not",
659 token::Tilde => "Tilde",
660 token::At => "At",
661 token::Dot => "Dot",
662 token::DotDot => "DotDot",
663 token::Comma => "Comma",
664 token::Semi => "Semi",
665 token::Colon => "Colon",
666 token::ModSep => "ModSep",
667 token::RArrow => "RArrow",
668 token::LArrow => "LArrow",
669 token::FatArrow => "FatArrow",
670 token::Pound => "Pound",
671 token::Dollar => "Dollar",
672 token::Question => "Question",
673 token::Underscore => "Underscore",
674 token::Eof => "Eof",
675 _ => panic!("unhandled token in quote!"),
676 };
677 mk_token_path(cx, sp, name)
678 }
679
680 fn statements_mk_tt(cx: &ExtCtxt, tt: &ast::TokenTree, matcher: bool) -> Vec<P<ast::Stmt>> {
681 match *tt {
682 ast::TtToken(sp, SubstNt(ident, _)) => {
683 // tt.extend($ident.to_tokens(ext_cx).into_iter())
684
685 let e_to_toks =
686 cx.expr_method_call(sp,
687 cx.expr_ident(sp, ident),
688 id_ext("to_tokens"),
689 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
690 let e_to_toks =
691 cx.expr_method_call(sp, e_to_toks, id_ext("into_iter"), vec![]);
692
693 let e_push =
694 cx.expr_method_call(sp,
695 cx.expr_ident(sp, id_ext("tt")),
696 id_ext("extend"),
697 vec!(e_to_toks));
698
699 vec!(cx.stmt_expr(e_push))
700 }
701 ref tt @ ast::TtToken(_, MatchNt(..)) if !matcher => {
702 let mut seq = vec![];
703 for i in 0..tt.len() {
704 seq.push(tt.get_tt(i));
705 }
706 statements_mk_tts(cx, &seq[..], matcher)
707 }
708 ast::TtToken(sp, ref tok) => {
709 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
710 let e_tok = cx.expr_call(sp,
711 mk_ast_path(cx, sp, "TtToken"),
712 vec!(e_sp, expr_mk_token(cx, sp, tok)));
713 let e_push =
714 cx.expr_method_call(sp,
715 cx.expr_ident(sp, id_ext("tt")),
716 id_ext("push"),
717 vec!(e_tok));
718 vec!(cx.stmt_expr(e_push))
719 },
720 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()
726 },
727 ast::TtSequence(sp, ref seq) => {
728 if !matcher {
729 panic!("TtSequence in quote!");
730 }
731
732 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
733
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))
768 }
769 }
770 }
771
772 fn parse_arguments_to_quote(cx: &ExtCtxt, tts: &[ast::TokenTree])
773 -> (P<ast::Expr>, Vec<ast::TokenTree>) {
774 // NB: It appears that the main parser loses its mind if we consider
775 // $foo as a SubstNt during the main parse, so we have to re-parse
776 // under quote_depth > 0. This is silly and should go away; the _guess_ is
777 // it has to do with transition away from supporting old-style macros, so
778 // try removing it when enough of them are gone.
779
780 let mut p = cx.new_parser_from_tts(tts);
781 p.quote_depth += 1;
782
783 let cx_expr = p.parse_expr();
784 if !p.eat(&token::Comma) {
785 p.fatal("expected token `,`");
786 }
787
788 let tts = p.parse_all_token_trees();
789 p.abort_if_errors();
790
791 (cx_expr, tts)
792 }
793
794 fn mk_stmts_let(cx: &ExtCtxt, sp: Span) -> Vec<P<ast::Stmt>> {
795 // We also bind a single value, sp, to ext_cx.call_site()
796 //
797 // This causes every span in a token-tree quote to be attributed to the
798 // call site of the extension using the quote. We can't really do much
799 // better since the source of the quote may well be in a library that
800 // was not even parsed by this compilation run, that the user has no
801 // source code for (eg. in libsyntax, which they're just _using_).
802 //
803 // The old quasiquoter had an elaborate mechanism for denoting input
804 // file locations from which quotes originated; unfortunately this
805 // relied on feeding the source string of the quote back into the
806 // compiler (which we don't really want to do) and, in any case, only
807 // pushed the problem a very small step further back: an error
808 // resulting from a parse of the resulting quote is still attributed to
809 // the site the string literal occurred, which was in a source file
810 // _other_ than the one the user has control over. For example, an
811 // error in a quote from the protocol compiler, invoked in user code
812 // using macro_rules! for example, will be attributed to the macro_rules.rs
813 // file in libsyntax, which the user might not even have source to (unless
814 // they happen to have a compiler on hand). Over all, the phase distinction
815 // just makes quotes "hard to attribute". Possibly this could be fixed
816 // by recreating some of the original qq machinery in the tt regime
817 // (pushing fake FileMaps onto the parser to account for original sites
818 // of quotes, for example) but at this point it seems not likely to be
819 // worth the hassle.
820
821 let e_sp = cx.expr_method_call(sp,
822 cx.expr_ident(sp, id_ext("ext_cx")),
823 id_ext("call_site"),
824 Vec::new());
825
826 let stmt_let_sp = cx.stmt_let(sp, false,
827 id_ext("_sp"),
828 e_sp);
829
830 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
831
832 vec!(stmt_let_sp, stmt_let_tt)
833 }
834
835 fn statements_mk_tts(cx: &ExtCtxt, tts: &[ast::TokenTree], matcher: bool) -> Vec<P<ast::Stmt>> {
836 let mut ss = Vec::new();
837 for tt in tts {
838 ss.extend(statements_mk_tt(cx, tt, matcher).into_iter());
839 }
840 ss
841 }
842
843 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
844 -> (P<ast::Expr>, P<ast::Expr>) {
845 let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
846
847 let mut vector = mk_stmts_let(cx, sp);
848 vector.extend(statements_mk_tts(cx, &tts[..], false).into_iter());
849 let block = cx.expr_block(
850 cx.block_all(sp,
851 vector,
852 Some(cx.expr_ident(sp, id_ext("tt")))));
853
854 (cx_expr, block)
855 }
856
857 fn expand_wrapper(cx: &ExtCtxt,
858 sp: Span,
859 cx_expr: P<ast::Expr>,
860 expr: P<ast::Expr>,
861 imports: &[&[&str]]) -> P<ast::Expr> {
862 // Explicitly borrow to avoid moving from the invoker (#16992)
863 let cx_expr_borrow = cx.expr_addr_of(sp, cx.expr_deref(sp, cx_expr));
864 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr_borrow);
865
866 let stmts = imports.iter().map(|path| {
867 // make item: `use ...;`
868 let path = path.iter().map(|s| s.to_string()).collect();
869 cx.stmt_item(sp, cx.item_use_glob(sp, ast::Inherited, ids_ext(path)))
870 }).chain(Some(stmt_let_ext_cx).into_iter()).collect();
871
872 cx.expr_block(cx.block_all(sp, stmts, Some(expr)))
873 }
874
875 fn expand_parse_call(cx: &ExtCtxt,
876 sp: Span,
877 parse_method: &str,
878 arg_exprs: Vec<P<ast::Expr>> ,
879 tts: &[ast::TokenTree]) -> P<ast::Expr> {
880 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
881
882 let cfg_call = || cx.expr_method_call(
883 sp, cx.expr_ident(sp, id_ext("ext_cx")),
884 id_ext("cfg"), Vec::new());
885
886 let parse_sess_call = || cx.expr_method_call(
887 sp, cx.expr_ident(sp, id_ext("ext_cx")),
888 id_ext("parse_sess"), Vec::new());
889
890 let new_parser_call =
891 cx.expr_call(sp,
892 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
893 vec!(parse_sess_call(), cfg_call(), tts_expr));
894
895 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
896 arg_exprs);
897
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 }
904 }