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) -> Vec<TokenTree> ;
47 impl ToTokens for Vec<TokenTree> {
48 fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<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 get_ident(*self).get().to_str()
78 impl ToSource for @ast::Item {
79 fn to_source(&self) -> ~str {
80 pprust::item_to_str(*self)
84 impl<'a> ToSource for &'a [@ast::Item] {
85 fn to_source(&self) -> ~str {
86 self.iter().map(|i| i.to_source()).collect::<Vec<~str>>().connect("\n\n")
90 impl ToSource for ast::Ty {
91 fn to_source(&self) -> ~str {
92 pprust::ty_to_str(self)
96 impl<'a> ToSource for &'a [ast::Ty] {
97 fn to_source(&self) -> ~str {
98 self.iter().map(|i| i.to_source()).collect::<Vec<~str>>().connect(", ")
102 impl ToSource for Generics {
103 fn to_source(&self) -> ~str {
104 pprust::generics_to_str(self)
108 impl ToSource for @ast::Expr {
109 fn to_source(&self) -> ~str {
110 pprust::expr_to_str(*self)
114 impl ToSource for ast::Block {
115 fn to_source(&self) -> ~str {
116 pprust::block_to_str(self)
120 impl<'a> ToSource for &'a str {
121 fn to_source(&self) -> ~str {
122 let lit = dummy_spanned(ast::LitStr(
123 token::intern_and_get_ident(*self), ast::CookedStr));
124 pprust::lit_to_str(&lit)
128 impl ToSource for int {
129 fn to_source(&self) -> ~str {
130 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI));
131 pprust::lit_to_str(&lit)
135 impl ToSource for i8 {
136 fn to_source(&self) -> ~str {
137 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI8));
138 pprust::lit_to_str(&lit)
142 impl ToSource for i16 {
143 fn to_source(&self) -> ~str {
144 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI16));
145 pprust::lit_to_str(&lit)
150 impl ToSource for i32 {
151 fn to_source(&self) -> ~str {
152 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI32));
153 pprust::lit_to_str(&lit)
157 impl ToSource for i64 {
158 fn to_source(&self) -> ~str {
159 let lit = dummy_spanned(ast::LitInt(*self as i64, ast::TyI64));
160 pprust::lit_to_str(&lit)
164 impl ToSource for uint {
165 fn to_source(&self) -> ~str {
166 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU));
167 pprust::lit_to_str(&lit)
171 impl ToSource for u8 {
172 fn to_source(&self) -> ~str {
173 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU8));
174 pprust::lit_to_str(&lit)
178 impl ToSource for u16 {
179 fn to_source(&self) -> ~str {
180 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU16));
181 pprust::lit_to_str(&lit)
185 impl ToSource for u32 {
186 fn to_source(&self) -> ~str {
187 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU32));
188 pprust::lit_to_str(&lit)
192 impl ToSource for u64 {
193 fn to_source(&self) -> ~str {
194 let lit = dummy_spanned(ast::LitUint(*self as u64, ast::TyU64));
195 pprust::lit_to_str(&lit)
199 // Alas ... we write these out instead. All redundant.
201 macro_rules! impl_to_tokens(
203 impl ToTokens for $t {
204 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
205 cx.parse_tts(self.to_source())
211 macro_rules! impl_to_tokens_self(
213 impl<'a> ToTokens for $t {
214 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
215 cx.parse_tts(self.to_source())
221 impl_to_tokens!(ast::Ident)
222 impl_to_tokens!(@ast::Item)
223 impl_to_tokens_self!(&'a [@ast::Item])
224 impl_to_tokens!(ast::Ty)
225 impl_to_tokens_self!(&'a [ast::Ty])
226 impl_to_tokens!(Generics)
227 impl_to_tokens!(@ast::Expr)
228 impl_to_tokens!(ast::Block)
229 impl_to_tokens_self!(&'a str)
235 impl_to_tokens!(uint)
241 pub trait ExtParseUtils {
242 fn parse_item(&self, s: ~str) -> @ast::Item;
243 fn parse_expr(&self, s: ~str) -> @ast::Expr;
244 fn parse_stmt(&self, s: ~str) -> @ast::Stmt;
245 fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> ;
248 impl<'a> ExtParseUtils for ExtCtxt<'a> {
250 fn parse_item(&self, s: ~str) -> @ast::Item {
251 let res = parse::parse_item_from_source_str(
252 "<quote expansion>".to_str(),
259 error!("parse error");
265 fn parse_stmt(&self, s: ~str) -> @ast::Stmt {
266 parse::parse_stmt_from_source_str("<quote expansion>".to_str(),
273 fn parse_expr(&self, s: ~str) -> @ast::Expr {
274 parse::parse_expr_from_source_str("<quote expansion>".to_str(),
280 fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> {
281 parse::parse_tts_from_source_str("<quote expansion>".to_str(),
290 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
292 tts: &[ast::TokenTree]) -> ~base::MacResult {
293 let (cx_expr, expr) = expand_tts(cx, sp, tts);
294 let expanded = expand_wrapper(cx, sp, cx_expr, expr);
295 base::MacExpr::new(expanded)
298 pub fn expand_quote_expr(cx: &mut ExtCtxt,
300 tts: &[ast::TokenTree]) -> ~base::MacResult {
301 let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
302 base::MacExpr::new(expanded)
305 pub fn expand_quote_item(cx: &mut ExtCtxt,
307 tts: &[ast::TokenTree]) -> ~base::MacResult {
308 let e_attrs = cx.expr_vec_ng(sp);
309 let expanded = expand_parse_call(cx, sp, "parse_item",
311 base::MacExpr::new(expanded)
314 pub fn expand_quote_pat(cx: &mut ExtCtxt,
316 tts: &[ast::TokenTree]) -> ~base::MacResult {
317 let e_refutable = cx.expr_lit(sp, ast::LitBool(true));
318 let expanded = expand_parse_call(cx, sp, "parse_pat",
319 vec!(e_refutable), tts);
320 base::MacExpr::new(expanded)
323 pub fn expand_quote_ty(cx: &mut ExtCtxt,
325 tts: &[ast::TokenTree]) -> ~base::MacResult {
326 let e_param_colons = cx.expr_lit(sp, ast::LitBool(false));
327 let expanded = expand_parse_call(cx, sp, "parse_ty",
328 vec!(e_param_colons), tts);
329 base::MacExpr::new(expanded)
332 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
334 tts: &[ast::TokenTree]) -> ~base::MacResult {
335 let e_attrs = cx.expr_vec_ng(sp);
336 let expanded = expand_parse_call(cx, sp, "parse_stmt",
338 base::MacExpr::new(expanded)
341 fn ids_ext(strs: Vec<~str> ) -> Vec<ast::Ident> {
342 strs.iter().map(|str| str_to_ident(*str)).collect()
345 fn id_ext(str: &str) -> ast::Ident {
349 // Lift an ident to the expr that evaluates to that ident.
350 fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> @ast::Expr {
351 let e_str = cx.expr_str(sp, token::get_ident(ident));
352 cx.expr_method_call(sp,
353 cx.expr_ident(sp, id_ext("ext_cx")),
358 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
359 let name = match bop {
364 PERCENT => "PERCENT",
371 cx.expr_ident(sp, id_ext(name))
374 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
378 return cx.expr_call_ident(sp,
380 vec!(mk_binop(cx, sp, binop)));
383 return cx.expr_call_ident(sp,
385 vec!(mk_binop(cx, sp, binop)));
389 let e_char = cx.expr_lit(sp, ast::LitChar(i));
391 return cx.expr_call_ident(sp, id_ext("LIT_CHAR"), vec!(e_char));
395 let s_ity = match ity {
396 ast::TyI => "TyI".to_owned(),
397 ast::TyI8 => "TyI8".to_owned(),
398 ast::TyI16 => "TyI16".to_owned(),
399 ast::TyI32 => "TyI32".to_owned(),
400 ast::TyI64 => "TyI64".to_owned()
402 let e_ity = cx.expr_ident(sp, id_ext(s_ity));
404 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
406 return cx.expr_call_ident(sp,
411 LIT_UINT(u, uty) => {
412 let s_uty = match uty {
413 ast::TyU => "TyU".to_owned(),
414 ast::TyU8 => "TyU8".to_owned(),
415 ast::TyU16 => "TyU16".to_owned(),
416 ast::TyU32 => "TyU32".to_owned(),
417 ast::TyU64 => "TyU64".to_owned()
419 let e_uty = cx.expr_ident(sp, id_ext(s_uty));
421 let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
423 return cx.expr_call_ident(sp,
428 LIT_INT_UNSUFFIXED(i) => {
429 let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
431 return cx.expr_call_ident(sp,
432 id_ext("LIT_INT_UNSUFFIXED"),
436 LIT_FLOAT(fident, fty) => {
437 let s_fty = match fty {
438 ast::TyF32 => "TyF32".to_owned(),
439 ast::TyF64 => "TyF64".to_owned()
441 let e_fty = cx.expr_ident(sp, id_ext(s_fty));
443 let e_fident = mk_ident(cx, sp, fident);
445 return cx.expr_call_ident(sp,
447 vec!(e_fident, e_fty));
451 return cx.expr_call_ident(sp,
453 vec!(mk_ident(cx, sp, ident)));
456 LIT_STR_RAW(ident, n) => {
457 return cx.expr_call_ident(sp,
458 id_ext("LIT_STR_RAW"),
459 vec!(mk_ident(cx, sp, ident),
460 cx.expr_uint(sp, n)));
464 return cx.expr_call_ident(sp,
466 vec!(mk_ident(cx, sp, ident),
467 cx.expr_bool(sp, b)));
471 return cx.expr_call_ident(sp,
473 vec!(mk_ident(cx, sp, ident)));
476 DOC_COMMENT(ident) => {
477 return cx.expr_call_ident(sp,
478 id_ext("DOC_COMMENT"),
479 vec!(mk_ident(cx, sp, ident)));
482 INTERPOLATED(_) => fail!("quote! with interpolated token"),
487 let name = match *tok {
505 MOD_SEP => "MOD_SEP",
509 FAT_ARROW => "FAT_ARROW",
512 LBRACKET => "LBRACKET",
513 RBRACKET => "RBRACKET",
518 UNDERSCORE => "UNDERSCORE",
522 cx.expr_ident(sp, id_ext(name))
526 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> {
530 ast::TTTok(sp, ref tok) => {
531 let e_sp = cx.expr_ident(sp, id_ext("_sp"));
532 let e_tok = cx.expr_call_ident(sp,
534 vec!(e_sp, mk_token(cx, sp, tok)));
536 cx.expr_method_call(sp,
537 cx.expr_ident(sp, id_ext("tt")),
540 vec!(cx.stmt_expr(e_push))
543 ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
544 ast::TTSeq(..) => fail!("TTSeq in quote!"),
546 ast::TTNonterminal(sp, ident) => {
548 // tt.push_all_move($ident.to_tokens(ext_cx))
551 cx.expr_method_call(sp,
552 cx.expr_ident(sp, ident),
554 vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
557 cx.expr_method_call(sp,
558 cx.expr_ident(sp, id_ext("tt")),
559 id_ext("push_all_move"),
562 vec!(cx.stmt_expr(e_push))
567 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
569 let mut ss = Vec::new();
570 for tt in tts.iter() {
571 ss.push_all_move(mk_tt(cx, sp, tt));
576 fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
577 -> (@ast::Expr, @ast::Expr) {
578 // NB: It appears that the main parser loses its mind if we consider
579 // $foo as a TTNonterminal during the main parse, so we have to re-parse
580 // under quote_depth > 0. This is silly and should go away; the _guess_ is
581 // it has to do with transition away from supporting old-style macros, so
582 // try removing it when enough of them are gone.
584 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
587 .map(|x| (*x).clone())
591 let cx_expr = p.parse_expr();
592 if !p.eat(&token::COMMA) {
593 p.fatal("expected token `,`");
596 let tts = p.parse_all_token_trees();
599 // We also bind a single value, sp, to ext_cx.call_site()
601 // This causes every span in a token-tree quote to be attributed to the
602 // call site of the extension using the quote. We can't really do much
603 // better since the source of the quote may well be in a library that
604 // was not even parsed by this compilation run, that the user has no
605 // source code for (eg. in libsyntax, which they're just _using_).
607 // The old quasiquoter had an elaborate mechanism for denoting input
608 // file locations from which quotes originated; unfortunately this
609 // relied on feeding the source string of the quote back into the
610 // compiler (which we don't really want to do) and, in any case, only
611 // pushed the problem a very small step further back: an error
612 // resulting from a parse of the resulting quote is still attributed to
613 // the site the string literal occurred, which was in a source file
614 // _other_ than the one the user has control over. For example, an
615 // error in a quote from the protocol compiler, invoked in user code
616 // using macro_rules! for example, will be attributed to the macro_rules.rs
617 // file in libsyntax, which the user might not even have source to (unless
618 // they happen to have a compiler on hand). Over all, the phase distinction
619 // just makes quotes "hard to attribute". Possibly this could be fixed
620 // by recreating some of the original qq machinery in the tt regime
621 // (pushing fake FileMaps onto the parser to account for original sites
622 // of quotes, for example) but at this point it seems not likely to be
625 let e_sp = cx.expr_method_call(sp,
626 cx.expr_ident(sp, id_ext("ext_cx")),
630 let stmt_let_sp = cx.stmt_let(sp, false,
634 let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
636 let mut vector = vec!(stmt_let_sp, stmt_let_tt);
637 vector.push_all_move(mk_tts(cx, sp, tts.as_slice()));
638 let block = cx.expr_block(
642 Some(cx.expr_ident(sp, id_ext("tt")))));
647 fn expand_wrapper(cx: &ExtCtxt,
650 expr: @ast::Expr) -> @ast::Expr {
651 let uses = vec!( cx.view_use_glob(sp, ast::Inherited,
652 ids_ext(vec!("syntax".to_owned(),
655 "rt".to_owned()))) );
657 let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
659 cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
662 fn expand_parse_call(cx: &ExtCtxt,
665 arg_exprs: Vec<@ast::Expr> ,
666 tts: &[ast::TokenTree]) -> @ast::Expr {
667 let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
669 let cfg_call = || cx.expr_method_call(
670 sp, cx.expr_ident(sp, id_ext("ext_cx")),
671 id_ext("cfg"), Vec::new());
673 let parse_sess_call = || cx.expr_method_call(
674 sp, cx.expr_ident(sp, id_ext("ext_cx")),
675 id_ext("parse_sess"), Vec::new());
677 let new_parser_call =
679 cx.expr_ident(sp, id_ext("new_parser_from_tts")),
680 vec!(parse_sess_call(), cfg_call(), tts_expr));
682 let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
685 expand_wrapper(cx, sp, cx_expr, expr)