]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/quote.rs
7d86b9880779c139917c6e38b3f02018e3c1c6eb
[rust.git] / src / libsyntax / ext / quote.rs
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.
4 //
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.
10
11 use ast;
12 use codemap::Span;
13 use ext::base::ExtCtxt;
14 use ext::base;
15 use ext::build::AstBuilder;
16 use parse::token::*;
17 use parse::token;
18 use parse;
19
20 /**
21 *
22 * Quasiquoting works via token trees.
23 *
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).
28 *
29 */
30
31 pub mod rt {
32     use ast;
33     use ext::base::ExtCtxt;
34     use parse::token;
35     use parse;
36     use print::pprust;
37
38     pub use ast::*;
39     pub use parse::token::*;
40     pub use parse::new_parser_from_tts;
41     pub use codemap::{BytePos, Span, dummy_spanned};
42
43     pub trait ToTokens {
44         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
45     }
46
47     impl ToTokens for Vec<TokenTree> {
48         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
49             (*self).clone()
50         }
51     }
52
53     /* Should be (when bugs in default methods are fixed):
54
55     trait ToSource : ToTokens {
56         // Takes a thing and generates a string containing rust code for it.
57         pub fn to_source() -> ~str;
58
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())
62         }
63     }
64
65     */
66
67     pub trait ToSource {
68         // Takes a thing and generates a string containing rust code for it.
69         fn to_source(&self) -> ~str;
70     }
71
72     impl ToSource for ast::Ident {
73         fn to_source(&self) -> ~str {
74             get_ident(*self).get().to_str()
75         }
76     }
77
78     impl ToSource for @ast::Item {
79         fn to_source(&self) -> ~str {
80             pprust::item_to_str(*self)
81         }
82     }
83
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")
87         }
88     }
89
90     impl ToSource for ast::Ty {
91         fn to_source(&self) -> ~str {
92             pprust::ty_to_str(self)
93         }
94     }
95
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(", ")
99         }
100     }
101
102     impl ToSource for Generics {
103         fn to_source(&self) -> ~str {
104             pprust::generics_to_str(self)
105         }
106     }
107
108     impl ToSource for @ast::Expr {
109         fn to_source(&self) -> ~str {
110             pprust::expr_to_str(*self)
111         }
112     }
113
114     impl ToSource for ast::Block {
115         fn to_source(&self) -> ~str {
116             pprust::block_to_str(self)
117         }
118     }
119
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)
125         }
126     }
127
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)
132         }
133     }
134
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)
139         }
140     }
141
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)
146         }
147     }
148
149
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)
154         }
155     }
156
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)
161         }
162     }
163
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)
168         }
169     }
170
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)
175         }
176     }
177
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)
182         }
183     }
184
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)
189         }
190     }
191
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)
196         }
197     }
198
199     // Alas ... we write these out instead. All redundant.
200
201     macro_rules! impl_to_tokens(
202         ($t:ty) => (
203             impl ToTokens for $t {
204                 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
205                     cx.parse_tts(self.to_source())
206                 }
207             }
208         )
209     )
210
211     macro_rules! impl_to_tokens_self(
212         ($t:ty) => (
213             impl<'a> ToTokens for $t {
214                 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
215                     cx.parse_tts(self.to_source())
216                 }
217             }
218         )
219     )
220
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)
230     impl_to_tokens!(int)
231     impl_to_tokens!(i8)
232     impl_to_tokens!(i16)
233     impl_to_tokens!(i32)
234     impl_to_tokens!(i64)
235     impl_to_tokens!(uint)
236     impl_to_tokens!(u8)
237     impl_to_tokens!(u16)
238     impl_to_tokens!(u32)
239     impl_to_tokens!(u64)
240
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> ;
246     }
247
248     impl<'a> ExtParseUtils for ExtCtxt<'a> {
249
250         fn parse_item(&self, s: ~str) -> @ast::Item {
251             let res = parse::parse_item_from_source_str(
252                 "<quote expansion>".to_str(),
253                 s,
254                 self.cfg(),
255                 self.parse_sess());
256             match res {
257                 Some(ast) => ast,
258                 None => {
259                     error!("parse error");
260                     fail!()
261                 }
262             }
263         }
264
265         fn parse_stmt(&self, s: ~str) -> @ast::Stmt {
266             parse::parse_stmt_from_source_str("<quote expansion>".to_str(),
267                                               s,
268                                               self.cfg(),
269                                               Vec::new(),
270                                               self.parse_sess())
271         }
272
273         fn parse_expr(&self, s: ~str) -> @ast::Expr {
274             parse::parse_expr_from_source_str("<quote expansion>".to_str(),
275                                               s,
276                                               self.cfg(),
277                                               self.parse_sess())
278         }
279
280         fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> {
281             parse::parse_tts_from_source_str("<quote expansion>".to_str(),
282                                              s,
283                                              self.cfg(),
284                                              self.parse_sess())
285         }
286     }
287
288 }
289
290 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
291                            sp: Span,
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)
296 }
297
298 pub fn expand_quote_expr(cx: &mut ExtCtxt,
299                          sp: Span,
300                          tts: &[ast::TokenTree]) -> ~base::MacResult {
301     let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
302     base::MacExpr::new(expanded)
303 }
304
305 pub fn expand_quote_item(cx: &mut ExtCtxt,
306                          sp: Span,
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",
310                                     vec!(e_attrs), tts);
311     base::MacExpr::new(expanded)
312 }
313
314 pub fn expand_quote_pat(cx: &mut ExtCtxt,
315                         sp: Span,
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)
321 }
322
323 pub fn expand_quote_ty(cx: &mut ExtCtxt,
324                        sp: Span,
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)
330 }
331
332 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
333                          sp: Span,
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",
337                                     vec!(e_attrs), tts);
338     base::MacExpr::new(expanded)
339 }
340
341 fn ids_ext(strs: Vec<~str> ) -> Vec<ast::Ident> {
342     strs.iter().map(|str| str_to_ident(*str)).collect()
343 }
344
345 fn id_ext(str: &str) -> ast::Ident {
346     str_to_ident(str)
347 }
348
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")),
354                         id_ext("ident_of"),
355                         vec!(e_str))
356 }
357
358 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
359     let name = match bop {
360         PLUS => "PLUS",
361         MINUS => "MINUS",
362         STAR => "STAR",
363         SLASH => "SLASH",
364         PERCENT => "PERCENT",
365         CARET => "CARET",
366         AND => "AND",
367         OR => "OR",
368         SHL => "SHL",
369         SHR => "SHR"
370     };
371     cx.expr_ident(sp, id_ext(name))
372 }
373
374 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
375
376     match *tok {
377         BINOP(binop) => {
378             return cx.expr_call_ident(sp,
379                                       id_ext("BINOP"),
380                                       vec!(mk_binop(cx, sp, binop)));
381         }
382         BINOPEQ(binop) => {
383             return cx.expr_call_ident(sp,
384                                       id_ext("BINOPEQ"),
385                                       vec!(mk_binop(cx, sp, binop)));
386         }
387
388         LIT_CHAR(i) => {
389             let e_char = cx.expr_lit(sp, ast::LitChar(i));
390
391             return cx.expr_call_ident(sp, id_ext("LIT_CHAR"), vec!(e_char));
392         }
393
394         LIT_INT(i, ity) => {
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()
401             };
402             let e_ity = cx.expr_ident(sp, id_ext(s_ity));
403
404             let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
405
406             return cx.expr_call_ident(sp,
407                                       id_ext("LIT_INT"),
408                                       vec!(e_i64, e_ity));
409         }
410
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()
418             };
419             let e_uty = cx.expr_ident(sp, id_ext(s_uty));
420
421             let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
422
423             return cx.expr_call_ident(sp,
424                                       id_ext("LIT_UINT"),
425                                       vec!(e_u64, e_uty));
426         }
427
428         LIT_INT_UNSUFFIXED(i) => {
429             let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
430
431             return cx.expr_call_ident(sp,
432                                       id_ext("LIT_INT_UNSUFFIXED"),
433                                       vec!(e_i64));
434         }
435
436         LIT_FLOAT(fident, fty) => {
437             let s_fty = match fty {
438                 ast::TyF32 => "TyF32".to_owned(),
439                 ast::TyF64 => "TyF64".to_owned()
440             };
441             let e_fty = cx.expr_ident(sp, id_ext(s_fty));
442
443             let e_fident = mk_ident(cx, sp, fident);
444
445             return cx.expr_call_ident(sp,
446                                       id_ext("LIT_FLOAT"),
447                                       vec!(e_fident, e_fty));
448         }
449
450         LIT_STR(ident) => {
451             return cx.expr_call_ident(sp,
452                                       id_ext("LIT_STR"),
453                                       vec!(mk_ident(cx, sp, ident)));
454         }
455
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)));
461         }
462
463         IDENT(ident, b) => {
464             return cx.expr_call_ident(sp,
465                                       id_ext("IDENT"),
466                                       vec!(mk_ident(cx, sp, ident),
467                                         cx.expr_bool(sp, b)));
468         }
469
470         LIFETIME(ident) => {
471             return cx.expr_call_ident(sp,
472                                       id_ext("LIFETIME"),
473                                       vec!(mk_ident(cx, sp, ident)));
474         }
475
476         DOC_COMMENT(ident) => {
477             return cx.expr_call_ident(sp,
478                                       id_ext("DOC_COMMENT"),
479                                       vec!(mk_ident(cx, sp, ident)));
480         }
481
482         INTERPOLATED(_) => fail!("quote! with interpolated token"),
483
484         _ => ()
485     }
486
487     let name = match *tok {
488         EQ => "EQ",
489         LT => "LT",
490         LE => "LE",
491         EQEQ => "EQEQ",
492         NE => "NE",
493         GE => "GE",
494         GT => "GT",
495         ANDAND => "ANDAND",
496         OROR => "OROR",
497         NOT => "NOT",
498         TILDE => "TILDE",
499         AT => "AT",
500         DOT => "DOT",
501         DOTDOT => "DOTDOT",
502         COMMA => "COMMA",
503         SEMI => "SEMI",
504         COLON => "COLON",
505         MOD_SEP => "MOD_SEP",
506         RARROW => "RARROW",
507         LARROW => "LARROW",
508         DARROW => "DARROW",
509         FAT_ARROW => "FAT_ARROW",
510         LPAREN => "LPAREN",
511         RPAREN => "RPAREN",
512         LBRACKET => "LBRACKET",
513         RBRACKET => "RBRACKET",
514         LBRACE => "LBRACE",
515         RBRACE => "RBRACE",
516         POUND => "POUND",
517         DOLLAR => "DOLLAR",
518         UNDERSCORE => "UNDERSCORE",
519         EOF => "EOF",
520         _ => fail!()
521     };
522     cx.expr_ident(sp, id_ext(name))
523 }
524
525
526 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> {
527
528     match *tt {
529
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,
533                                            id_ext("TTTok"),
534                                            vec!(e_sp, mk_token(cx, sp, tok)));
535             let e_push =
536                 cx.expr_method_call(sp,
537                                     cx.expr_ident(sp, id_ext("tt")),
538                                     id_ext("push"),
539                                     vec!(e_tok));
540             vec!(cx.stmt_expr(e_push))
541         }
542
543         ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
544         ast::TTSeq(..) => fail!("TTSeq in quote!"),
545
546         ast::TTNonterminal(sp, ident) => {
547
548             // tt.push_all_move($ident.to_tokens(ext_cx))
549
550             let e_to_toks =
551                 cx.expr_method_call(sp,
552                                     cx.expr_ident(sp, ident),
553                                     id_ext("to_tokens"),
554                                     vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
555
556             let e_push =
557                 cx.expr_method_call(sp,
558                                     cx.expr_ident(sp, id_ext("tt")),
559                                     id_ext("push_all_move"),
560                                     vec!(e_to_toks));
561
562             vec!(cx.stmt_expr(e_push))
563         }
564     }
565 }
566
567 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
568     -> Vec<@ast::Stmt> {
569     let mut ss = Vec::new();
570     for tt in tts.iter() {
571         ss.push_all_move(mk_tt(cx, sp, tt));
572     }
573     ss
574 }
575
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.
583
584     let mut p = parse::new_parser_from_tts(cx.parse_sess(),
585                                            cx.cfg(),
586                                            tts.iter()
587                                               .map(|x| (*x).clone())
588                                               .collect());
589     p.quote_depth += 1u;
590
591     let cx_expr = p.parse_expr();
592     if !p.eat(&token::COMMA) {
593         p.fatal("expected token `,`");
594     }
595
596     let tts = p.parse_all_token_trees();
597     p.abort_if_errors();
598
599     // We also bind a single value, sp, to ext_cx.call_site()
600     //
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_).
606     //
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
623     // worth the hassle.
624
625     let e_sp = cx.expr_method_call(sp,
626                                    cx.expr_ident(sp, id_ext("ext_cx")),
627                                    id_ext("call_site"),
628                                    Vec::new());
629
630     let stmt_let_sp = cx.stmt_let(sp, false,
631                                   id_ext("_sp"),
632                                   e_sp);
633
634     let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
635
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(
639         cx.block_all(sp,
640                      Vec::new(),
641                      vector,
642                      Some(cx.expr_ident(sp, id_ext("tt")))));
643
644     (cx_expr, block)
645 }
646
647 fn expand_wrapper(cx: &ExtCtxt,
648                   sp: Span,
649                   cx_expr: @ast::Expr,
650                   expr: @ast::Expr) -> @ast::Expr {
651     let uses = vec!( cx.view_use_glob(sp, ast::Inherited,
652                                    ids_ext(vec!("syntax".to_owned(),
653                                              "ext".to_owned(),
654                                              "quote".to_owned(),
655                                              "rt".to_owned()))) );
656
657     let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
658
659     cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
660 }
661
662 fn expand_parse_call(cx: &ExtCtxt,
663                      sp: Span,
664                      parse_method: &str,
665                      arg_exprs: Vec<@ast::Expr> ,
666                      tts: &[ast::TokenTree]) -> @ast::Expr {
667     let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
668
669     let cfg_call = || cx.expr_method_call(
670         sp, cx.expr_ident(sp, id_ext("ext_cx")),
671         id_ext("cfg"), Vec::new());
672
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());
676
677     let new_parser_call =
678         cx.expr_call(sp,
679                      cx.expr_ident(sp, id_ext("new_parser_from_tts")),
680                      vec!(parse_sess_call(), cfg_call(), tts_expr));
681
682     let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
683                                    arg_exprs);
684
685     expand_wrapper(cx, sp, cx_expr, expr)
686 }