1 // Copyright 2012 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.
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.
13 use ext::base::ExtCtxt;
15 use ext::build::AstBuilder;
24 * Quasiquoting works via token trees.
26 * This is registered as a set of expression syntax extension called quote!
27 * that lifts its argument token-tree to an AST representing the
28 * construction of the same token tree, with ast::TTNonterminal nodes
29 * interpreted as antiquotes (splices).
35 use ext::base::ExtCtxt;
40 use ast::{TokenTree, Generics, Expr};
42 pub use parse::new_parser_from_tts;
43 pub use codemap::{BytePos, Span, dummy_spanned};
48 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
51 impl ToTokens for Vec<TokenTree> {
52 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
57 /* Should be (when bugs in default methods are fixed):
59 trait ToSource : ToTokens {
60 // Takes a thing and generates a string containing rust code for it.
61 pub fn to_source() -> String;
63 // If you can make source, you can definitely make tokens.
64 pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
65 cx.parse_tts(self.to_source())
72 // Takes a thing and generates a string containing rust code for it.
73 fn to_source(&self) -> String;
76 impl ToSource for ast::Ident {
77 fn to_source(&self) -> String {
78 token::get_ident(*self).get().to_string()
82 impl ToSource for Gc<ast::Item> {
83 fn to_source(&self) -> String {
84 pprust::item_to_str(&**self)
88 impl<'a> ToSource for &'a [Gc<ast::Item>] {
89 fn to_source(&self) -> String {
91 .map(|i| i.to_source())
92 .collect::<Vec<String>>()
98 impl ToSource for ast::Ty {
99 fn to_source(&self) -> String {
100 pprust::ty_to_str(self)
104 impl<'a> ToSource for &'a [ast::Ty] {
105 fn to_source(&self) -> String {
107 .map(|i| i.to_source())
108 .collect::<Vec<String>>()
114 impl ToSource for Generics {
115 fn to_source(&self) -> String {
116 pprust::generics_to_str(self)
120 impl ToSource for Gc<ast::Expr> {
121 fn to_source(&self) -> String {
122 pprust::expr_to_str(&**self)
126 impl ToSource for ast::Block {
127 fn to_source(&self) -> String {
128 pprust::block_to_str(self)
132 impl ToSource for ast::Arg {
133 fn to_source(&self) -> String {
134 pprust::arg_to_str(self)
138 impl<'a> ToSource for &'a str {
139 fn to_source(&self) -> String {
140 let lit = dummy_spanned(ast::LitStr(
141 token::intern_and_get_ident(*self), ast::CookedStr));
142 pprust::lit_to_str(&lit)
146 impl ToSource for () {
147 fn to_source(&self) -> String {
152 impl ToSource for bool {
153 fn to_source(&self) -> String {
154 let lit = dummy_spanned(ast::LitBool(*self));
155 pprust::lit_to_str(&lit)
159 impl ToSource for char {
160 fn to_source(&self) -> String {
161 let lit = dummy_spanned(ast::LitChar(*self));
162 pprust::lit_to_str(&lit)
166 impl ToSource for int {
167 fn to_source(&self) -> String {
168 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI));
169 pprust::lit_to_str(&lit)
173 impl ToSource for i8 {
174 fn to_source(&self) -> String {
175 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI8));
176 pprust::lit_to_str(&lit)
180 impl ToSource for i16 {
181 fn to_source(&self) -> String {
182 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI16));
183 pprust::lit_to_str(&lit)
188 impl ToSource for i32 {
189 fn to_source(&self) -> String {
190 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI32));
191 pprust::lit_to_str(&lit)
195 impl ToSource for i64 {
196 fn to_source(&self) -> String {
197 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI64));
198 pprust::lit_to_str(&lit)
202 impl ToSource for uint {
203 fn to_source(&self) -> String {
204 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU));
205 pprust::lit_to_str(&lit)
209 impl ToSource for u8 {
210 fn to_source(&self) -> String {
211 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU8));
212 pprust::lit_to_str(&lit)
216 impl ToSource for u16 {
217 fn to_source(&self) -> String {
218 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU16));
219 pprust::lit_to_str(&lit)
223 impl ToSource for u32 {
224 fn to_source(&self) -> String {
225 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU32));
226 pprust::lit_to_str(&lit)
230 impl ToSource for u64 {
231 fn to_source(&self) -> String {
232 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU64));
233 pprust::lit_to_str(&lit)
237 // Alas ... we write these out instead. All redundant.
239 macro_rules! impl_to_tokens(
241 impl ToTokens for $t {
242 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
243 cx.parse_tts(self.to_source())
249 macro_rules! impl_to_tokens_self(
251 impl<'a> ToTokens for $t {
252 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
253 cx.parse_tts(self.to_source())
259 impl_to_tokens!(ast::Ident)
260 impl_to_tokens!(Gc<ast::Item>)
261 impl_to_tokens_self!(&'a [Gc<ast::Item>])
262 impl_to_tokens!(ast::Ty)
263 impl_to_tokens_self!(&'a [ast::Ty])
264 impl_to_tokens!(Generics)
265 impl_to_tokens!(Gc<ast::Expr>)
266 impl_to_tokens!(ast::Block)
267 impl_to_tokens!(ast::Arg)
268 impl_to_tokens_self!(&'a str)
270 impl_to_tokens!(char)
271 impl_to_tokens!(bool)
277 impl_to_tokens!(uint)
283 pub trait ExtParseUtils {
284 fn parse_item(&self, s: String) -> Gc<ast::Item>;
285 fn parse_expr(&self, s: String) -> Gc<ast::Expr>;
286 fn parse_stmt(&self, s: String) -> Gc<ast::Stmt>;
287 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> ;
290 impl<'a> ExtParseUtils for ExtCtxt<'a> {
292 fn parse_item(&self, s: String) -> Gc<ast::Item> {
293 let res = parse::parse_item_from_source_str(
294 "<quote expansion>".to_string(),
301 error!("parse error");
307 fn parse_stmt(&self, s: String) -> Gc<ast::Stmt> {
308 parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
315 fn parse_expr(&self, s: String) -> Gc<ast::Expr> {
316 parse::parse_expr_from_source_str("<quote expansion>".to_string(),
322 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
323 parse::parse_tts_from_source_str("<quote expansion>".to_string(),
332 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
334 tts: &[ast::TokenTree])
335 -> Box<base::MacResult> {
336 let (cx_expr, expr) = expand_tts(cx, sp, tts);
337 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
338 base::MacExpr::new(expanded)
341 pub fn expand_quote_expr(cx: &mut ExtCtxt,
343 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
344 let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
345 base::MacExpr::new(expanded)
348 pub fn expand_quote_item(cx: &mut ExtCtxt,
350 tts: &[ast::TokenTree])
351 -> Box<base::MacResult> {
352 let expanded = expand_parse_call(cx, sp, "parse_item_with_outer_attributes",
354 base::MacExpr::new(expanded)
357 pub fn expand_quote_pat(cx: &mut ExtCtxt,
359 tts: &[ast::TokenTree])
360 -> Box<base::MacResult> {
361 let expanded = expand_parse_call(cx, sp, "parse_pat", vec!(), tts);
362 base::MacExpr::new(expanded)
365 pub fn expand_quote_ty(cx: &mut ExtCtxt,
367 tts: &[ast::TokenTree])
368 -> Box<base::MacResult> {
369 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
370 let expanded = expand_parse_call(cx, sp, "parse_ty",
371 vec!(e_param_colons), tts);
372 base::MacExpr::new(expanded)
375 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
377 tts: &[ast::TokenTree])
378 -> Box<base::MacResult> {
379 let e_attrs = cx.expr_vec_ng(sp);
380 let expanded = expand_parse_call(cx, sp, "parse_stmt",
382 base::MacExpr::new(expanded)
385 fn ids_ext(strs: Vec<String> ) -> Vec<ast::Ident> {
386 strs.iter().map(|str| str_to_ident((*str).as_slice())).collect()
389 fn id_ext(str: &str) -> ast::Ident {
393 // Lift an ident to the expr that evaluates to that ident.
394 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> Gc<ast::Expr> {
395 let e_str = cx.expr_str(sp, token::get_ident(ident));
396 cx.expr_method_call(sp,
397 cx.expr_ident(sp, id_ext("ext_cx")),
402 fn mk_ast_path(cx: &ExtCtxt, sp: Span, name: &str) -> Gc<ast::Expr> {
403 let idents = vec!(id_ext("syntax"), id_ext("ast"), id_ext(name));
404 cx.expr_path(cx.path_global(sp, idents))
407 fn mk_token_path(cx: &ExtCtxt, sp: Span, name: &str) -> Gc<ast::Expr> {
408 let idents = vec!(id_ext("syntax"), id_ext("parse"), id_ext("token"), id_ext(name));
409 cx.expr_path(cx.path_global(sp, idents))
412 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> Gc<ast::Expr> {
413 let name = match bop {
418 PERCENT => "PERCENT",
425 mk_token_path(cx, sp, name)
428 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> Gc<ast::Expr> {
432 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOP"), vec!(mk_binop(cx, sp, binop)));
435 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOPEQ"),
436 vec!(mk_binop(cx, sp, binop)));
440 let e_byte = cx.expr_lit(sp, ast::LitByte(i));
442 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_BYTE"), vec!(e_byte));
446 let e_char = cx.expr_lit(sp, ast::LitChar(i));
448 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_CHAR"), vec!(e_char));
452 let s_ity = match ity {
455 ast::TyI16 => "TyI16",
456 ast::TyI32 => "TyI32",
457 ast::TyI64 => "TyI64"
459 let e_ity = mk_ast_path(cx, sp, s_ity);
460 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
461 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT"), vec!(e_i64, e_ity));
464 LIT_UINT(u, uty) => {
465 let s_uty = match uty {
468 ast::TyU16 => "TyU16",
469 ast::TyU32 => "TyU32",
470 ast::TyU64 => "TyU64"
472 let e_uty = mk_ast_path(cx, sp, s_uty);
473 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
474 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_UINT"), vec!(e_u64, e_uty));
477 LIT_INT_UNSUFFIXED(i) => {
478 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
479 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT_UNSUFFIXED"), vec!(e_i64));
482 LIT_FLOAT(fident, fty) => {
483 let s_fty = match fty {
484 ast::TyF32 => "TyF32",
485 ast::TyF64 => "TyF64",
486 ast::TyF128 => "TyF128"
488 let e_fty = mk_ast_path(cx, sp, s_fty);
489 let e_fident = mk_ident(cx, sp, fident);
490 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_FLOAT"), vec!(e_fident, e_fty));
494 return cx.expr_call(sp,
495 mk_token_path(cx, sp, "LIT_STR"),
496 vec!(mk_ident(cx, sp, ident)));
499 LIT_STR_RAW(ident, n) => {
500 return cx.expr_call(sp,
501 mk_token_path(cx, sp, "LIT_STR_RAW"),
502 vec!(mk_ident(cx, sp, ident), cx.expr_uint(sp, n)));
506 return cx.expr_call(sp,
507 mk_token_path(cx, sp, "IDENT"),
508 vec!(mk_ident(cx, sp, ident), cx.expr_bool(sp, b)));
512 return cx.expr_call(sp,
513 mk_token_path(cx, sp, "LIFETIME"),
514 vec!(mk_ident(cx, sp, ident)));
517 DOC_COMMENT(ident) => {
518 return cx.expr_call(sp,
519 mk_token_path(cx, sp, "DOC_COMMENT"),
520 vec!(mk_ident(cx, sp, ident)));
523 INTERPOLATED(_) => fail!("quote! with interpolated token"),
528 let name = match *tok {
546 MOD_SEP => "MOD_SEP",
549 FAT_ARROW => "FAT_ARROW",
552 LBRACKET => "LBRACKET",
553 RBRACKET => "RBRACKET",
558 UNDERSCORE => "UNDERSCORE",
562 mk_token_path(cx, sp, name)
566 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<Gc<ast::Stmt>> {
568 ast::TTTok(sp, ref tok) => {
569 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
570 let e_tok = cx.expr_call(sp,
571 mk_ast_path(cx, sp, "TTTok"),
572 vec!(e_sp, mk_token(cx, sp, tok)));
574 cx.expr_method_call(sp,
575 cx.expr_ident(sp, id_ext("tt")),
578 vec!(cx.stmt_expr(e_push))
581 ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
582 ast::TTSeq(..) => fail!("TTSeq in quote!"),
584 ast::TTNonterminal(sp, ident) => {
586 // tt.push_all_move($ident.to_tokens(ext_cx))
589 cx.expr_method_call(sp,
590 cx.expr_ident(sp, ident),
592 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
595 cx.expr_method_call(sp,
596 cx.expr_ident(sp, id_ext("tt")),
597 id_ext("push_all_move"),
600 vec!(cx.stmt_expr(e_push))
605 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
606 -> Vec<Gc<ast::Stmt>> {
607 let mut ss = Vec::new();
608 for tt in tts.iter() {
609 ss.push_all_move(mk_tt(cx, sp, tt));
614 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
615 -> (Gc<ast::Expr>, Gc<ast::Expr>) {
616 // NB: It appears that the main parser loses its mind if we consider
617 // $foo as a TTNonterminal during the main parse, so we have to re-parse
618 // under quote_depth > 0. This is silly and should go away; the _guess_ is
619 // it has to do with transition away from supporting old-style macros, so
620 // try removing it when enough of them are gone.
622 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
625 .map(|x| (*x).clone())
629 let cx_expr = p.parse_expr();
630 if !p.eat(&token::COMMA) {
631 p.fatal("expected token `,`");
634 let tts = p.parse_all_token_trees();
637 // We also bind a single value, sp, to ext_cx.call_site()
639 // This causes every span in a token-tree quote to be attributed to the
640 // call site of the extension using the quote. We can't really do much
641 // better since the source of the quote may well be in a library that
642 // was not even parsed by this compilation run, that the user has no
643 // source code for (eg. in libsyntax, which they're just _using_).
645 // The old quasiquoter had an elaborate mechanism for denoting input
646 // file locations from which quotes originated; unfortunately this
647 // relied on feeding the source string of the quote back into the
648 // compiler (which we don't really want to do) and, in any case, only
649 // pushed the problem a very small step further back: an error
650 // resulting from a parse of the resulting quote is still attributed to
651 // the site the string literal occurred, which was in a source file
652 // _other_ than the one the user has control over. For example, an
653 // error in a quote from the protocol compiler, invoked in user code
654 // using macro_rules! for example, will be attributed to the macro_rules.rs
655 // file in libsyntax, which the user might not even have source to (unless
656 // they happen to have a compiler on hand). Over all, the phase distinction
657 // just makes quotes "hard to attribute". Possibly this could be fixed
658 // by recreating some of the original qq machinery in the tt regime
659 // (pushing fake FileMaps onto the parser to account for original sites
660 // of quotes, for example) but at this point it seems not likely to be
663 let e_sp = cx.expr_method_call(sp,
664 cx.expr_ident(sp, id_ext("ext_cx")),
668 let stmt_let_sp = cx.stmt_let(sp, false,
672 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
674 let mut vector = vec!(stmt_let_sp, stmt_let_tt);
675 vector.push_all_move(mk_tts(cx, sp, tts.as_slice()));
676 let block = cx.expr_block(
680 Some(cx.expr_ident(sp, id_ext("tt")))));
685 fn expand_wrapper(cx: &ExtCtxt,
687 cx_expr: Gc<ast::Expr>,
688 expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
690 &["syntax", "ext", "quote", "rt"],
691 ].iter().map(|path| {
692 let path = path.iter().map(|s| s.to_string()).collect();
693 cx.view_use_glob(sp, ast::Inherited, ids_ext(path))
696 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
698 cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
701 fn expand_parse_call(cx: &ExtCtxt,
704 arg_exprs: Vec<Gc<ast::Expr>>,
705 tts: &[ast::TokenTree]) -> Gc<ast::Expr> {
706 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
708 let cfg_call = || cx.expr_method_call(
709 sp, cx.expr_ident(sp, id_ext("ext_cx")),
710 id_ext("cfg"), Vec::new());
712 let parse_sess_call = || cx.expr_method_call(
713 sp, cx.expr_ident(sp, id_ext("ext_cx")),
714 id_ext("parse_sess"), Vec::new());
716 let new_parser_call =
718 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
719 vec!(parse_sess_call(), cfg_call(), tts_expr));
721 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
724 expand_wrapper(cx, sp, cx_expr, expr)