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;
41 use ast::{TokenTree, Generics, Expr};
43 // NOTE remove this after snapshot
44 // (stage0 quasiquoter needs this)
46 pub use ast::{Generics, TokenTree, TTTok};
48 pub use parse::token::{IDENT, SEMI, LBRACE, RBRACE, LIFETIME, COLON, AND, BINOP, EQ,
49 LBRACKET, RBRACKET, LPAREN, RPAREN, POUND, NOT, MOD_SEP, DOT, COMMA};
51 pub use parse::new_parser_from_tts;
52 pub use codemap::{BytePos, Span, dummy_spanned};
57 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
60 impl ToTokens for Vec<TokenTree> {
61 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
66 /* Should be (when bugs in default methods are fixed):
68 trait ToSource : ToTokens {
69 // Takes a thing and generates a string containing rust code for it.
70 pub fn to_source() -> String;
72 // If you can make source, you can definitely make tokens.
73 pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
74 cx.parse_tts(self.to_source())
81 // Takes a thing and generates a string containing rust code for it.
82 fn to_source(&self) -> String;
85 impl ToSource for ast::Ident {
86 fn to_source(&self) -> String {
87 token::get_ident(*self).get().to_string()
91 impl ToSource for Gc<ast::Item> {
92 fn to_source(&self) -> String {
93 pprust::item_to_str(&**self)
97 impl<'a> ToSource for &'a [Gc<ast::Item>] {
98 fn to_source(&self) -> String {
100 .map(|i| i.to_source())
101 .collect::<Vec<String>>()
107 impl ToSource for ast::Ty {
108 fn to_source(&self) -> String {
109 pprust::ty_to_str(self)
113 impl<'a> ToSource for &'a [ast::Ty] {
114 fn to_source(&self) -> String {
116 .map(|i| i.to_source())
117 .collect::<Vec<String>>()
123 impl ToSource for Generics {
124 fn to_source(&self) -> String {
125 pprust::generics_to_str(self)
129 impl ToSource for Gc<ast::Expr> {
130 fn to_source(&self) -> String {
131 pprust::expr_to_str(&**self)
135 impl ToSource for ast::Block {
136 fn to_source(&self) -> String {
137 pprust::block_to_str(self)
141 impl ToSource for ast::Arg {
142 fn to_source(&self) -> String {
143 pprust::arg_to_str(self)
147 impl<'a> ToSource for &'a str {
148 fn to_source(&self) -> String {
149 let lit = dummy_spanned(ast::LitStr(
150 token::intern_and_get_ident(*self), ast::CookedStr));
151 pprust::lit_to_str(&lit)
155 impl ToSource for () {
156 fn to_source(&self) -> String {
161 impl ToSource for bool {
162 fn to_source(&self) -> String {
163 let lit = dummy_spanned(ast::LitBool(*self));
164 pprust::lit_to_str(&lit)
168 impl ToSource for char {
169 fn to_source(&self) -> String {
170 let lit = dummy_spanned(ast::LitChar(*self));
171 pprust::lit_to_str(&lit)
175 impl ToSource for int {
176 fn to_source(&self) -> String {
177 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI));
178 pprust::lit_to_str(&lit)
182 impl ToSource for i8 {
183 fn to_source(&self) -> String {
184 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI8));
185 pprust::lit_to_str(&lit)
189 impl ToSource for i16 {
190 fn to_source(&self) -> String {
191 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI16));
192 pprust::lit_to_str(&lit)
197 impl ToSource for i32 {
198 fn to_source(&self) -> String {
199 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI32));
200 pprust::lit_to_str(&lit)
204 impl ToSource for i64 {
205 fn to_source(&self) -> String {
206 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI64));
207 pprust::lit_to_str(&lit)
211 impl ToSource for uint {
212 fn to_source(&self) -> String {
213 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU));
214 pprust::lit_to_str(&lit)
218 impl ToSource for u8 {
219 fn to_source(&self) -> String {
220 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU8));
221 pprust::lit_to_str(&lit)
225 impl ToSource for u16 {
226 fn to_source(&self) -> String {
227 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU16));
228 pprust::lit_to_str(&lit)
232 impl ToSource for u32 {
233 fn to_source(&self) -> String {
234 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU32));
235 pprust::lit_to_str(&lit)
239 impl ToSource for u64 {
240 fn to_source(&self) -> String {
241 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU64));
242 pprust::lit_to_str(&lit)
246 // Alas ... we write these out instead. All redundant.
248 macro_rules! impl_to_tokens(
250 impl ToTokens for $t {
251 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
252 cx.parse_tts(self.to_source())
258 macro_rules! impl_to_tokens_self(
260 impl<'a> ToTokens for $t {
261 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
262 cx.parse_tts(self.to_source())
268 impl_to_tokens!(ast::Ident)
269 impl_to_tokens!(Gc<ast::Item>)
270 impl_to_tokens_self!(&'a [Gc<ast::Item>])
271 impl_to_tokens!(ast::Ty)
272 impl_to_tokens_self!(&'a [ast::Ty])
273 impl_to_tokens!(Generics)
274 impl_to_tokens!(Gc<ast::Expr>)
275 impl_to_tokens!(ast::Block)
276 impl_to_tokens!(ast::Arg)
277 impl_to_tokens_self!(&'a str)
279 impl_to_tokens!(char)
280 impl_to_tokens!(bool)
286 impl_to_tokens!(uint)
292 pub trait ExtParseUtils {
293 fn parse_item(&self, s: String) -> Gc<ast::Item>;
294 fn parse_expr(&self, s: String) -> Gc<ast::Expr>;
295 fn parse_stmt(&self, s: String) -> Gc<ast::Stmt>;
296 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> ;
299 impl<'a> ExtParseUtils for ExtCtxt<'a> {
301 fn parse_item(&self, s: String) -> Gc<ast::Item> {
302 let res = parse::parse_item_from_source_str(
303 "<quote expansion>".to_string(),
310 error!("parse error");
316 fn parse_stmt(&self, s: String) -> Gc<ast::Stmt> {
317 parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
324 fn parse_expr(&self, s: String) -> Gc<ast::Expr> {
325 parse::parse_expr_from_source_str("<quote expansion>".to_string(),
331 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
332 parse::parse_tts_from_source_str("<quote expansion>".to_string(),
341 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
343 tts: &[ast::TokenTree])
344 -> Box<base::MacResult> {
345 let (cx_expr, expr) = expand_tts(cx, sp, tts);
346 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
347 base::MacExpr::new(expanded)
350 pub fn expand_quote_expr(cx: &mut ExtCtxt,
352 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
353 let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
354 base::MacExpr::new(expanded)
357 pub fn expand_quote_item(cx: &mut ExtCtxt,
359 tts: &[ast::TokenTree])
360 -> Box<base::MacResult> {
361 let expanded = expand_parse_call(cx, sp, "parse_item_with_outer_attributes",
363 base::MacExpr::new(expanded)
366 pub fn expand_quote_pat(cx: &mut ExtCtxt,
368 tts: &[ast::TokenTree])
369 -> Box<base::MacResult> {
370 let expanded = expand_parse_call(cx, sp, "parse_pat", vec!(), tts);
371 base::MacExpr::new(expanded)
374 pub fn expand_quote_ty(cx: &mut ExtCtxt,
376 tts: &[ast::TokenTree])
377 -> Box<base::MacResult> {
378 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
379 let expanded = expand_parse_call(cx, sp, "parse_ty",
380 vec!(e_param_colons), tts);
381 base::MacExpr::new(expanded)
384 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
386 tts: &[ast::TokenTree])
387 -> Box<base::MacResult> {
388 let e_attrs = cx.expr_vec_ng(sp);
389 let expanded = expand_parse_call(cx, sp, "parse_stmt",
391 base::MacExpr::new(expanded)
394 fn ids_ext(strs: Vec<String> ) -> Vec<ast::Ident> {
395 strs.iter().map(|str| str_to_ident((*str).as_slice())).collect()
398 fn id_ext(str: &str) -> ast::Ident {
402 // Lift an ident to the expr that evaluates to that ident.
403 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> Gc<ast::Expr> {
404 let e_str = cx.expr_str(sp, token::get_ident(ident));
405 cx.expr_method_call(sp,
406 cx.expr_ident(sp, id_ext("ext_cx")),
411 fn mk_ast_path(cx: &ExtCtxt, sp: Span, name: &str) -> Gc<ast::Expr> {
412 let idents = vec!(id_ext("syntax"), id_ext("ast"), id_ext(name));
413 cx.expr_path(cx.path_global(sp, idents))
416 fn mk_token_path(cx: &ExtCtxt, sp: Span, name: &str) -> Gc<ast::Expr> {
417 let idents = vec!(id_ext("syntax"), id_ext("parse"), id_ext("token"), id_ext(name));
418 cx.expr_path(cx.path_global(sp, idents))
421 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> Gc<ast::Expr> {
422 let name = match bop {
427 PERCENT => "PERCENT",
434 mk_token_path(cx, sp, name)
437 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> Gc<ast::Expr> {
441 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOP"), vec!(mk_binop(cx, sp, binop)));
444 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOPEQ"),
445 vec!(mk_binop(cx, sp, binop)));
449 let e_char = cx.expr_lit(sp, ast::LitChar(i));
451 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_CHAR"), vec!(e_char));
455 let s_ity = match ity {
458 ast::TyI16 => "TyI16",
459 ast::TyI32 => "TyI32",
460 ast::TyI64 => "TyI64"
462 let e_ity = mk_ast_path(cx, sp, s_ity);
463 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
464 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT"), vec!(e_i64, e_ity));
467 LIT_UINT(u, uty) => {
468 let s_uty = match uty {
471 ast::TyU16 => "TyU16",
472 ast::TyU32 => "TyU32",
473 ast::TyU64 => "TyU64"
475 let e_uty = mk_ast_path(cx, sp, s_uty);
476 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
477 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_UINT"), vec!(e_u64, e_uty));
480 LIT_INT_UNSUFFIXED(i) => {
481 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
482 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT_UNSUFFIXED"), vec!(e_i64));
485 LIT_FLOAT(fident, fty) => {
486 let s_fty = match fty {
487 ast::TyF32 => "TyF32",
488 ast::TyF64 => "TyF64",
489 ast::TyF128 => "TyF128"
491 let e_fty = mk_ast_path(cx, sp, s_fty);
492 let e_fident = mk_ident(cx, sp, fident);
493 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_FLOAT"), vec!(e_fident, e_fty));
497 return cx.expr_call(sp,
498 mk_token_path(cx, sp, "LIT_STR"),
499 vec!(mk_ident(cx, sp, ident)));
502 LIT_STR_RAW(ident, n) => {
503 return cx.expr_call(sp,
504 mk_token_path(cx, sp, "LIT_STR_RAW"),
505 vec!(mk_ident(cx, sp, ident), cx.expr_uint(sp, n)));
509 return cx.expr_call(sp,
510 mk_token_path(cx, sp, "IDENT"),
511 vec!(mk_ident(cx, sp, ident), cx.expr_bool(sp, b)));
515 return cx.expr_call(sp,
516 mk_token_path(cx, sp, "LIFETIME"),
517 vec!(mk_ident(cx, sp, ident)));
520 DOC_COMMENT(ident) => {
521 return cx.expr_call(sp,
522 mk_token_path(cx, sp, "DOC_COMMENT"),
523 vec!(mk_ident(cx, sp, ident)));
526 INTERPOLATED(_) => fail!("quote! with interpolated token"),
531 let name = match *tok {
549 MOD_SEP => "MOD_SEP",
552 FAT_ARROW => "FAT_ARROW",
555 LBRACKET => "LBRACKET",
556 RBRACKET => "RBRACKET",
561 UNDERSCORE => "UNDERSCORE",
565 mk_token_path(cx, sp, name)
569 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<Gc<ast::Stmt>> {
571 ast::TTTok(sp, ref tok) => {
572 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
573 let e_tok = cx.expr_call(sp,
574 mk_ast_path(cx, sp, "TTTok"),
575 vec!(e_sp, mk_token(cx, sp, tok)));
577 cx.expr_method_call(sp,
578 cx.expr_ident(sp, id_ext("tt")),
581 vec!(cx.stmt_expr(e_push))
584 ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
585 ast::TTSeq(..) => fail!("TTSeq in quote!"),
587 ast::TTNonterminal(sp, ident) => {
589 // tt.push_all_move($ident.to_tokens(ext_cx))
592 cx.expr_method_call(sp,
593 cx.expr_ident(sp, ident),
595 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
598 cx.expr_method_call(sp,
599 cx.expr_ident(sp, id_ext("tt")),
600 id_ext("push_all_move"),
603 vec!(cx.stmt_expr(e_push))
608 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
609 -> Vec<Gc<ast::Stmt>> {
610 let mut ss = Vec::new();
611 for tt in tts.iter() {
612 ss.push_all_move(mk_tt(cx, sp, tt));
617 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
618 -> (Gc<ast::Expr>, Gc<ast::Expr>) {
619 // NB: It appears that the main parser loses its mind if we consider
620 // $foo as a TTNonterminal during the main parse, so we have to re-parse
621 // under quote_depth > 0. This is silly and should go away; the _guess_ is
622 // it has to do with transition away from supporting old-style macros, so
623 // try removing it when enough of them are gone.
625 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
628 .map(|x| (*x).clone())
632 let cx_expr = p.parse_expr();
633 if !p.eat(&token::COMMA) {
634 p.fatal("expected token `,`");
637 let tts = p.parse_all_token_trees();
640 // We also bind a single value, sp, to ext_cx.call_site()
642 // This causes every span in a token-tree quote to be attributed to the
643 // call site of the extension using the quote. We can't really do much
644 // better since the source of the quote may well be in a library that
645 // was not even parsed by this compilation run, that the user has no
646 // source code for (eg. in libsyntax, which they're just _using_).
648 // The old quasiquoter had an elaborate mechanism for denoting input
649 // file locations from which quotes originated; unfortunately this
650 // relied on feeding the source string of the quote back into the
651 // compiler (which we don't really want to do) and, in any case, only
652 // pushed the problem a very small step further back: an error
653 // resulting from a parse of the resulting quote is still attributed to
654 // the site the string literal occurred, which was in a source file
655 // _other_ than the one the user has control over. For example, an
656 // error in a quote from the protocol compiler, invoked in user code
657 // using macro_rules! for example, will be attributed to the macro_rules.rs
658 // file in libsyntax, which the user might not even have source to (unless
659 // they happen to have a compiler on hand). Over all, the phase distinction
660 // just makes quotes "hard to attribute". Possibly this could be fixed
661 // by recreating some of the original qq machinery in the tt regime
662 // (pushing fake FileMaps onto the parser to account for original sites
663 // of quotes, for example) but at this point it seems not likely to be
666 let e_sp = cx.expr_method_call(sp,
667 cx.expr_ident(sp, id_ext("ext_cx")),
671 let stmt_let_sp = cx.stmt_let(sp, false,
675 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
677 let mut vector = vec!(stmt_let_sp, stmt_let_tt);
678 vector.push_all_move(mk_tts(cx, sp, tts.as_slice()));
679 let block = cx.expr_block(
683 Some(cx.expr_ident(sp, id_ext("tt")))));
688 fn expand_wrapper(cx: &ExtCtxt,
690 cx_expr: Gc<ast::Expr>,
691 expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
693 &["syntax", "ext", "quote", "rt"],
694 ].iter().map(|path| {
695 let path = path.iter().map(|s| s.to_string()).collect();
696 cx.view_use_glob(sp, ast::Inherited, ids_ext(path))
699 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
701 cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
704 fn expand_parse_call(cx: &ExtCtxt,
707 arg_exprs: Vec<Gc<ast::Expr>>,
708 tts: &[ast::TokenTree]) -> Gc<ast::Expr> {
709 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
711 let cfg_call = || cx.expr_method_call(
712 sp, cx.expr_ident(sp, id_ext("ext_cx")),
713 id_ext("cfg"), Vec::new());
715 let parse_sess_call = || cx.expr_method_call(
716 sp, cx.expr_ident(sp, id_ext("ext_cx")),
717 id_ext("parse_sess"), Vec::new());
719 let new_parser_call =
721 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
722 vec!(parse_sess_call(), cfg_call(), tts_expr));
724 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
727 expand_wrapper(cx, sp, cx_expr, expr)