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;
22 * Quasiquoting works via token trees.
24 * This is registered as a set of expression syntax extension called quote!
25 * that lifts its argument token-tree to an AST representing the
26 * construction of the same token tree, with ast::TTNonterminal nodes
27 * interpreted as antiquotes (splices).
33 use ext::base::ExtCtxt;
39 pub use parse::token::*;
40 pub use parse::new_parser_from_tts;
41 pub use codemap::{BytePos, Span, dummy_spanned};
44 fn to_tokens(&self, _cx: &ExtCtxt) -> ~[TokenTree];
47 impl ToTokens for ~[TokenTree] {
48 fn to_tokens(&self, _cx: &ExtCtxt) -> ~[TokenTree] {
53 /* Should be (when bugs in default methods are fixed):
55 trait ToSource : ToTokens {
56 // Takes a thing and generates a string containing rust code for it.
57 pub fn to_source() -> ~str;
59 // If you can make source, you can definitely make tokens.
60 pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
61 cx.parse_tts(self.to_source())
68 // Takes a thing and generates a string containing rust code for it.
69 fn to_source(&self) -> ~str;
72 impl ToSource for ast::Ident {
73 fn to_source(&self) -> ~str {
74 let this = get_ident(self.name);
79 impl ToSource for @ast::Item {
80 fn to_source(&self) -> ~str {
81 pprust::item_to_str(*self, get_ident_interner())
85 impl<'a> ToSource for &'a [@ast::Item] {
86 fn to_source(&self) -> ~str {
87 self.map(|i| i.to_source()).connect("\n\n")
91 impl ToSource for ast::Ty {
92 fn to_source(&self) -> ~str {
93 pprust::ty_to_str(self, get_ident_interner())
97 impl<'a> ToSource for &'a [ast::Ty] {
98 fn to_source(&self) -> ~str {
99 self.map(|i| i.to_source()).connect(", ")
103 impl ToSource for Generics {
104 fn to_source(&self) -> ~str {
105 pprust::generics_to_str(self, get_ident_interner())
109 impl ToSource for @ast::Expr {
110 fn to_source(&self) -> ~str {
111 pprust::expr_to_str(*self, get_ident_interner())
115 impl ToSource for ast::Block {
116 fn to_source(&self) -> ~str {
117 pprust::block_to_str(self, get_ident_interner())
121 impl<'a> ToSource for &'a str {
122 fn to_source(&self) -> ~str {
123 let lit = dummy_spanned(ast::LitStr(
124 token::intern_and_get_ident(*self), ast::CookedStr));
125 pprust::lit_to_str(&lit)
129 impl ToSource for int {
130 fn to_source(&self) -> ~str {
131 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI));
132 pprust::lit_to_str(&lit)
136 impl ToSource for i8 {
137 fn to_source(&self) -> ~str {
138 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI8));
139 pprust::lit_to_str(&lit)
143 impl ToSource for i16 {
144 fn to_source(&self) -> ~str {
145 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI16));
146 pprust::lit_to_str(&lit)
151 impl ToSource for i32 {
152 fn to_source(&self) -> ~str {
153 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI32));
154 pprust::lit_to_str(&lit)
158 impl ToSource for i64 {
159 fn to_source(&self) -> ~str {
160 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI64));
161 pprust::lit_to_str(&lit)
165 impl ToSource for uint {
166 fn to_source(&self) -> ~str {
167 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU));
168 pprust::lit_to_str(&lit)
172 impl ToSource for u8 {
173 fn to_source(&self) -> ~str {
174 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU8));
175 pprust::lit_to_str(&lit)
179 impl ToSource for u16 {
180 fn to_source(&self) -> ~str {
181 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU16));
182 pprust::lit_to_str(&lit)
186 impl ToSource for u32 {
187 fn to_source(&self) -> ~str {
188 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU32));
189 pprust::lit_to_str(&lit)
193 impl ToSource for u64 {
194 fn to_source(&self) -> ~str {
195 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU64));
196 pprust::lit_to_str(&lit)
200 // Alas ... we write these out instead. All redundant.
202 macro_rules! impl_to_tokens(
204 impl ToTokens for $t {
205 fn to_tokens(&self, cx: &ExtCtxt) -> ~[TokenTree] {
206 cx.parse_tts(self.to_source())
212 macro_rules! impl_to_tokens_self(
214 impl<'a> ToTokens for $t {
215 fn to_tokens(&self, cx: &ExtCtxt) -> ~[TokenTree] {
216 cx.parse_tts(self.to_source())
222 impl_to_tokens!(ast::Ident)
223 impl_to_tokens!(@ast::Item)
224 impl_to_tokens_self!(&'a [@ast::Item])
225 impl_to_tokens!(ast::Ty)
226 impl_to_tokens_self!(&'a [ast::Ty])
227 impl_to_tokens!(Generics)
228 impl_to_tokens!(@ast::Expr)
229 impl_to_tokens!(ast::Block)
230 impl_to_tokens_self!(&'a str)
236 impl_to_tokens!(uint)
242 pub trait ExtParseUtils {
243 fn parse_item(&self, s: ~str) -> @ast::Item;
244 fn parse_expr(&self, s: ~str) -> @ast::Expr;
245 fn parse_stmt(&self, s: ~str) -> @ast::Stmt;
246 fn parse_tts(&self, s: ~str) -> ~[ast::TokenTree];
249 impl<'a> ExtParseUtils for ExtCtxt<'a> {
251 fn parse_item(&self, s: ~str) -> @ast::Item {
252 let res = parse::parse_item_from_source_str(
253 "<quote expansion>".to_str(),
260 error!("Parse error");
266 fn parse_stmt(&self, s: ~str) -> @ast::Stmt {
267 parse::parse_stmt_from_source_str("<quote expansion>".to_str(),
274 fn parse_expr(&self, s: ~str) -> @ast::Expr {
275 parse::parse_expr_from_source_str("<quote expansion>".to_str(),
281 fn parse_tts(&self, s: ~str) -> ~[ast::TokenTree] {
282 parse::parse_tts_from_source_str("<quote expansion>".to_str(),
291 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
293 tts: &[ast::TokenTree]) -> base::MacResult {
294 let (cx_expr, expr) = expand_tts(cx, sp, tts);
295 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
296 base::MRExpr(expanded)
299 pub fn expand_quote_expr(cx: &mut ExtCtxt,
301 tts: &[ast::TokenTree]) -> base::MacResult {
302 let expanded = expand_parse_call(cx, sp, "parse_expr", ~[], tts);
303 base::MRExpr(expanded)
306 pub fn expand_quote_item(cx: &mut ExtCtxt,
308 tts: &[ast::TokenTree]) -> base::MacResult {
309 let e_attrs = cx.expr_vec_uniq(sp, ~[]);
310 let expanded = expand_parse_call(cx, sp, "parse_item",
312 base::MRExpr(expanded)
315 pub fn expand_quote_pat(cx: &mut ExtCtxt,
317 tts: &[ast::TokenTree]) -> base::MacResult {
318 let e_refutable = cx.expr_lit(sp, ast::LitBool(true));
319 let expanded = expand_parse_call(cx, sp, "parse_pat",
320 ~[e_refutable], tts);
321 base::MRExpr(expanded)
324 pub fn expand_quote_ty(cx: &mut ExtCtxt,
326 tts: &[ast::TokenTree]) -> base::MacResult {
327 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
328 let expanded = expand_parse_call(cx, sp, "parse_ty",
329 ~[e_param_colons], tts);
330 base::MRExpr(expanded)
333 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
335 tts: &[ast::TokenTree]) -> base::MacResult {
336 let e_attrs = cx.expr_vec_uniq(sp, ~[]);
337 let expanded = expand_parse_call(cx, sp, "parse_stmt",
339 base::MRExpr(expanded)
342 fn ids_ext(strs: ~[~str]) -> ~[ast::Ident] {
343 strs.map(|str| str_to_ident(*str))
346 fn id_ext(str: &str) -> ast::Ident {
350 // Lift an ident to the expr that evaluates to that ident.
351 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> @ast::Expr {
352 let e_str = cx.expr_str(sp, token::get_ident(ident.name));
353 cx.expr_method_call(sp,
354 cx.expr_ident(sp, id_ext("ext_cx")),
359 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
360 let name = match bop {
365 PERCENT => "PERCENT",
372 cx.expr_ident(sp, id_ext(name))
375 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
379 return cx.expr_call_ident(sp,
381 ~[mk_binop(cx, sp, binop)]);
384 return cx.expr_call_ident(sp,
386 ~[mk_binop(cx, sp, binop)]);
390 let e_char = cx.expr_lit(sp, ast::LitChar(i));
392 return cx.expr_call_ident(sp, id_ext("LIT_CHAR"), ~[e_char]);
396 let s_ity = match ity {
398 ast::TyI8 => ~"TyI8",
399 ast::TyI16 => ~"TyI16",
400 ast::TyI32 => ~"TyI32",
401 ast::TyI64 => ~"TyI64"
403 let e_ity = cx.expr_ident(sp, id_ext(s_ity));
405 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
407 return cx.expr_call_ident(sp,
412 LIT_UINT(u, uty) => {
413 let s_uty = match uty {
415 ast::TyU8 => ~"TyU8",
416 ast::TyU16 => ~"TyU16",
417 ast::TyU32 => ~"TyU32",
418 ast::TyU64 => ~"TyU64"
420 let e_uty = cx.expr_ident(sp, id_ext(s_uty));
422 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
424 return cx.expr_call_ident(sp,
429 LIT_INT_UNSUFFIXED(i) => {
430 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
432 return cx.expr_call_ident(sp,
433 id_ext("LIT_INT_UNSUFFIXED"),
437 LIT_FLOAT(fident, fty) => {
438 let s_fty = match fty {
439 ast::TyF32 => ~"TyF32",
440 ast::TyF64 => ~"TyF64"
442 let e_fty = cx.expr_ident(sp, id_ext(s_fty));
444 let e_fident = mk_ident(cx, sp, fident);
446 return cx.expr_call_ident(sp,
452 return cx.expr_call_ident(sp,
454 ~[mk_ident(cx, sp, ident)]);
457 LIT_STR_RAW(ident, n) => {
458 return cx.expr_call_ident(sp,
459 id_ext("LIT_STR_RAW"),
460 ~[mk_ident(cx, sp, ident),
461 cx.expr_uint(sp, n)]);
465 return cx.expr_call_ident(sp,
467 ~[mk_ident(cx, sp, ident),
468 cx.expr_bool(sp, b)]);
472 return cx.expr_call_ident(sp,
474 ~[mk_ident(cx, sp, ident)]);
477 DOC_COMMENT(ident) => {
478 return cx.expr_call_ident(sp,
479 id_ext("DOC_COMMENT"),
480 ~[mk_ident(cx, sp, ident)]);
483 INTERPOLATED(_) => fail!("quote! with interpolated token"),
488 let name = match *tok {
506 MOD_SEP => "MOD_SEP",
510 FAT_ARROW => "FAT_ARROW",
513 LBRACKET => "LBRACKET",
514 RBRACKET => "RBRACKET",
519 UNDERSCORE => "UNDERSCORE",
523 cx.expr_ident(sp, id_ext(name))
527 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> ~[@ast::Stmt] {
531 ast::TTTok(sp, ref tok) => {
532 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
533 let e_tok = cx.expr_call_ident(sp,
535 ~[e_sp, mk_token(cx, sp, tok)]);
537 cx.expr_method_call(sp,
538 cx.expr_ident(sp, id_ext("tt")),
541 ~[cx.stmt_expr(e_push)]
544 ast::TTDelim(ref tts) => mk_tts(cx, sp, **tts),
545 ast::TTSeq(..) => fail!("TTSeq in quote!"),
547 ast::TTNonterminal(sp, ident) => {
549 // tt.push_all_move($ident.to_tokens(ext_cx))
552 cx.expr_method_call(sp,
553 cx.expr_ident(sp, ident),
555 ~[cx.expr_ident(sp, id_ext("ext_cx"))]);
558 cx.expr_method_call(sp,
559 cx.expr_ident(sp, id_ext("tt")),
560 id_ext("push_all_move"),
563 ~[cx.stmt_expr(e_push)]
568 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
571 for tt in tts.iter() {
572 ss.push_all_move(mk_tt(cx, sp, tt));
577 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
578 -> (@ast::Expr, @ast::Expr) {
579 // NB: It appears that the main parser loses its mind if we consider
580 // $foo as a TTNonterminal during the main parse, so we have to re-parse
581 // under quote_depth > 0. This is silly and should go away; the _guess_ is
582 // it has to do with transition away from supporting old-style macros, so
583 // try removing it when enough of them are gone.
585 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
590 let cx_expr = p.parse_expr();
591 if !p.eat(&token::COMMA) {
592 p.fatal("Expected token `,`");
595 let tts = p.parse_all_token_trees();
598 // We also bind a single value, sp, to ext_cx.call_site()
600 // This causes every span in a token-tree quote to be attributed to the
601 // call site of the extension using the quote. We can't really do much
602 // better since the source of the quote may well be in a library that
603 // was not even parsed by this compilation run, that the user has no
604 // source code for (eg. in libsyntax, which they're just _using_).
606 // The old quasiquoter had an elaborate mechanism for denoting input
607 // file locations from which quotes originated; unfortunately this
608 // relied on feeding the source string of the quote back into the
609 // compiler (which we don't really want to do) and, in any case, only
610 // pushed the problem a very small step further back: an error
611 // resulting from a parse of the resulting quote is still attributed to
612 // the site the string literal occurred, which was in a source file
613 // _other_ than the one the user has control over. For example, an
614 // error in a quote from the protocol compiler, invoked in user code
615 // using macro_rules! for example, will be attributed to the macro_rules.rs
616 // file in libsyntax, which the user might not even have source to (unless
617 // they happen to have a compiler on hand). Over all, the phase distinction
618 // just makes quotes "hard to attribute". Possibly this could be fixed
619 // by recreating some of the original qq machinery in the tt regime
620 // (pushing fake FileMaps onto the parser to account for original sites
621 // of quotes, for example) but at this point it seems not likely to be
624 let e_sp = cx.expr_method_call(sp,
625 cx.expr_ident(sp, id_ext("ext_cx")),
629 let stmt_let_sp = cx.stmt_let(sp, false,
633 let stmt_let_tt = cx.stmt_let(sp, true,
635 cx.expr_vec_uniq(sp, ~[]));
637 let block = cx.expr_block(
640 ~[stmt_let_sp, stmt_let_tt] + mk_tts(cx, sp, tts),
641 Some(cx.expr_ident(sp, id_ext("tt")))));
646 fn expand_wrapper(cx: &ExtCtxt,
649 expr: @ast::Expr) -> @ast::Expr {
650 let uses = ~[ cx.view_use_glob(sp, ast::Inherited,
656 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
658 cx.expr_block(cx.block_all(sp, uses, ~[stmt_let_ext_cx], Some(expr)))
661 fn expand_parse_call(cx: &ExtCtxt,
664 arg_exprs: ~[@ast::Expr],
665 tts: &[ast::TokenTree]) -> @ast::Expr {
666 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
668 let cfg_call = || cx.expr_method_call(
669 sp, cx.expr_ident(sp, id_ext("ext_cx")),
672 let parse_sess_call = || cx.expr_method_call(
673 sp, cx.expr_ident(sp, id_ext("ext_cx")),
674 id_ext("parse_sess"), ~[]);
676 let new_parser_call =
678 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
679 ~[parse_sess_call(), cfg_call(), tts_expr]);
681 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
684 expand_wrapper(cx, sp, cx_expr, expr)