]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/quote.rs
libsyntax: Fix errors arising from the automated `~[T]` conversion
[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 use std::vec_ng::Vec;
21
22 /**
23 *
24 * Quasiquoting works via token trees.
25 *
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).
30 *
31 */
32
33 pub mod rt {
34     use ast;
35     use ext::base::ExtCtxt;
36     use parse::token;
37     use parse;
38     use print::pprust;
39
40     use std::vec_ng::Vec;
41
42     pub use ast::*;
43     pub use parse::token::*;
44     pub use parse::new_parser_from_tts;
45     pub use codemap::{BytePos, Span, dummy_spanned};
46
47     pub trait ToTokens {
48         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
49     }
50
51     impl ToTokens for Vec<TokenTree> {
52         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
53             (*self).clone()
54         }
55     }
56
57     /* Should be (when bugs in default methods are fixed):
58
59     trait ToSource : ToTokens {
60         // Takes a thing and generates a string containing rust code for it.
61         pub fn to_source() -> ~str;
62
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())
66         }
67     }
68
69     */
70
71     pub trait ToSource {
72         // Takes a thing and generates a string containing rust code for it.
73         fn to_source(&self) -> ~str;
74     }
75
76     impl ToSource for ast::Ident {
77         fn to_source(&self) -> ~str {
78             get_ident(*self).get().to_str()
79         }
80     }
81
82     impl ToSource for @ast::Item {
83         fn to_source(&self) -> ~str {
84             pprust::item_to_str(*self)
85         }
86     }
87
88     impl<'a> ToSource for &'a [@ast::Item] {
89         fn to_source(&self) -> ~str {
90             self.map(|i| i.to_source()).connect("\n\n")
91         }
92     }
93
94     impl ToSource for ast::Ty {
95         fn to_source(&self) -> ~str {
96             pprust::ty_to_str(self)
97         }
98     }
99
100     impl<'a> ToSource for &'a [ast::Ty] {
101         fn to_source(&self) -> ~str {
102             self.map(|i| i.to_source()).connect(", ")
103         }
104     }
105
106     impl ToSource for Generics {
107         fn to_source(&self) -> ~str {
108             pprust::generics_to_str(self)
109         }
110     }
111
112     impl ToSource for @ast::Expr {
113         fn to_source(&self) -> ~str {
114             pprust::expr_to_str(*self)
115         }
116     }
117
118     impl ToSource for ast::Block {
119         fn to_source(&self) -> ~str {
120             pprust::block_to_str(self)
121         }
122     }
123
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)
129         }
130     }
131
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)
136         }
137     }
138
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)
143         }
144     }
145
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)
150         }
151     }
152
153
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)
158         }
159     }
160
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)
165         }
166     }
167
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)
172         }
173     }
174
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)
179         }
180     }
181
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)
186         }
187     }
188
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)
193         }
194     }
195
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)
200         }
201     }
202
203     // Alas ... we write these out instead. All redundant.
204
205     macro_rules! impl_to_tokens(
206         ($t:ty) => (
207             impl ToTokens for $t {
208                 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
209                     cx.parse_tts(self.to_source())
210                 }
211             }
212         )
213     )
214
215     macro_rules! impl_to_tokens_self(
216         ($t:ty) => (
217             impl<'a> ToTokens for $t {
218                 fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
219                     cx.parse_tts(self.to_source())
220                 }
221             }
222         )
223     )
224
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)
234     impl_to_tokens!(int)
235     impl_to_tokens!(i8)
236     impl_to_tokens!(i16)
237     impl_to_tokens!(i32)
238     impl_to_tokens!(i64)
239     impl_to_tokens!(uint)
240     impl_to_tokens!(u8)
241     impl_to_tokens!(u16)
242     impl_to_tokens!(u32)
243     impl_to_tokens!(u64)
244
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> ;
250     }
251
252     impl<'a> ExtParseUtils for ExtCtxt<'a> {
253
254         fn parse_item(&self, s: ~str) -> @ast::Item {
255             let res = parse::parse_item_from_source_str(
256                 "<quote expansion>".to_str(),
257                 s,
258                 self.cfg(),
259                 self.parse_sess());
260             match res {
261                 Some(ast) => ast,
262                 None => {
263                     error!("parse error");
264                     fail!()
265                 }
266             }
267         }
268
269         fn parse_stmt(&self, s: ~str) -> @ast::Stmt {
270             parse::parse_stmt_from_source_str("<quote expansion>".to_str(),
271                                               s,
272                                               self.cfg(),
273                                               Vec::new(),
274                                               self.parse_sess())
275         }
276
277         fn parse_expr(&self, s: ~str) -> @ast::Expr {
278             parse::parse_expr_from_source_str("<quote expansion>".to_str(),
279                                               s,
280                                               self.cfg(),
281                                               self.parse_sess())
282         }
283
284         fn parse_tts(&self, s: ~str) -> Vec<ast::TokenTree> {
285             parse::parse_tts_from_source_str("<quote expansion>".to_str(),
286                                              s,
287                                              self.cfg(),
288                                              self.parse_sess())
289         }
290     }
291
292 }
293
294 pub fn expand_quote_tokens(cx: &mut ExtCtxt,
295                            sp: Span,
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)
300 }
301
302 pub fn expand_quote_expr(cx: &mut ExtCtxt,
303                          sp: Span,
304                          tts: &[ast::TokenTree]) -> base::MacResult {
305     let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
306     base::MRExpr(expanded)
307 }
308
309 pub fn expand_quote_item(cx: &mut ExtCtxt,
310                          sp: Span,
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",
314                                     vec!(e_attrs), tts);
315     base::MRExpr(expanded)
316 }
317
318 pub fn expand_quote_pat(cx: &mut ExtCtxt,
319                         sp: Span,
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)
325 }
326
327 pub fn expand_quote_ty(cx: &mut ExtCtxt,
328                        sp: Span,
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)
334 }
335
336 pub fn expand_quote_stmt(cx: &mut ExtCtxt,
337                          sp: Span,
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",
341                                     vec!(e_attrs), tts);
342     base::MRExpr(expanded)
343 }
344
345 fn ids_ext(strs: Vec<~str> ) -> Vec<ast::Ident> {
346     strs.map(|str| str_to_ident(*str))
347 }
348
349 fn id_ext(str: &str) -> ast::Ident {
350     str_to_ident(str)
351 }
352
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")),
358                         id_ext("ident_of"),
359                         vec!(e_str))
360 }
361
362 fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOp) -> @ast::Expr {
363     let name = match bop {
364         PLUS => "PLUS",
365         MINUS => "MINUS",
366         STAR => "STAR",
367         SLASH => "SLASH",
368         PERCENT => "PERCENT",
369         CARET => "CARET",
370         AND => "AND",
371         OR => "OR",
372         SHL => "SHL",
373         SHR => "SHR"
374     };
375     cx.expr_ident(sp, id_ext(name))
376 }
377
378 fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> @ast::Expr {
379
380     match *tok {
381         BINOP(binop) => {
382             return cx.expr_call_ident(sp,
383                                       id_ext("BINOP"),
384                                       vec!(mk_binop(cx, sp, binop)));
385         }
386         BINOPEQ(binop) => {
387             return cx.expr_call_ident(sp,
388                                       id_ext("BINOPEQ"),
389                                       vec!(mk_binop(cx, sp, binop)));
390         }
391
392         LIT_CHAR(i) => {
393             let e_char = cx.expr_lit(sp, ast::LitChar(i));
394
395             return cx.expr_call_ident(sp, id_ext("LIT_CHAR"), vec!(e_char));
396         }
397
398         LIT_INT(i, ity) => {
399             let s_ity = match ity {
400                 ast::TyI => ~"TyI",
401                 ast::TyI8 => ~"TyI8",
402                 ast::TyI16 => ~"TyI16",
403                 ast::TyI32 => ~"TyI32",
404                 ast::TyI64 => ~"TyI64"
405             };
406             let e_ity = cx.expr_ident(sp, id_ext(s_ity));
407
408             let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
409
410             return cx.expr_call_ident(sp,
411                                       id_ext("LIT_INT"),
412                                       vec!(e_i64, e_ity));
413         }
414
415         LIT_UINT(u, uty) => {
416             let s_uty = match uty {
417                 ast::TyU => ~"TyU",
418                 ast::TyU8 => ~"TyU8",
419                 ast::TyU16 => ~"TyU16",
420                 ast::TyU32 => ~"TyU32",
421                 ast::TyU64 => ~"TyU64"
422             };
423             let e_uty = cx.expr_ident(sp, id_ext(s_uty));
424
425             let e_u64 = cx.expr_lit(sp, ast::LitUint(u, ast::TyU64));
426
427             return cx.expr_call_ident(sp,
428                                       id_ext("LIT_UINT"),
429                                       vec!(e_u64, e_uty));
430         }
431
432         LIT_INT_UNSUFFIXED(i) => {
433             let e_i64 = cx.expr_lit(sp, ast::LitInt(i, ast::TyI64));
434
435             return cx.expr_call_ident(sp,
436                                       id_ext("LIT_INT_UNSUFFIXED"),
437                                       vec!(e_i64));
438         }
439
440         LIT_FLOAT(fident, fty) => {
441             let s_fty = match fty {
442                 ast::TyF32 => ~"TyF32",
443                 ast::TyF64 => ~"TyF64"
444             };
445             let e_fty = cx.expr_ident(sp, id_ext(s_fty));
446
447             let e_fident = mk_ident(cx, sp, fident);
448
449             return cx.expr_call_ident(sp,
450                                       id_ext("LIT_FLOAT"),
451                                       vec!(e_fident, e_fty));
452         }
453
454         LIT_STR(ident) => {
455             return cx.expr_call_ident(sp,
456                                       id_ext("LIT_STR"),
457                                       vec!(mk_ident(cx, sp, ident)));
458         }
459
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)));
465         }
466
467         IDENT(ident, b) => {
468             return cx.expr_call_ident(sp,
469                                       id_ext("IDENT"),
470                                       vec!(mk_ident(cx, sp, ident),
471                                         cx.expr_bool(sp, b)));
472         }
473
474         LIFETIME(ident) => {
475             return cx.expr_call_ident(sp,
476                                       id_ext("LIFETIME"),
477                                       vec!(mk_ident(cx, sp, ident)));
478         }
479
480         DOC_COMMENT(ident) => {
481             return cx.expr_call_ident(sp,
482                                       id_ext("DOC_COMMENT"),
483                                       vec!(mk_ident(cx, sp, ident)));
484         }
485
486         INTERPOLATED(_) => fail!("quote! with interpolated token"),
487
488         _ => ()
489     }
490
491     let name = match *tok {
492         EQ => "EQ",
493         LT => "LT",
494         LE => "LE",
495         EQEQ => "EQEQ",
496         NE => "NE",
497         GE => "GE",
498         GT => "GT",
499         ANDAND => "ANDAND",
500         OROR => "OROR",
501         NOT => "NOT",
502         TILDE => "TILDE",
503         AT => "AT",
504         DOT => "DOT",
505         DOTDOT => "DOTDOT",
506         COMMA => "COMMA",
507         SEMI => "SEMI",
508         COLON => "COLON",
509         MOD_SEP => "MOD_SEP",
510         RARROW => "RARROW",
511         LARROW => "LARROW",
512         DARROW => "DARROW",
513         FAT_ARROW => "FAT_ARROW",
514         LPAREN => "LPAREN",
515         RPAREN => "RPAREN",
516         LBRACKET => "LBRACKET",
517         RBRACKET => "RBRACKET",
518         LBRACE => "LBRACE",
519         RBRACE => "RBRACE",
520         POUND => "POUND",
521         DOLLAR => "DOLLAR",
522         UNDERSCORE => "UNDERSCORE",
523         EOF => "EOF",
524         _ => fail!()
525     };
526     cx.expr_ident(sp, id_ext(name))
527 }
528
529
530 fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> {
531
532     match *tt {
533
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,
537                                            id_ext("TTTok"),
538                                            vec!(e_sp, mk_token(cx, sp, tok)));
539             let e_push =
540                 cx.expr_method_call(sp,
541                                     cx.expr_ident(sp, id_ext("tt")),
542                                     id_ext("push"),
543                                     vec!(e_tok));
544             vec!(cx.stmt_expr(e_push))
545         }
546
547         ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
548         ast::TTSeq(..) => fail!("TTSeq in quote!"),
549
550         ast::TTNonterminal(sp, ident) => {
551
552             // tt.push_all_move($ident.to_tokens(ext_cx))
553
554             let e_to_toks =
555                 cx.expr_method_call(sp,
556                                     cx.expr_ident(sp, ident),
557                                     id_ext("to_tokens"),
558                                     vec!(cx.expr_ident(sp, id_ext("ext_cx"))));
559
560             let e_push =
561                 cx.expr_method_call(sp,
562                                     cx.expr_ident(sp, id_ext("tt")),
563                                     id_ext("push_all_move"),
564                                     vec!(e_to_toks));
565
566             vec!(cx.stmt_expr(e_push))
567         }
568     }
569 }
570
571 fn mk_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
572     -> Vec<@ast::Stmt> {
573     let mut ss = Vec::new();
574     for tt in tts.iter() {
575         ss.push_all_move(mk_tt(cx, sp, tt));
576     }
577     ss
578 }
579
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.
587
588     let mut p = parse::new_parser_from_tts(cx.parse_sess(),
589                                            cx.cfg(),
590                                            tts.iter()
591                                               .map(|x| (*x).clone())
592                                               .collect());
593     p.quote_depth += 1u;
594
595     let cx_expr = p.parse_expr();
596     if !p.eat(&token::COMMA) {
597         p.fatal("expected token `,`");
598     }
599
600     let tts = p.parse_all_token_trees();
601     p.abort_if_errors();
602
603     // We also bind a single value, sp, to ext_cx.call_site()
604     //
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_).
610     //
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
627     // worth the hassle.
628
629     let e_sp = cx.expr_method_call(sp,
630                                    cx.expr_ident(sp, id_ext("ext_cx")),
631                                    id_ext("call_site"),
632                                    Vec::new());
633
634     let stmt_let_sp = cx.stmt_let(sp, false,
635                                   id_ext("_sp"),
636                                   e_sp);
637
638     let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
639
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(
643         cx.block_all(sp,
644                      Vec::new(),
645                      vector,
646                      Some(cx.expr_ident(sp, id_ext("tt")))));
647
648     (cx_expr, block)
649 }
650
651 fn expand_wrapper(cx: &ExtCtxt,
652                   sp: Span,
653                   cx_expr: @ast::Expr,
654                   expr: @ast::Expr) -> @ast::Expr {
655     let uses = vec!( cx.view_use_glob(sp, ast::Inherited,
656                                    ids_ext(vec!(~"syntax",
657                                              ~"ext",
658                                              ~"quote",
659                                              ~"rt"))) );
660
661     let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr);
662
663     cx.expr_block(cx.block_all(sp, uses, vec!(stmt_let_ext_cx), Some(expr)))
664 }
665
666 fn expand_parse_call(cx: &ExtCtxt,
667                      sp: Span,
668                      parse_method: &str,
669                      arg_exprs: Vec<@ast::Expr> ,
670                      tts: &[ast::TokenTree]) -> @ast::Expr {
671     let (cx_expr, tts_expr) = expand_tts(cx, sp, tts);
672
673     let cfg_call = || cx.expr_method_call(
674         sp, cx.expr_ident(sp, id_ext("ext_cx")),
675         id_ext("cfg"), Vec::new());
676
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());
680
681     let new_parser_call =
682         cx.expr_call(sp,
683                      cx.expr_ident(sp, id_ext("new_parser_from_tts")),
684                      vec!(parse_sess_call(), cfg_call(), tts_expr));
685
686     let expr = cx.expr_method_call(sp, new_parser_call, id_ext(parse_method),
687                                    arg_exprs);
688
689     expand_wrapper(cx, sp, cx_expr, expr)
690 }