]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/base.rs
Merge remote-tracking branch 'brson/codemap'
[rust.git] / src / libsyntax / ext / base.rs
1 use std::map::HashMap;
2 use parse::parser;
3 use diagnostic::span_handler;
4 use codemap::{CodeMap, span, ExpnInfo, ExpandedFrom};
5 use ast_util::dummy_sp;
6
7 // obsolete old-style #macro code:
8 //
9 //    syntax_expander, normal, macro_defining, macro_definer,
10 //    builtin
11 //
12 // new-style macro! tt code:
13 //
14 //    syntax_expander_tt, syntax_expander_tt_item, mac_result,
15 //    expr_tt, item_tt
16 //
17 // also note that ast::mac has way too many cases and can probably
18 // be trimmed down substantially.
19
20 // second argument is the span to blame for general argument problems
21 type syntax_expander_ =
22     fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> @ast::expr;
23 // second argument is the origin of the macro, if user-defined
24 type syntax_expander = {expander: syntax_expander_, span: Option<span>};
25
26 type macro_def = {name: ~str, ext: syntax_extension};
27
28 // macro_definer is obsolete, remove when #old_macros go away.
29 type macro_definer =
30     fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> macro_def;
31
32 type item_decorator =
33     fn@(ext_ctxt, span, ast::meta_item, ~[@ast::item]) -> ~[@ast::item];
34
35 type syntax_expander_tt = {expander: syntax_expander_tt_, span: Option<span>};
36 type syntax_expander_tt_ = fn@(ext_ctxt, span, ~[ast::token_tree])
37     -> mac_result;
38
39 type syntax_expander_tt_item
40     = {expander: syntax_expander_tt_item_, span: Option<span>};
41 type syntax_expander_tt_item_
42     = fn@(ext_ctxt, span, ast::ident, ~[ast::token_tree]) -> mac_result;
43
44 enum mac_result {
45     mr_expr(@ast::expr),
46     mr_item(@ast::item),
47     mr_def(macro_def)
48 }
49
50 enum syntax_extension {
51
52     // normal() is obsolete, remove when #old_macros go away.
53     normal(syntax_expander),
54
55     // macro_defining() is obsolete, remove when #old_macros go away.
56     macro_defining(macro_definer),
57
58     // #[auto_serialize] and such. will probably survive death of #old_macros
59     item_decorator(item_decorator),
60
61     // Token-tree expanders
62     expr_tt(syntax_expander_tt),
63     item_tt(syntax_expander_tt_item),
64 }
65
66 // A temporary hard-coded map of methods for expanding syntax extension
67 // AST nodes into full ASTs
68 fn syntax_expander_table() -> HashMap<~str, syntax_extension> {
69     fn builtin(f: syntax_expander_) -> syntax_extension
70         {normal({expander: f, span: None})}
71     fn builtin_expr_tt(f: syntax_expander_tt_) -> syntax_extension {
72         expr_tt({expander: f, span: None})
73     }
74     fn builtin_item_tt(f: syntax_expander_tt_item_) -> syntax_extension {
75         item_tt({expander: f, span: None})
76     }
77     let syntax_expanders = HashMap();
78     syntax_expanders.insert(~"macro",
79                             macro_defining(ext::simplext::add_new_extension));
80     syntax_expanders.insert(~"macro_rules",
81                             builtin_item_tt(
82                                 ext::tt::macro_rules::add_new_extension));
83     syntax_expanders.insert(~"fmt", builtin(ext::fmt::expand_syntax_ext));
84     syntax_expanders.insert(
85         ~"auto_serialize",
86         item_decorator(ext::auto_serialize::expand_auto_serialize));
87     syntax_expanders.insert(
88         ~"auto_deserialize",
89         item_decorator(ext::auto_serialize::expand_auto_deserialize));
90     syntax_expanders.insert(~"env", builtin(ext::env::expand_syntax_ext));
91     syntax_expanders.insert(~"concat_idents",
92                             builtin(ext::concat_idents::expand_syntax_ext));
93     syntax_expanders.insert(~"ident_to_str",
94                             builtin(ext::ident_to_str::expand_syntax_ext));
95     syntax_expanders.insert(~"log_syntax",
96                             builtin_expr_tt(
97                                 ext::log_syntax::expand_syntax_ext));
98     syntax_expanders.insert(~"ast",
99                             builtin(ext::qquote::expand_ast));
100
101     // Quasi-quoting expanders
102     syntax_expanders.insert(~"quote_tokens",
103                             builtin_expr_tt(ext::quote::expand_quote_tokens));
104     syntax_expanders.insert(~"quote_expr",
105                             builtin_expr_tt(ext::quote::expand_quote_expr));
106     syntax_expanders.insert(~"quote_type",
107                             builtin_expr_tt(ext::quote::expand_quote_type));
108     syntax_expanders.insert(~"quote_item",
109                             builtin_expr_tt(ext::quote::expand_quote_item));
110     syntax_expanders.insert(~"quote_pat",
111                             builtin_expr_tt(ext::quote::expand_quote_pat));
112     syntax_expanders.insert(~"quote_stmt",
113                             builtin_expr_tt(ext::quote::expand_quote_stmt));
114
115     syntax_expanders.insert(~"line",
116                             builtin(ext::source_util::expand_line));
117     syntax_expanders.insert(~"col",
118                             builtin(ext::source_util::expand_col));
119     syntax_expanders.insert(~"file",
120                             builtin(ext::source_util::expand_file));
121     syntax_expanders.insert(~"stringify",
122                             builtin(ext::source_util::expand_stringify));
123     syntax_expanders.insert(~"include",
124                             builtin(ext::source_util::expand_include));
125     syntax_expanders.insert(~"include_str",
126                             builtin(ext::source_util::expand_include_str));
127     syntax_expanders.insert(~"include_bin",
128                             builtin(ext::source_util::expand_include_bin));
129     syntax_expanders.insert(~"module_path",
130                             builtin(ext::source_util::expand_mod));
131     syntax_expanders.insert(~"proto",
132                             builtin_item_tt(ext::pipes::expand_proto));
133     syntax_expanders.insert(
134         ~"trace_macros",
135         builtin_expr_tt(ext::trace_macros::expand_trace_macros));
136     return syntax_expanders;
137 }
138
139 // One of these is made during expansion and incrementally updated as we go;
140 // when a macro expansion occurs, the resulting nodes have the backtrace()
141 // -> expn_info of their expansion context stored into their span.
142 trait ext_ctxt {
143     fn codemap() -> @CodeMap;
144     fn parse_sess() -> parse::parse_sess;
145     fn cfg() -> ast::crate_cfg;
146     fn print_backtrace();
147     fn backtrace() -> Option<@ExpnInfo>;
148     fn mod_push(mod_name: ast::ident);
149     fn mod_pop();
150     fn mod_path() -> ~[ast::ident];
151     fn bt_push(ei: codemap::ExpnInfo);
152     fn bt_pop();
153     fn span_fatal(sp: span, msg: &str) -> !;
154     fn span_err(sp: span, msg: &str);
155     fn span_warn(sp: span, msg: &str);
156     fn span_unimpl(sp: span, msg: &str) -> !;
157     fn span_bug(sp: span, msg: &str) -> !;
158     fn bug(msg: &str) -> !;
159     fn next_id() -> ast::node_id;
160     pure fn trace_macros() -> bool;
161     fn set_trace_macros(x: bool);
162     /* for unhygienic identifier transformation */
163     fn str_of(id: ast::ident) -> ~str;
164     fn ident_of(st: ~str) -> ast::ident;
165 }
166
167 fn mk_ctxt(parse_sess: parse::parse_sess,
168            cfg: ast::crate_cfg) -> ext_ctxt {
169     type ctxt_repr = {parse_sess: parse::parse_sess,
170                       cfg: ast::crate_cfg,
171                       mut backtrace: Option<@ExpnInfo>,
172                       mut mod_path: ~[ast::ident],
173                       mut trace_mac: bool};
174     impl ctxt_repr: ext_ctxt {
175         fn codemap() -> @CodeMap { self.parse_sess.cm }
176         fn parse_sess() -> parse::parse_sess { self.parse_sess }
177         fn cfg() -> ast::crate_cfg { self.cfg }
178         fn print_backtrace() { }
179         fn backtrace() -> Option<@ExpnInfo> { self.backtrace }
180         fn mod_push(i: ast::ident) { self.mod_path.push(i); }
181         fn mod_pop() { self.mod_path.pop(); }
182         fn mod_path() -> ~[ast::ident] { return self.mod_path; }
183         fn bt_push(ei: codemap::ExpnInfo) {
184             match ei {
185               ExpandedFrom({call_site: cs, callie: callie}) => {
186                 self.backtrace =
187                     Some(@ExpandedFrom({
188                         call_site: span {lo: cs.lo, hi: cs.hi,
189                                          expn_info: self.backtrace},
190                         callie: callie}));
191               }
192             }
193         }
194         fn bt_pop() {
195             match self.backtrace {
196               Some(@ExpandedFrom({
197                   call_site: span {expn_info: prev, _}, _
198               })) => {
199                 self.backtrace = prev
200               }
201               _ => self.bug(~"tried to pop without a push")
202             }
203         }
204         fn span_fatal(sp: span, msg: &str) -> ! {
205             self.print_backtrace();
206             self.parse_sess.span_diagnostic.span_fatal(sp, msg);
207         }
208         fn span_err(sp: span, msg: &str) {
209             self.print_backtrace();
210             self.parse_sess.span_diagnostic.span_err(sp, msg);
211         }
212         fn span_warn(sp: span, msg: &str) {
213             self.print_backtrace();
214             self.parse_sess.span_diagnostic.span_warn(sp, msg);
215         }
216         fn span_unimpl(sp: span, msg: &str) -> ! {
217             self.print_backtrace();
218             self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
219         }
220         fn span_bug(sp: span, msg: &str) -> ! {
221             self.print_backtrace();
222             self.parse_sess.span_diagnostic.span_bug(sp, msg);
223         }
224         fn bug(msg: &str) -> ! {
225             self.print_backtrace();
226             self.parse_sess.span_diagnostic.handler().bug(msg);
227         }
228         fn next_id() -> ast::node_id {
229             return parse::next_node_id(self.parse_sess);
230         }
231         pure fn trace_macros() -> bool {
232             self.trace_mac
233         }
234         fn set_trace_macros(x: bool) {
235             self.trace_mac = x
236         }
237
238         fn str_of(id: ast::ident) -> ~str {
239             *self.parse_sess.interner.get(id)
240         }
241         fn ident_of(st: ~str) -> ast::ident {
242             self.parse_sess.interner.intern(@st)
243         }
244     }
245     let imp: ctxt_repr = {
246         parse_sess: parse_sess,
247         cfg: cfg,
248         mut backtrace: None,
249         mut mod_path: ~[],
250         mut trace_mac: false
251     };
252     move ((move imp) as ext_ctxt)
253 }
254
255 fn expr_to_str(cx: ext_ctxt, expr: @ast::expr, err_msg: ~str) -> ~str {
256     match expr.node {
257       ast::expr_lit(l) => match l.node {
258         ast::lit_str(s) => return *s,
259         _ => cx.span_fatal(l.span, err_msg)
260       },
261       _ => cx.span_fatal(expr.span, err_msg)
262     }
263 }
264
265 fn expr_to_ident(cx: ext_ctxt,
266                  expr: @ast::expr,
267                  err_msg: ~str) -> ast::ident {
268     match expr.node {
269       ast::expr_path(p) => {
270         if vec::len(p.types) > 0u || vec::len(p.idents) != 1u {
271             cx.span_fatal(expr.span, err_msg);
272         }
273         return p.idents[0];
274       }
275       _ => cx.span_fatal(expr.span, err_msg)
276     }
277 }
278
279 fn get_mac_args_no_max(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
280                        min: uint, name: ~str) -> ~[@ast::expr] {
281     return get_mac_args(cx, sp, arg, min, None, name);
282 }
283
284 fn get_mac_args(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
285                 min: uint, max: Option<uint>, name: ~str) -> ~[@ast::expr] {
286     match arg {
287       Some(expr) => match expr.node {
288         ast::expr_vec(elts, _) => {
289             let elts_len = vec::len(elts);
290               match max {
291                 Some(max) if ! (min <= elts_len && elts_len <= max) => {
292                   cx.span_fatal(sp,
293                                 fmt!("%s! takes between %u and %u arguments.",
294                                      name, min, max));
295                 }
296                 None if ! (min <= elts_len) => {
297                   cx.span_fatal(sp, fmt!("%s! needs at least %u arguments.",
298                                          name, min));
299                 }
300                 _ => return elts /* we are good */
301               }
302           }
303         _ => {
304             cx.span_fatal(sp, fmt!("%s!: malformed invocation", name))
305         }
306       },
307       None => cx.span_fatal(sp, fmt!("%s!: missing arguments", name))
308     }
309 }
310
311 fn get_mac_body(cx: ext_ctxt, sp: span, args: ast::mac_body)
312     -> ast::mac_body_
313 {
314     match (args) {
315       Some(body) => body,
316       None => cx.span_fatal(sp, ~"missing macro body")
317     }
318 }
319
320 // Massage syntactic form of new-style arguments to internal representation
321 // of old-style macro args, such that old-style macro can be run and invoked
322 // using new syntax. This will be obsolete when #old_macros go away.
323 fn tt_args_to_original_flavor(cx: ext_ctxt, sp: span, arg: ~[ast::token_tree])
324     -> ast::mac_arg {
325     use ast::{matcher, matcher_, match_tok, match_seq, match_nonterminal};
326     use parse::lexer::{new_tt_reader, reader};
327     use tt::macro_parser::{parse_or_else, matched_seq,
328                               matched_nonterminal};
329
330     // these spans won't matter, anyways
331     fn ms(m: matcher_) -> matcher {
332         {node: m, span: dummy_sp()}
333     }
334     let arg_nm = cx.parse_sess().interner.gensym(@~"arg");
335
336     let argument_gram = ~[ms(match_seq(~[
337         ms(match_nonterminal(arg_nm, parse::token::special_idents::expr, 0u))
338     ], Some(parse::token::COMMA), true, 0u, 1u))];
339
340     let arg_reader = new_tt_reader(cx.parse_sess().span_diagnostic,
341                                    cx.parse_sess().interner, None, arg);
342     let args =
343         match parse_or_else(cx.parse_sess(), cx.cfg(), arg_reader as reader,
344                           argument_gram).get(arg_nm) {
345           @matched_seq(s, _) => {
346             do s.map() |lf| {
347                 match *lf {
348                   @matched_nonterminal(parse::token::nt_expr(arg)) =>
349                     arg, /* whew! list of exprs, here we come! */
350                   _ => fail ~"badly-structured parse result"
351                 }
352             }
353           },
354           _ => fail ~"badly-structured parse result"
355         };
356
357     return Some(@{id: parse::next_node_id(cx.parse_sess()),
358                callee_id: parse::next_node_id(cx.parse_sess()),
359                node: ast::expr_vec(args, ast::m_imm), span: sp});
360 }
361
362 //
363 // Local Variables:
364 // mode: rust
365 // fill-column: 78;
366 // indent-tabs-mode: nil
367 // c-basic-offset: 4
368 // buffer-file-coding-system: utf-8-unix
369 // End:
370 //