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;
43 pub use parse::token::*;
44 pub use parse::new_parser_from_tts;
45 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() -> ~str;
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) -> ~str;
76 impl ToSource for ast::Ident {
77 fn to_source(&self) -> ~str {
78 get_ident(*self).get().to_str()
82 impl ToSource for @ast::Item {
83 fn to_source(&self) -> ~str {
84 pprust::item_to_str(*self)
88 impl<'a> ToSource for &'a [@ast::Item] {
89 fn to_source(&self) -> ~str {
90 self.map(|i| i.to_source()).connect("\n\n")
94 impl ToSource for ast::Ty {
95 fn to_source(&self) -> ~str {
96 pprust::ty_to_str(self)
100 impl<'a> ToSource for &'a [ast::Ty] {
101 fn to_source(&self) -> ~str {
102 self.map(|i| i.to_source()).connect(", ")
106 impl ToSource for Generics {
107 fn to_source(&self) -> ~str {
108 pprust::generics_to_str(self)
112 impl ToSource for @ast::Expr {
113 fn to_source(&self) -> ~str {
114 pprust::expr_to_str(*self)
118 impl ToSource for ast::Block {
119 fn to_source(&self) -> ~str {
120 pprust::block_to_str(self)
124 impl<'a> ToSource for &'a str {
125 fn to_source(&self) -> ~str {
126 let lit = dummy_spanned(ast::LitStr(
127 token::intern_and_get_ident(*self), ast::CookedStr));
128 pprust::lit_to_str(&lit)
132 impl ToSource for int {
133 fn to_source(&self) -> ~str {
134 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI));
135 pprust::lit_to_str(&lit)
139 impl ToSource for i8 {
140 fn to_source(&self) -> ~str {
141 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI8));
142 pprust::lit_to_str(&lit)
146 impl ToSource for i16 {
147 fn to_source(&self) -> ~str {
148 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI16));
149 pprust::lit_to_str(&lit)
154 impl ToSource for i32 {
155 fn to_source(&self) -> ~str {
156 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI32));
157 pprust::lit_to_str(&lit)
161 impl ToSource for i64 {
162 fn to_source(&self) -> ~str {
163 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI64));
164 pprust::lit_to_str(&lit)
168 impl ToSource for uint {
169 fn to_source(&self) -> ~str {
170 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU));
171 pprust::lit_to_str(&lit)
175 impl ToSource for u8 {
176 fn to_source(&self) -> ~str {
177 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU8));
178 pprust::lit_to_str(&lit)
182 impl ToSource for u16 {
183 fn to_source(&self) -> ~str {
184 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU16));
185 pprust::lit_to_str(&lit)
189 impl ToSource for u32 {
190 fn to_source(&self) -> ~str {
191 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU32));
192 pprust::lit_to_str(&lit)
196 impl ToSource for u64 {
197 fn to_source(&self) -> ~str {
198 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU64));
199 pprust::lit_to_str(&lit)
203 // Alas ... we write these out instead. All redundant.
205 macro_rules! impl_to_tokens(
207 impl ToTokens for $t {
208 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
209 cx.parse_tts(self.to_source())
215 macro_rules! impl_to_tokens_self(
217 impl<'a> ToTokens for $t {
218 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
219 cx.parse_tts(self.to_source())
225 impl_to_tokens!(ast::Ident)
226 impl_to_tokens!(@ast::Item)
227 impl_to_tokens_self!(&'a [@ast::Item])
228 impl_to_tokens!(ast::Ty)
229 impl_to_tokens_self!(&'a [ast::Ty])
230 impl_to_tokens!(Generics)
231 impl_to_tokens!(@ast::Expr)
232 impl_to_tokens!(ast::Block)
233 impl_to_tokens_self!(&'a str)
239 impl_to_tokens!(uint)
245 pub trait ExtParseUtils {
246 fn parse_item(&self, s: ~str) -> @ast::Item;
247 fn parse_expr(&self, s: ~str) -> @ast::Expr;
248 fn parse_stmt(&self, s: ~str) -> @ast::Stmt;
249 fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> ;
252 impl<'a> ExtParseUtils for ExtCtxt<'a> {
254 fn parse_item(&self, s: ~str) -> @ast::Item {
255 let res = parse::parse_item_from_source_str(
256 "<quote expansion>".to_str(),
263 error!("parse error");
269 fn parse_stmt(&self, s: ~str) -> @ast::Stmt {
270 parse::parse_stmt_from_source_str("<quote expansion>".to_str(),
277 fn parse_expr(&self, s: ~str) -> @ast::Expr {
278 parse::parse_expr_from_source_str("<quote expansion>".to_str(),
284 fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> {
285 parse::parse_tts_from_source_str("<quote expansion>".to_str(),
294 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
296 tts: &[ast::TokenTree]) -> base::MacResult {
297 let (cx_expr, expr) = expand_tts(cx, sp, tts);
298 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
299 base::MRExpr(expanded)
302 pub fn expand_quote_expr(cx: &mut ExtCtxt,
304 tts: &[ast::TokenTree]) -> base::MacResult {
305 let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
306 base::MRExpr(expanded)
309 pub fn expand_quote_item(cx: &mut ExtCtxt,
311 tts: &[ast::TokenTree]) -> base::MacResult {
312 let e_attrs = cx.expr_vec_ng(sp);
313 let expanded = expand_parse_call(cx, sp, "parse_item",
315 base::MRExpr(expanded)
318 pub fn expand_quote_pat(cx: &mut ExtCtxt,
320 tts: &[ast::TokenTree]) -> base::MacResult {
321 let e_refutable = cx.expr_lit(sp, ast::LitBool(true));
322 let expanded = expand_parse_call(cx, sp, "parse_pat",
323 vec!(e_refutable), tts);
324 base::MRExpr(expanded)
327 pub fn expand_quote_ty(cx: &mut ExtCtxt,
329 tts: &[ast::TokenTree]) -> base::MacResult {
330 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
331 let expanded = expand_parse_call(cx, sp, "parse_ty",
332 vec!(e_param_colons), tts);
333 base::MRExpr(expanded)
336 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
338 tts: &[ast::TokenTree]) -> base::MacResult {
339 let e_attrs = cx.expr_vec_ng(sp);
340 let expanded = expand_parse_call(cx, sp, "parse_stmt",
342 base::MRExpr(expanded)
345 fn ids_ext(strs: Vec<~str> ) -> Vec<ast::Ident> {
346 strs.map(|str| str_to_ident(*str))
349 fn id_ext(str: &str) -> ast::Ident {
353 // Lift an ident to the expr that evaluates to that ident.
354 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> @ast::Expr {
355 let e_str = cx.expr_str(sp, token::get_ident(ident));
356 cx.expr_method_call(sp,
357 cx.expr_ident(sp, id_ext("ext_cx")),
362 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
363 let name = match bop {
368 PERCENT => "PERCENT",
375 cx.expr_ident(sp, id_ext(name))
378 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
382 return cx.expr_call_ident(sp,
384 vec!(mk_binop(cx, sp, binop)));
387 return cx.expr_call_ident(sp,
389 vec!(mk_binop(cx, sp, binop)));
393 let e_char = cx.expr_lit(sp, ast::LitChar(i));
395 return cx.expr_call_ident(sp, id_ext("LIT_CHAR"), vec!(e_char));
399 let s_ity = match ity {
401 ast::TyI8 => ~"TyI8",
402 ast::TyI16 => ~"TyI16",
403 ast::TyI32 => ~"TyI32",
404 ast::TyI64 => ~"TyI64"
406 let e_ity = cx.expr_ident(sp, id_ext(s_ity));
408 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
410 return cx.expr_call_ident(sp,
415 LIT_UINT(u, uty) => {
416 let s_uty = match uty {
418 ast::TyU8 => ~"TyU8",
419 ast::TyU16 => ~"TyU16",
420 ast::TyU32 => ~"TyU32",
421 ast::TyU64 => ~"TyU64"
423 let e_uty = cx.expr_ident(sp, id_ext(s_uty));
425 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
427 return cx.expr_call_ident(sp,
432 LIT_INT_UNSUFFIXED(i) => {
433 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
435 return cx.expr_call_ident(sp,
436 id_ext("LIT_INT_UNSUFFIXED"),
440 LIT_FLOAT(fident, fty) => {
441 let s_fty = match fty {
442 ast::TyF32 => ~"TyF32",
443 ast::TyF64 => ~"TyF64"
445 let e_fty = cx.expr_ident(sp, id_ext(s_fty));
447 let e_fident = mk_ident(cx, sp, fident);
449 return cx.expr_call_ident(sp,
451 vec!(e_fident, e_fty));
455 return cx.expr_call_ident(sp,
457 vec!(mk_ident(cx, sp, ident)));
460 LIT_STR_RAW(ident, n) => {
461 return cx.expr_call_ident(sp,
462 id_ext("LIT_STR_RAW"),
463 vec!(mk_ident(cx, sp, ident),
464 cx.expr_uint(sp, n)));
468 return cx.expr_call_ident(sp,
470 vec!(mk_ident(cx, sp, ident),
471 cx.expr_bool(sp, b)));
475 return cx.expr_call_ident(sp,
477 vec!(mk_ident(cx, sp, ident)));
480 DOC_COMMENT(ident) => {
481 return cx.expr_call_ident(sp,
482 id_ext("DOC_COMMENT"),
483 vec!(mk_ident(cx, sp, ident)));
486 INTERPOLATED(_) => fail!("quote! with interpolated token"),
491 let name = match *tok {
509 MOD_SEP => "MOD_SEP",
513 FAT_ARROW => "FAT_ARROW",
516 LBRACKET => "LBRACKET",
517 RBRACKET => "RBRACKET",
522 UNDERSCORE => "UNDERSCORE",
526 cx.expr_ident(sp, id_ext(name))
530 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> {
534 ast::TTTok(sp, ref tok) => {
535 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
536 let e_tok = cx.expr_call_ident(sp,
538 vec!(e_sp, mk_token(cx, sp, tok)));
540 cx.expr_method_call(sp,
541 cx.expr_ident(sp, id_ext("tt")),
544 vec!(cx.stmt_expr(e_push))
547 ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
548 ast::TTSeq(..) => fail!("TTSeq in quote!"),
550 ast::TTNonterminal(sp, ident) => {
552 // tt.push_all_move($ident.to_tokens(ext_cx))
555 cx.expr_method_call(sp,
556 cx.expr_ident(sp, ident),
558 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
561 cx.expr_method_call(sp,
562 cx.expr_ident(sp, id_ext("tt")),
563 id_ext("push_all_move"),
566 vec!(cx.stmt_expr(e_push))
571 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
573 let mut ss = Vec::new();
574 for tt in tts.iter() {
575 ss.push_all_move(mk_tt(cx, sp, tt));
580 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
581 -> (@ast::Expr, @ast::Expr) {
582 // NB: It appears that the main parser loses its mind if we consider
583 // $foo as a TTNonterminal during the main parse, so we have to re-parse
584 // under quote_depth > 0. This is silly and should go away; the _guess_ is
585 // it has to do with transition away from supporting old-style macros, so
586 // try removing it when enough of them are gone.
588 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
591 .map(|x| (*x).clone())
595 let cx_expr = p.parse_expr();
596 if !p.eat(&token::COMMA) {
597 p.fatal("expected token `,`");
600 let tts = p.parse_all_token_trees();
603 // We also bind a single value, sp, to ext_cx.call_site()
605 // This causes every span in a token-tree quote to be attributed to the
606 // call site of the extension using the quote. We can't really do much
607 // better since the source of the quote may well be in a library that
608 // was not even parsed by this compilation run, that the user has no
609 // source code for (eg. in libsyntax, which they're just _using_).
611 // The old quasiquoter had an elaborate mechanism for denoting input
612 // file locations from which quotes originated; unfortunately this
613 // relied on feeding the source string of the quote back into the
614 // compiler (which we don't really want to do) and, in any case, only
615 // pushed the problem a very small step further back: an error
616 // resulting from a parse of the resulting quote is still attributed to
617 // the site the string literal occurred, which was in a source file
618 // _other_ than the one the user has control over. For example, an
619 // error in a quote from the protocol compiler, invoked in user code
620 // using macro_rules! for example, will be attributed to the macro_rules.rs
621 // file in libsyntax, which the user might not even have source to (unless
622 // they happen to have a compiler on hand). Over all, the phase distinction
623 // just makes quotes "hard to attribute". Possibly this could be fixed
624 // by recreating some of the original qq machinery in the tt regime
625 // (pushing fake FileMaps onto the parser to account for original sites
626 // of quotes, for example) but at this point it seems not likely to be
629 let e_sp = cx.expr_method_call(sp,
630 cx.expr_ident(sp, id_ext("ext_cx")),
634 let stmt_let_sp = cx.stmt_let(sp, false,
638 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
640 let mut vector = vec!(stmt_let_sp, stmt_let_tt);
641 vector.push_all_move(mk_tts(cx, sp, tts.as_slice()));
642 let block = cx.expr_block(
646 Some(cx.expr_ident(sp, id_ext("tt")))));
651 fn expand_wrapper(cx: &ExtCtxt,
654 expr: @ast::Expr) -> @ast::Expr {
655 let uses = vec!( cx.view_use_glob(sp, ast::Inherited,
656 ids_ext(vec!(~"syntax",
661 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
663 cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
666 fn expand_parse_call(cx: &ExtCtxt,
669 arg_exprs: Vec<@ast::Expr> ,
670 tts: &[ast::TokenTree]) -> @ast::Expr {
671 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
673 let cfg_call = || cx.expr_method_call(
674 sp, cx.expr_ident(sp, id_ext("ext_cx")),
675 id_ext("cfg"), Vec::new());
677 let parse_sess_call = || cx.expr_method_call(
678 sp, cx.expr_ident(sp, id_ext("ext_cx")),
679 id_ext("parse_sess"), Vec::new());
681 let new_parser_call =
683 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
684 vec!(parse_sess_call(), cfg_call(), tts_expr));
686 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
689 expand_wrapper(cx, sp, cx_expr, expr)