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;
23 * Quasiquoting works via token trees.
25 * This is registered as a set of expression syntax extension called quote!
26 * that lifts its argument token-tree to an AST representing the
27 * construction of the same token tree, with ast::TTNonterminal nodes
28 * interpreted as antiquotes (splices).
34 use ext::base::ExtCtxt;
40 use ast::{TokenTree, Generics, Expr};
42 // NOTE remove this after snapshot
43 // (stage0 quasiquoter needs this)
45 pub use ast::{Generics, TokenTree, TTTok};
47 pub use parse::token::{IDENT, SEMI, LBRACE, RBRACE, LIFETIME, COLON, AND, BINOP, EQ,
48 LBRACKET, RBRACKET, LPAREN, RPAREN, POUND, NOT, MOD_SEP, DOT, COMMA};
50 pub use parse::new_parser_from_tts;
51 pub use codemap::{BytePos, Span, dummy_spanned};
54 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
57 impl ToTokens for Vec<TokenTree> {
58 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
63 /* Should be (when bugs in default methods are fixed):
65 trait ToSource : ToTokens {
66 // Takes a thing and generates a string containing rust code for it.
67 pub fn to_source() -> String;
69 // If you can make source, you can definitely make tokens.
70 pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
71 cx.parse_tts(self.to_source())
78 // Takes a thing and generates a string containing rust code for it.
79 fn to_source(&self) -> String;
82 impl ToSource for ast::Ident {
83 fn to_source(&self) -> String {
84 token::get_ident(*self).get().to_string()
88 impl ToSource for @ast::Item {
89 fn to_source(&self) -> String {
90 pprust::item_to_str(*self)
94 impl<'a> ToSource for &'a [@ast::Item] {
95 fn to_source(&self) -> String {
97 .map(|i| i.to_source())
98 .collect::<Vec<String>>()
104 impl ToSource for ast::Ty {
105 fn to_source(&self) -> String {
106 pprust::ty_to_str(self)
110 impl<'a> ToSource for &'a [ast::Ty] {
111 fn to_source(&self) -> String {
113 .map(|i| i.to_source())
114 .collect::<Vec<String>>()
120 impl ToSource for Generics {
121 fn to_source(&self) -> String {
122 pprust::generics_to_str(self)
126 impl ToSource for @ast::Expr {
127 fn to_source(&self) -> String {
128 pprust::expr_to_str(*self)
132 impl ToSource for ast::Block {
133 fn to_source(&self) -> String {
134 pprust::block_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!(@ast::Item)
261 impl_to_tokens_self!(&'a [@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!(@ast::Expr)
266 impl_to_tokens!(ast::Block)
267 impl_to_tokens_self!(&'a str)
269 impl_to_tokens!(char)
270 impl_to_tokens!(bool)
276 impl_to_tokens!(uint)
282 pub trait ExtParseUtils {
283 fn parse_item(&self, s: String) -> @ast::Item;
284 fn parse_expr(&self, s: String) -> @ast::Expr;
285 fn parse_stmt(&self, s: String) -> @ast::Stmt;
286 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> ;
289 impl<'a> ExtParseUtils for ExtCtxt<'a> {
291 fn parse_item(&self, s: String) -> @ast::Item {
292 let res = parse::parse_item_from_source_str(
293 "<quote expansion>".to_string(),
300 error!("parse error");
306 fn parse_stmt(&self, s: String) -> @ast::Stmt {
307 parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
314 fn parse_expr(&self, s: String) -> @ast::Expr {
315 parse::parse_expr_from_source_str("<quote expansion>".to_string(),
321 fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
322 parse::parse_tts_from_source_str("<quote expansion>".to_string(),
331 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
333 tts: &[ast::TokenTree])
334 -> Box<base::MacResult> {
335 let (cx_expr, expr) = expand_tts(cx, sp, tts);
336 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
337 base::MacExpr::new(expanded)
340 pub fn expand_quote_expr(cx: &mut ExtCtxt,
342 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
343 let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
344 base::MacExpr::new(expanded)
347 pub fn expand_quote_item(cx: &mut ExtCtxt,
349 tts: &[ast::TokenTree])
350 -> Box<base::MacResult> {
351 let e_attrs = cx.expr_vec_ng(sp);
352 let expanded = expand_parse_call(cx, sp, "parse_item",
354 base::MacExpr::new(expanded)
357 pub fn expand_quote_pat(cx: &mut ExtCtxt,
359 tts: &[ast::TokenTree])
360 -> Box<base::MacResult> {
361 let e_refutable = cx.expr_lit(sp, ast::LitBool(true));
362 let expanded = expand_parse_call(cx, sp, "parse_pat",
363 vec!(e_refutable), tts);
364 base::MacExpr::new(expanded)
367 pub fn expand_quote_ty(cx: &mut ExtCtxt,
369 tts: &[ast::TokenTree])
370 -> Box<base::MacResult> {
371 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
372 let expanded = expand_parse_call(cx, sp, "parse_ty",
373 vec!(e_param_colons), tts);
374 base::MacExpr::new(expanded)
377 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
379 tts: &[ast::TokenTree])
380 -> Box<base::MacResult> {
381 let e_attrs = cx.expr_vec_ng(sp);
382 let expanded = expand_parse_call(cx, sp, "parse_stmt",
384 base::MacExpr::new(expanded)
387 fn ids_ext(strs: Vec<String> ) -> Vec<ast::Ident> {
388 strs.iter().map(|str| str_to_ident((*str).as_slice())).collect()
391 fn id_ext(str: &str) -> ast::Ident {
395 // Lift an ident to the expr that evaluates to that ident.
396 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> @ast::Expr {
397 let e_str = cx.expr_str(sp, token::get_ident(ident));
398 cx.expr_method_call(sp,
399 cx.expr_ident(sp, id_ext("ext_cx")),
404 fn mk_ast_path(cx: &ExtCtxt, sp: Span, name: &str) -> @ast::Expr {
405 let idents = vec!(id_ext("syntax"), id_ext("ast"), id_ext(name));
406 cx.expr_path(cx.path_global(sp, idents))
409 fn mk_token_path(cx: &ExtCtxt, sp: Span, name: &str) -> @ast::Expr {
410 let idents = vec!(id_ext("syntax"), id_ext("parse"), id_ext("token"), id_ext(name));
411 cx.expr_path(cx.path_global(sp, idents))
414 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
415 let name = match bop {
420 PERCENT => "PERCENT",
427 mk_token_path(cx, sp, name)
430 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
434 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOP"), vec!(mk_binop(cx, sp, binop)));
437 return cx.expr_call(sp, mk_token_path(cx, sp, "BINOPEQ"),
438 vec!(mk_binop(cx, sp, binop)));
442 let e_char = cx.expr_lit(sp, ast::LitChar(i));
444 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_CHAR"), vec!(e_char));
448 let s_ity = match ity {
451 ast::TyI16 => "TyI16",
452 ast::TyI32 => "TyI32",
453 ast::TyI64 => "TyI64"
455 let e_ity = mk_ast_path(cx, sp, s_ity);
456 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
457 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT"), vec!(e_i64, e_ity));
460 LIT_UINT(u, uty) => {
461 let s_uty = match uty {
464 ast::TyU16 => "TyU16",
465 ast::TyU32 => "TyU32",
466 ast::TyU64 => "TyU64"
468 let e_uty = mk_ast_path(cx, sp, s_uty);
469 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
470 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_UINT"), vec!(e_u64, e_uty));
473 LIT_INT_UNSUFFIXED(i) => {
474 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
475 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_INT_UNSUFFIXED"), vec!(e_i64));
478 LIT_FLOAT(fident, fty) => {
479 let s_fty = match fty {
480 ast::TyF32 => "TyF32",
481 ast::TyF64 => "TyF64",
482 ast::TyF128 => "TyF128"
484 let e_fty = mk_ast_path(cx, sp, s_fty);
485 let e_fident = mk_ident(cx, sp, fident);
486 return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_FLOAT"), vec!(e_fident, e_fty));
490 return cx.expr_call(sp,
491 mk_token_path(cx, sp, "LIT_STR"),
492 vec!(mk_ident(cx, sp, ident)));
495 LIT_STR_RAW(ident, n) => {
496 return cx.expr_call(sp,
497 mk_token_path(cx, sp, "LIT_STR_RAW"),
498 vec!(mk_ident(cx, sp, ident), cx.expr_uint(sp, n)));
502 return cx.expr_call(sp,
503 mk_token_path(cx, sp, "IDENT"),
504 vec!(mk_ident(cx, sp, ident), cx.expr_bool(sp, b)));
508 return cx.expr_call(sp,
509 mk_token_path(cx, sp, "LIFETIME"),
510 vec!(mk_ident(cx, sp, ident)));
513 DOC_COMMENT(ident) => {
514 return cx.expr_call(sp,
515 mk_token_path(cx, sp, "DOC_COMMENT"),
516 vec!(mk_ident(cx, sp, ident)));
519 INTERPOLATED(_) => fail!("quote! with interpolated token"),
524 let name = match *tok {
542 MOD_SEP => "MOD_SEP",
545 FAT_ARROW => "FAT_ARROW",
548 LBRACKET => "LBRACKET",
549 RBRACKET => "RBRACKET",
554 UNDERSCORE => "UNDERSCORE",
558 mk_token_path(cx, sp, name)
561 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> {
563 ast::TTTok(sp, ref tok) => {
564 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
565 let e_tok = cx.expr_call(sp,
566 mk_ast_path(cx, sp, "TTTok"),
567 vec!(e_sp, mk_token(cx, sp, tok)));
569 cx.expr_method_call(sp,
570 cx.expr_ident(sp, id_ext("tt")),
573 vec!(cx.stmt_expr(e_push))
576 ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
577 ast::TTSeq(..) => fail!("TTSeq in quote!"),
579 ast::TTNonterminal(sp, ident) => {
581 // tt.push_all_move($ident.to_tokens(ext_cx))
584 cx.expr_method_call(sp,
585 cx.expr_ident(sp, ident),
587 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
590 cx.expr_method_call(sp,
591 cx.expr_ident(sp, id_ext("tt")),
592 id_ext("push_all_move"),
595 vec!(cx.stmt_expr(e_push))
600 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
602 let mut ss = Vec::new();
603 for tt in tts.iter() {
604 ss.push_all_move(mk_tt(cx, sp, tt));
609 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
610 -> (@ast::Expr, @ast::Expr) {
611 // NB: It appears that the main parser loses its mind if we consider
612 // $foo as a TTNonterminal during the main parse, so we have to re-parse
613 // under quote_depth > 0. This is silly and should go away; the _guess_ is
614 // it has to do with transition away from supporting old-style macros, so
615 // try removing it when enough of them are gone.
617 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
620 .map(|x| (*x).clone())
624 let cx_expr = p.parse_expr();
625 if !p.eat(&token::COMMA) {
626 p.fatal("expected token `,`");
629 let tts = p.parse_all_token_trees();
632 // We also bind a single value, sp, to ext_cx.call_site()
634 // This causes every span in a token-tree quote to be attributed to the
635 // call site of the extension using the quote. We can't really do much
636 // better since the source of the quote may well be in a library that
637 // was not even parsed by this compilation run, that the user has no
638 // source code for (eg. in libsyntax, which they're just _using_).
640 // The old quasiquoter had an elaborate mechanism for denoting input
641 // file locations from which quotes originated; unfortunately this
642 // relied on feeding the source string of the quote back into the
643 // compiler (which we don't really want to do) and, in any case, only
644 // pushed the problem a very small step further back: an error
645 // resulting from a parse of the resulting quote is still attributed to
646 // the site the string literal occurred, which was in a source file
647 // _other_ than the one the user has control over. For example, an
648 // error in a quote from the protocol compiler, invoked in user code
649 // using macro_rules! for example, will be attributed to the macro_rules.rs
650 // file in libsyntax, which the user might not even have source to (unless
651 // they happen to have a compiler on hand). Over all, the phase distinction
652 // just makes quotes "hard to attribute". Possibly this could be fixed
653 // by recreating some of the original qq machinery in the tt regime
654 // (pushing fake FileMaps onto the parser to account for original sites
655 // of quotes, for example) but at this point it seems not likely to be
658 let e_sp = cx.expr_method_call(sp,
659 cx.expr_ident(sp, id_ext("ext_cx")),
663 let stmt_let_sp = cx.stmt_let(sp, false,
667 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
669 let mut vector = vec!(stmt_let_sp, stmt_let_tt);
670 vector.push_all_move(mk_tts(cx, sp, tts.as_slice()));
671 let block = cx.expr_block(
675 Some(cx.expr_ident(sp, id_ext("tt")))));
680 fn expand_wrapper(cx: &ExtCtxt,
683 expr: @ast::Expr) -> @ast::Expr {
685 &["syntax", "ext", "quote", "rt"],
686 ].iter().map(|path| {
687 let path = path.iter().map(|s| s.to_string()).collect();
688 cx.view_use_glob(sp, ast::Inherited, ids_ext(path))
691 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
693 cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
696 fn expand_parse_call(cx: &ExtCtxt,
699 arg_exprs: Vec<@ast::Expr> ,
700 tts: &[ast::TokenTree]) -> @ast::Expr {
701 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
703 let cfg_call = || cx.expr_method_call(
704 sp, cx.expr_ident(sp, id_ext("ext_cx")),
705 id_ext("cfg"), Vec::new());
707 let parse_sess_call = || cx.expr_method_call(
708 sp, cx.expr_ident(sp, id_ext("ext_cx")),
709 id_ext("parse_sess"), Vec::new());
711 let new_parser_call =
713 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
714 vec!(parse_sess_call(), cfg_call(), tts_expr));
716 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
719 expand_wrapper(cx, sp, cx_expr, expr)