]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/quote.rs
Merge remote-tracking branch 'brson/codemap'
[rust.git] / src / libsyntax / ext / quote.rs
1 use mod ast;
2 use mod parse::token;
3
4 use codemap::{span, BytePos};
5 use ext::base::ext_ctxt;
6 use token::*;
7
8 /**
9 *
10 * Quasiquoting works via token trees.
11 *
12 * This is registered as a expression syntax extension called quote! that lifts
13 * its argument token-tree to an AST representing the construction of the same
14 * token tree, with ast::tt_nonterminal nodes interpreted as antiquotes
15 * (splices).
16 *
17 */
18
19 pub mod rt {
20     pub use ast::*;
21     pub use parse::token::*;
22     pub use parse::new_parser_from_tt;
23     pub use codemap::BytePos;
24     pub use codemap::span;
25 }
26
27 pub fn expand_quote_tokens(cx: ext_ctxt,
28                            sp: span,
29                            tts: ~[ast::token_tree]) -> base::mac_result {
30     base::mr_expr(expand_tt(cx, sp, tts))
31 }
32
33 pub fn expand_quote_expr(cx: ext_ctxt,
34                          sp: span,
35                          tts: ~[ast::token_tree]) -> base::mac_result {
36     base::mr_expr(expand_parse_call(cx, sp, ~"parse_expr", ~[], tts))
37 }
38
39 pub fn expand_quote_item(cx: ext_ctxt,
40                          sp: span,
41                          tts: ~[ast::token_tree]) -> base::mac_result {
42     let e_attrs = build::mk_uniq_vec_e(cx, sp, ~[]);
43     base::mr_expr(expand_parse_call(cx, sp, ~"parse_item",
44                                     ~[e_attrs], tts))
45 }
46
47 pub fn expand_quote_pat(cx: ext_ctxt,
48                         sp: span,
49                         tts: ~[ast::token_tree]) -> base::mac_result {
50     let e_refutable = build::mk_lit(cx, sp, ast::lit_bool(true));
51     base::mr_expr(expand_parse_call(cx, sp, ~"parse_pat",
52                                     ~[e_refutable], tts))
53 }
54
55 pub fn expand_quote_type(cx: ext_ctxt,
56                          sp: span,
57                          tts: ~[ast::token_tree]) -> base::mac_result {
58     let e_param_colons = build::mk_lit(cx, sp, ast::lit_bool(false));
59     base::mr_expr(expand_parse_call(cx, sp, ~"parse_type",
60                                     ~[e_param_colons], tts))
61 }
62
63 pub fn expand_quote_stmt(cx: ext_ctxt,
64                          sp: span,
65                          tts: ~[ast::token_tree]) -> base::mac_result {
66     let e_attrs = build::mk_uniq_vec_e(cx, sp, ~[]);
67     base::mr_expr(expand_parse_call(cx, sp, ~"parse_stmt",
68                                     ~[e_attrs], tts))
69 }
70
71 fn ids_ext(cx: ext_ctxt, strs: ~[~str]) -> ~[ast::ident] {
72     strs.map(|str| cx.parse_sess().interner.intern(@*str))
73 }
74
75 fn id_ext(cx: ext_ctxt, str: ~str) -> ast::ident {
76     cx.parse_sess().interner.intern(@str)
77 }
78
79 fn mk_option_span(cx: ext_ctxt,
80                   qsp: span,
81                   sp: Option<span>) -> @ast::expr {
82     match sp {
83         None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])),
84         Some(sp) => {
85             build::mk_call(cx, qsp,
86                            ids_ext(cx, ~[~"Some"]),
87                            ~[build::mk_managed(cx, qsp,
88                                                mk_span(cx, qsp, sp))])
89         }
90     }
91 }
92
93 fn mk_span(cx: ext_ctxt, qsp: span, sp: span) -> @ast::expr {
94
95     let e_expn_info = match sp.expn_info {
96         None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])),
97         Some(@codemap::ExpandedFrom(cr)) => {
98             let e_callee =
99                 build::mk_rec_e(
100                     cx, qsp,
101                     ~[{ident: id_ext(cx, ~"name"),
102                        ex: build::mk_uniq_str(cx, qsp,
103                                               cr.callie.name)},
104                       {ident: id_ext(cx, ~"span"),
105                        ex: mk_option_span(cx, qsp, cr.callie.span)}]);
106
107             let e_expn_info_ =
108                 build::mk_call(
109                     cx, qsp,
110                     ids_ext(cx, ~[~"expanded_from"]),
111                     ~[build::mk_rec_e(
112                         cx, qsp,
113                         ~[{ident: id_ext(cx, ~"call_site"),
114                            ex: mk_span(cx, qsp, cr.call_site)},
115                           {ident: id_ext(cx, ~"callie"),
116                            ex: e_callee}])]);
117
118             build::mk_call(cx, qsp,
119                            ids_ext(cx, ~[~"Some"]),
120                            ~[build::mk_managed(cx, qsp, e_expn_info_)])
121         }
122     };
123
124     let span_path = ids_ext(
125         cx, ~[~"syntax", ~"ext", ~"quote", ~"rt", ~"span"]);
126
127     build::mk_struct_e(cx, qsp,
128                        span_path,
129                     ~[{ident: id_ext(cx, ~"lo"),
130                        ex: mk_bytepos(cx, qsp, sp.lo) },
131
132                       {ident: id_ext(cx, ~"hi"),
133                        ex: mk_bytepos(cx, qsp, sp.hi) },
134
135                       {ident: id_ext(cx, ~"expn_info"),
136                        ex: e_expn_info}])
137 }
138
139 // Lift an ident to the expr that evaluates to that ident.
140 //
141 // NB: this identifies the interner used when re-parsing the token tree
142 // with the interner used during initial parse. This is _wrong_ and we
143 // should be emitting a &str here and the token type should be ok with
144 // &static/str or &session/str. Longer-term issue.
145 fn mk_ident(cx: ext_ctxt, sp: span, ident: ast::ident) -> @ast::expr {
146     build::mk_struct_e(cx, sp,
147                        ids_ext(cx, ~[~"ident"]),
148                        ~[{ident: id_ext(cx, ~"repr"),
149                           ex: build::mk_uint(cx, sp, ident.repr) }])
150 }
151
152 fn mk_bytepos(cx: ext_ctxt, sp: span, bpos: BytePos) -> @ast::expr {
153     let path = ids_ext(cx, ~[~"syntax", ~"ext", ~"quote", ~"rt", ~"BytePos"]);
154     let arg = build::mk_uint(cx, sp, bpos.to_uint());
155     build::mk_call(cx, sp, path, ~[arg])
156 }
157
158 fn mk_binop(cx: ext_ctxt, sp: span, bop: token::binop) -> @ast::expr {
159     let name = match bop {
160         PLUS => "PLUS",
161         MINUS => "MINUS",
162         STAR => "STAR",
163         SLASH => "SLASH",
164         PERCENT => "PERCENT",
165         CARET => "CARET",
166         AND => "AND",
167         OR => "OR",
168         SHL => "SHL",
169         SHR => "SHR"
170     };
171     build::mk_path(cx, sp,
172                    ids_ext(cx, ~[name.to_owned()]))
173 }
174
175 fn mk_token(cx: ext_ctxt, sp: span, tok: token::Token) -> @ast::expr {
176
177     match tok {
178         BINOP(binop) => {
179             return build::mk_call(cx, sp,
180                                   ids_ext(cx, ~[~"BINOP"]),
181                                   ~[mk_binop(cx, sp, binop)]);
182         }
183         BINOPEQ(binop) => {
184             return build::mk_call(cx, sp,
185                                   ids_ext(cx, ~[~"BINOPEQ"]),
186                                   ~[mk_binop(cx, sp, binop)]);
187         }
188
189         LIT_INT(i, ity) => {
190             let s_ity = match ity {
191                 ast::ty_i => ~"ty_i",
192                 ast::ty_char => ~"ty_char",
193                 ast::ty_i8 => ~"ty_i8",
194                 ast::ty_i16 => ~"ty_i16",
195                 ast::ty_i32 => ~"ty_i32",
196                 ast::ty_i64 => ~"ty_i64"
197             };
198             let e_ity =
199                 build::mk_path(cx, sp,
200                                ids_ext(cx, ~[s_ity]));
201
202             let e_i64 = build::mk_lit(cx, sp, ast::lit_int(i, ast::ty_i64));
203
204             return build::mk_call(cx, sp,
205                                   ids_ext(cx, ~[~"LIT_INT"]),
206                                   ~[e_i64, e_ity]);
207         }
208
209         LIT_UINT(u, uty) => {
210             let s_uty = match uty {
211                 ast::ty_u => ~"ty_u",
212                 ast::ty_u8 => ~"ty_u8",
213                 ast::ty_u16 => ~"ty_u16",
214                 ast::ty_u32 => ~"ty_u32",
215                 ast::ty_u64 => ~"ty_u64"
216             };
217             let e_uty =
218                 build::mk_path(cx, sp,
219                                ids_ext(cx, ~[s_uty]));
220
221             let e_u64 = build::mk_lit(cx, sp, ast::lit_uint(u, ast::ty_u64));
222
223             return build::mk_call(cx, sp,
224                                   ids_ext(cx, ~[~"LIT_UINT"]),
225                                   ~[e_u64, e_uty]);
226         }
227
228         LIT_INT_UNSUFFIXED(i) => {
229             let e_i64 = build::mk_lit(cx, sp,
230                                       ast::lit_int(i, ast::ty_i64));
231
232             return build::mk_call(cx, sp,
233                                   ids_ext(cx, ~[~"LIT_INT_UNSUFFIXED"]),
234                                   ~[e_i64]);
235         }
236
237         LIT_FLOAT(fident, fty) => {
238             let s_fty = match fty {
239                 ast::ty_f => ~"ty_f",
240                 ast::ty_f32 => ~"ty_f32",
241                 ast::ty_f64 => ~"ty_f64"
242             };
243             let e_fty =
244                 build::mk_path(cx, sp,
245                                ids_ext(cx, ~[s_fty]));
246
247             let e_fident = mk_ident(cx, sp, fident);
248
249             return build::mk_call(cx, sp,
250                                   ids_ext(cx, ~[~"LIT_FLOAT"]),
251                                   ~[e_fident, e_fty]);
252         }
253
254         LIT_STR(ident) => {
255             return build::mk_call(cx, sp,
256                                   ids_ext(cx, ~[~"LIT_STR"]),
257                                   ~[mk_ident(cx, sp, ident)]);
258         }
259
260         IDENT(ident, b) => {
261             return build::mk_call(cx, sp,
262                                   ids_ext(cx, ~[~"IDENT"]),
263                                   ~[mk_ident(cx, sp, ident),
264                                     build::mk_lit(cx, sp, ast::lit_bool(b))]);
265         }
266
267         DOC_COMMENT(ident) => {
268             return build::mk_call(cx, sp,
269                                   ids_ext(cx, ~[~"DOC_COMMENT"]),
270                                   ~[mk_ident(cx, sp, ident)]);
271         }
272
273         INTERPOLATED(_) => fail ~"quote! with interpolated token",
274
275         _ => ()
276     }
277
278     let name = match tok {
279         EQ => "EQ",
280         LT => "LT",
281         LE => "LE",
282         EQEQ => "EQEQ",
283         NE => "NE",
284         GE => "GE",
285         GT => "GT",
286         ANDAND => "ANDAND",
287         OROR => "OROR",
288         NOT => "NOT",
289         TILDE => "TILDE",
290         AT => "AT",
291         DOT => "DOT",
292         DOTDOT => "DOTDOT",
293         ELLIPSIS => "ELLIPSIS",
294         COMMA => "COMMA",
295         SEMI => "SEMI",
296         COLON => "COLON",
297         MOD_SEP => "MOD_SEP",
298         RARROW => "RARROW",
299         LARROW => "LARROW",
300         DARROW => "DARROW",
301         FAT_ARROW => "FAT_ARROW",
302         LPAREN => "LPAREN",
303         RPAREN => "RPAREN",
304         LBRACKET => "LBRACKET",
305         RBRACKET => "RBRACKET",
306         LBRACE => "LBRACE",
307         RBRACE => "RBRACE",
308         POUND => "POUND",
309         DOLLAR => "DOLLAR",
310         UNDERSCORE => "UNDERSCORE",
311         EOF => "EOF",
312         _ => fail
313     };
314     build::mk_path(cx, sp,
315                    ids_ext(cx, ~[name.to_owned()]))
316 }
317
318
319 fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree) -> @ast::expr {
320     match *tt {
321         ast::tt_tok(sp, tok) =>
322         build::mk_call(cx, sp,
323                        ids_ext(cx, ~[~"tt_tok"]),
324                        ~[mk_span(cx, sp, sp),
325                          mk_token(cx, sp, tok)]),
326
327         ast::tt_delim(tts) => {
328             let e_tts = tts.map(|tt| mk_tt(cx, sp, tt));
329             build::mk_call(cx, sp,
330                            ids_ext(cx, ~[~"tt_delim"]),
331                            ~[build::mk_uniq_vec_e(cx, sp, e_tts)])
332         }
333
334         ast::tt_seq(*) => fail ~"tt_seq in quote!",
335
336         ast::tt_nonterminal(sp, ident) =>
337         build::mk_copy(cx, sp, build::mk_path(cx, sp, ~[ident]))
338     }
339 }
340
341
342 fn expand_tt(cx: ext_ctxt,
343              sp: span,
344              tts: ~[ast::token_tree]) -> @ast::expr {
345     // NB: It appears that the main parser loses its mind if we consider
346     // $foo as a tt_nonterminal during the main parse, so we have to re-parse
347     // under quote_depth > 0. This is silly and should go away; the _guess_ is
348     // it has to do with transition away from supporting old-style macros, so
349     // try removing it when enough of them are gone.
350     let p = parse::new_parser_from_tt(cx.parse_sess(), cx.cfg(), tts);
351     p.quote_depth += 1u;
352     let tq = dvec::DVec();
353     while p.token != token::EOF {
354         tq.push(p.parse_token_tree());
355     }
356     let tts = tq.get();
357
358     // We want to emit a block expression that does a sequence of 'use's to
359     // import the runtime module, followed by a tt expression.
360     let uses = ~[ build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax",
361                                                            ~"ext",
362                                                            ~"quote",
363                                                            ~"rt"])) ];
364     build::mk_block(cx, sp, uses, ~[],
365                     Some(mk_tt(cx, sp, &ast::tt_delim(tts))))
366 }
367
368 fn expand_parse_call(cx: ext_ctxt,
369                      sp: span,
370                      parse_method: ~str,
371                      arg_exprs: ~[@ast::expr],
372                      tts: ~[ast::token_tree]) -> @ast::expr {
373     let tt_expr = expand_tt(cx, sp, tts);
374
375     let cfg_call = || build::mk_call_(
376         cx, sp, build::mk_access(cx, sp, ids_ext(cx, ~[~"ext_cx"]),
377                                  id_ext(cx, ~"cfg")), ~[]);
378
379     let parse_sess_call = || build::mk_call_(
380         cx, sp, build::mk_access(cx, sp, ids_ext(cx, ~[~"ext_cx"]),
381                                  id_ext(cx, ~"parse_sess")), ~[]);
382
383     let new_parser_call =
384         build::mk_call(cx, sp,
385                        ids_ext(cx, ~[~"syntax",
386                                      ~"ext",
387                                      ~"quote",
388                                      ~"rt",
389                                      ~"new_parser_from_tt"]),
390                        ~[parse_sess_call(),
391                          cfg_call(),
392                          build::mk_uniq_vec_e(cx, sp, ~[tt_expr])]);
393
394     build::mk_call_(cx, sp,
395                     build::mk_access_(cx, sp, new_parser_call,
396                                       id_ext(cx, parse_method)),
397                     arg_exprs)
398 }
399