]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/ext/base.rs
Imported Upstream version 0.7
[rustc.git] / src / libsyntax / ext / base.rs
CommitLineData
223e47cc
LB
1// Copyright 2012-2013 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
223e47cc 11use ast;
970d7e83 12use ast::Name;
223e47cc
LB
13use codemap;
14use codemap::{CodeMap, span, ExpnInfo, ExpandedFrom};
15use codemap::CallInfo;
16use diagnostic::span_handler;
17use ext;
18use parse;
19use parse::token;
970d7e83 20use parse::token::{ident_to_str, intern, str_to_ident};
223e47cc 21
970d7e83
LB
22use std::vec;
23use std::hashmap::HashMap;
223e47cc
LB
24
25// new-style macro! tt code:
26//
27// SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
28// NormalTT, IdentTT
29//
30// also note that ast::mac used to have a bunch of extraneous cases and
31// is now probably a redundant AST node, can be merged with
32// ast::mac_invoc_tt.
33
34pub struct MacroDef {
970d7e83 35 name: @str,
223e47cc
LB
36 ext: SyntaxExtension
37}
38
970d7e83 39pub type ItemDecorator = @fn(@ExtCtxt,
223e47cc
LB
40 span,
41 @ast::meta_item,
42 ~[@ast::item])
43 -> ~[@ast::item];
44
45pub struct SyntaxExpanderTT {
46 expander: SyntaxExpanderTTFun,
47 span: Option<span>
48}
49
970d7e83 50pub type SyntaxExpanderTTFun = @fn(@ExtCtxt,
223e47cc
LB
51 span,
52 &[ast::token_tree])
53 -> MacResult;
54
55pub struct SyntaxExpanderTTItem {
56 expander: SyntaxExpanderTTItemFun,
57 span: Option<span>
58}
59
970d7e83 60pub type SyntaxExpanderTTItemFun = @fn(@ExtCtxt,
223e47cc
LB
61 span,
62 ast::ident,
63 ~[ast::token_tree])
64 -> MacResult;
65
66pub enum MacResult {
67 MRExpr(@ast::expr),
68 MRItem(@ast::item),
69 MRAny(@fn() -> @ast::expr,
70 @fn() -> Option<@ast::item>,
71 @fn() -> @ast::stmt),
72 MRDef(MacroDef)
73}
74
75pub enum SyntaxExtension {
76
77 // #[auto_encode] and such
78 ItemDecorator(ItemDecorator),
79
80 // Token-tree expanders
81 NormalTT(SyntaxExpanderTT),
82
83 // An IdentTT is a macro that has an
84 // identifier in between the name of the
85 // macro and the argument. Currently,
86 // the only examples of this are
87 // macro_rules! and proto!
88
89 // perhaps macro_rules! will lose its odd special identifier argument,
90 // and this can go away also
91 IdentTT(SyntaxExpanderTTItem),
92}
93
970d7e83
LB
94// The SyntaxEnv is the environment that's threaded through the expansion
95// of macros. It contains bindings for macros, and also a special binding
96// for " block" (not a legal identifier) that maps to a BlockInfo
223e47cc
LB
97pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
98
223e47cc
LB
99// Transformer : the codomain of SyntaxEnvs
100
223e47cc
LB
101pub enum Transformer {
102 // this identifier maps to a syntax extension or macro
103 SE(SyntaxExtension),
970d7e83
LB
104 // blockinfo : this is ... well, it's simpler than threading
105 // another whole data stack-structured data structure through
106 // expansion. Basically, there's an invariant that every
107 // map must contain a binding for " block".
108 BlockInfo(BlockInfo)
223e47cc
LB
109}
110
970d7e83
LB
111pub struct BlockInfo {
112 // should macros escape from this scope?
113 macros_escape : bool,
114 // what are the pending renames?
115 pending_renames : @mut RenameList
116}
117
118// a list of ident->name renamings
119type RenameList = ~[(ast::ident,Name)];
120
223e47cc
LB
121// The base map of methods for expanding syntax extension
122// AST nodes into full ASTs
123pub fn syntax_expander_table() -> SyntaxEnv {
124 // utility function to simplify creating NormalTT syntax extensions
125 fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
126 @SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
127 }
128 // utility function to simplify creating IdentTT syntax extensions
129 fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
130 @SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
131 }
970d7e83 132 let mut syntax_expanders = HashMap::new();
223e47cc 133 // NB identifier starts with space, and can't conflict with legal idents
970d7e83
LB
134 syntax_expanders.insert(intern(&" block"),
135 @BlockInfo(BlockInfo{
136 macros_escape : false,
137 pending_renames : @mut ~[]
138 }));
139 syntax_expanders.insert(intern(&"macro_rules"),
223e47cc
LB
140 builtin_item_tt(
141 ext::tt::macro_rules::add_new_extension));
970d7e83 142 syntax_expanders.insert(intern(&"fmt"),
223e47cc
LB
143 builtin_normal_tt(ext::fmt::expand_syntax_ext));
144 syntax_expanders.insert(
970d7e83 145 intern(&"auto_encode"),
223e47cc
LB
146 @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
147 syntax_expanders.insert(
970d7e83 148 intern(&"auto_decode"),
223e47cc 149 @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
970d7e83 150 syntax_expanders.insert(intern(&"env"),
223e47cc 151 builtin_normal_tt(ext::env::expand_syntax_ext));
970d7e83
LB
152 syntax_expanders.insert(intern("bytes"),
153 builtin_normal_tt(ext::bytes::expand_syntax_ext));
154 syntax_expanders.insert(intern("concat_idents"),
223e47cc
LB
155 builtin_normal_tt(
156 ext::concat_idents::expand_syntax_ext));
970d7e83 157 syntax_expanders.insert(intern(&"log_syntax"),
223e47cc
LB
158 builtin_normal_tt(
159 ext::log_syntax::expand_syntax_ext));
970d7e83 160 syntax_expanders.insert(intern(&"deriving"),
223e47cc
LB
161 @SE(ItemDecorator(
162 ext::deriving::expand_meta_deriving)));
223e47cc
LB
163
164 // Quasi-quoting expanders
970d7e83 165 syntax_expanders.insert(intern(&"quote_tokens"),
223e47cc 166 builtin_normal_tt(ext::quote::expand_quote_tokens));
970d7e83 167 syntax_expanders.insert(intern(&"quote_expr"),
223e47cc 168 builtin_normal_tt(ext::quote::expand_quote_expr));
970d7e83 169 syntax_expanders.insert(intern(&"quote_ty"),
223e47cc 170 builtin_normal_tt(ext::quote::expand_quote_ty));
970d7e83 171 syntax_expanders.insert(intern(&"quote_item"),
223e47cc 172 builtin_normal_tt(ext::quote::expand_quote_item));
970d7e83 173 syntax_expanders.insert(intern(&"quote_pat"),
223e47cc 174 builtin_normal_tt(ext::quote::expand_quote_pat));
970d7e83 175 syntax_expanders.insert(intern(&"quote_stmt"),
223e47cc
LB
176 builtin_normal_tt(ext::quote::expand_quote_stmt));
177
970d7e83 178 syntax_expanders.insert(intern(&"line"),
223e47cc
LB
179 builtin_normal_tt(
180 ext::source_util::expand_line));
970d7e83 181 syntax_expanders.insert(intern(&"col"),
223e47cc
LB
182 builtin_normal_tt(
183 ext::source_util::expand_col));
970d7e83 184 syntax_expanders.insert(intern(&"file"),
223e47cc
LB
185 builtin_normal_tt(
186 ext::source_util::expand_file));
970d7e83 187 syntax_expanders.insert(intern(&"stringify"),
223e47cc
LB
188 builtin_normal_tt(
189 ext::source_util::expand_stringify));
970d7e83 190 syntax_expanders.insert(intern(&"include"),
223e47cc
LB
191 builtin_normal_tt(
192 ext::source_util::expand_include));
970d7e83 193 syntax_expanders.insert(intern(&"include_str"),
223e47cc
LB
194 builtin_normal_tt(
195 ext::source_util::expand_include_str));
970d7e83 196 syntax_expanders.insert(intern(&"include_bin"),
223e47cc
LB
197 builtin_normal_tt(
198 ext::source_util::expand_include_bin));
970d7e83 199 syntax_expanders.insert(intern(&"module_path"),
223e47cc
LB
200 builtin_normal_tt(
201 ext::source_util::expand_mod));
970d7e83 202 syntax_expanders.insert(intern(&"proto"),
223e47cc 203 builtin_item_tt(ext::pipes::expand_proto));
970d7e83 204 syntax_expanders.insert(intern(&"asm"),
223e47cc
LB
205 builtin_normal_tt(ext::asm::expand_asm));
206 syntax_expanders.insert(
970d7e83 207 intern(&"trace_macros"),
223e47cc
LB
208 builtin_normal_tt(ext::trace_macros::expand_trace_macros));
209 MapChain::new(~syntax_expanders)
210}
211
212// One of these is made during expansion and incrementally updated as we go;
213// when a macro expansion occurs, the resulting nodes have the backtrace()
214// -> expn_info of their expansion context stored into their span.
970d7e83
LB
215pub struct ExtCtxt {
216 parse_sess: @mut parse::ParseSess,
217 cfg: ast::crate_cfg,
218 backtrace: @mut Option<@ExpnInfo>,
219
220 // These two @mut's should really not be here,
221 // but the self types for CtxtRepr are all wrong
222 // and there are bugs in the code for object
223 // types that make this hard to get right at the
224 // moment. - nmatsakis
225 mod_path: @mut ~[ast::ident],
226 trace_mac: @mut bool
223e47cc
LB
227}
228
970d7e83
LB
229impl ExtCtxt {
230 pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
231 -> @ExtCtxt {
232 @ExtCtxt {
233 parse_sess: parse_sess,
234 cfg: cfg,
235 backtrace: @mut None,
236 mod_path: @mut ~[],
237 trace_mac: @mut false
238 }
239 }
240
241 pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
242 pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
243 pub fn cfg(&self) -> ast::crate_cfg { copy self.cfg }
244 pub fn call_site(&self) -> span {
245 match *self.backtrace {
246 Some(@ExpandedFrom(CallInfo {call_site: cs, _})) => cs,
247 None => self.bug("missing top span")
223e47cc 248 }
970d7e83
LB
249 }
250 pub fn print_backtrace(&self) { }
251 pub fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
252 pub fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); }
253 pub fn mod_pop(&self) { self.mod_path.pop(); }
254 pub fn mod_path(&self) -> ~[ast::ident] { copy *self.mod_path }
255 pub fn bt_push(&self, ei: codemap::ExpnInfo) {
256 match ei {
257 ExpandedFrom(CallInfo {call_site: cs, callee: ref callee}) => {
223e47cc
LB
258 *self.backtrace =
259 Some(@ExpandedFrom(CallInfo {
260 call_site: span {lo: cs.lo, hi: cs.hi,
261 expn_info: *self.backtrace},
262 callee: copy *callee}));
223e47cc
LB
263 }
264 }
970d7e83
LB
265 }
266 pub fn bt_pop(&self) {
267 match *self.backtrace {
268 Some(@ExpandedFrom(
269 CallInfo {
270 call_site: span {expn_info: prev, _}, _
271 })) => {
223e47cc 272 *self.backtrace = prev
223e47cc 273 }
970d7e83 274 _ => self.bug("tried to pop without a push")
223e47cc 275 }
223e47cc 276 }
970d7e83
LB
277 pub fn span_fatal(&self, sp: span, msg: &str) -> ! {
278 self.print_backtrace();
279 self.parse_sess.span_diagnostic.span_fatal(sp, msg);
280 }
281 pub fn span_err(&self, sp: span, msg: &str) {
282 self.print_backtrace();
283 self.parse_sess.span_diagnostic.span_err(sp, msg);
284 }
285 pub fn span_warn(&self, sp: span, msg: &str) {
286 self.print_backtrace();
287 self.parse_sess.span_diagnostic.span_warn(sp, msg);
288 }
289 pub fn span_unimpl(&self, sp: span, msg: &str) -> ! {
290 self.print_backtrace();
291 self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
292 }
293 pub fn span_bug(&self, sp: span, msg: &str) -> ! {
294 self.print_backtrace();
295 self.parse_sess.span_diagnostic.span_bug(sp, msg);
296 }
297 pub fn bug(&self, msg: &str) -> ! {
298 self.print_backtrace();
299 self.parse_sess.span_diagnostic.handler().bug(msg);
300 }
301 pub fn next_id(&self) -> ast::node_id {
302 parse::next_node_id(self.parse_sess)
303 }
304 pub fn trace_macros(&self) -> bool {
305 *self.trace_mac
306 }
307 pub fn set_trace_macros(&self, x: bool) {
308 *self.trace_mac = x
309 }
310 pub fn str_of(&self, id: ast::ident) -> @str {
311 ident_to_str(&id)
312 }
313 pub fn ident_of(&self, st: &str) -> ast::ident {
314 str_to_ident(st)
315 }
223e47cc
LB
316}
317
970d7e83 318pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str {
223e47cc
LB
319 match expr.node {
320 ast::expr_lit(l) => match l.node {
970d7e83 321 ast::lit_str(s) => s,
223e47cc
LB
322 _ => cx.span_fatal(l.span, err_msg)
323 },
324 _ => cx.span_fatal(expr.span, err_msg)
325 }
326}
327
970d7e83 328pub fn expr_to_ident(cx: @ExtCtxt,
223e47cc 329 expr: @ast::expr,
970d7e83 330 err_msg: &str) -> ast::ident {
223e47cc
LB
331 match expr.node {
332 ast::expr_path(p) => {
970d7e83 333 if p.types.len() > 0u || p.idents.len() != 1u {
223e47cc
LB
334 cx.span_fatal(expr.span, err_msg);
335 }
336 return p.idents[0];
337 }
338 _ => cx.span_fatal(expr.span, err_msg)
339 }
340}
341
970d7e83 342pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
223e47cc
LB
343 name: &str) {
344 if tts.len() != 0 {
345 cx.span_fatal(sp, fmt!("%s takes no arguments", name));
346 }
347}
348
970d7e83 349pub fn get_single_str_from_tts(cx: @ExtCtxt,
223e47cc
LB
350 sp: span,
351 tts: &[ast::token_tree],
970d7e83 352 name: &str) -> @str {
223e47cc
LB
353 if tts.len() != 1 {
354 cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
355 }
356
357 match tts[0] {
358 ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident),
359 _ =>
360 cx.span_fatal(sp, fmt!("%s requires a string.", name))
361 }
362}
363
970d7e83 364pub fn get_exprs_from_tts(cx: @ExtCtxt, tts: &[ast::token_tree])
223e47cc
LB
365 -> ~[@ast::expr] {
366 let p = parse::new_parser_from_tts(cx.parse_sess(),
367 cx.cfg(),
970d7e83 368 vec::to_owned(tts));
223e47cc
LB
369 let mut es = ~[];
370 while *p.token != token::EOF {
371 if es.len() != 0 {
372 p.eat(&token::COMMA);
373 }
374 es.push(p.parse_expr());
375 }
376 es
377}
378
379// in order to have some notion of scoping for macros,
380// we want to implement the notion of a transformation
381// environment.
382
383// This environment maps Names to Transformers.
384// Initially, this includes macro definitions and
385// block directives.
386
387
388
389// Actually, the following implementation is parameterized
390// by both key and value types.
391
392//impl question: how to implement it? Initially, the
393// env will contain only macros, so it might be painful
394// to add an empty frame for every context. Let's just
395// get it working, first....
396
397// NB! the mutability of the underlying maps means that
398// if expansion is out-of-order, a deeper scope may be
399// able to refer to a macro that was added to an enclosing
400// scope lexically later than the deeper scope.
401
402// Note on choice of representation: I've been pushed to
403// use a top-level managed pointer by some difficulties
404// with pushing and popping functionally, and the ownership
405// issues. As a result, the values returned by the table
406// also need to be managed; the &'self ... type that Maps
407// return won't work for things that need to get outside
408// of that managed pointer. The easiest way to do this
409// is just to insist that the values in the tables are
410// managed to begin with.
411
412// a transformer env is either a base map or a map on top
413// of another chain.
414pub enum MapChain<K,V> {
970d7e83
LB
415 BaseMapChain(~HashMap<K,@V>),
416 ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
223e47cc
LB
417}
418
419
420// get the map from an env frame
421impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
422
423 // Constructor. I don't think we need a zero-arg one.
970d7e83 424 fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
223e47cc
LB
425 @mut BaseMapChain(init)
426 }
427
428 // add a new frame to the environment (functionally)
429 fn push_frame (@mut self) -> @mut MapChain<K,V> {
970d7e83 430 @mut ConsMapChain(~HashMap::new() ,self)
223e47cc
LB
431 }
432
433// no need for pop, it'll just be functional.
434
435 // utility fn...
436
437 // ugh: can't get this to compile with mut because of the
438 // lack of flow sensitivity.
970d7e83 439 fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
223e47cc
LB
440 match *self {
441 BaseMapChain (~ref map) => map,
442 ConsMapChain (~ref map,_) => map
443 }
444 }
445
446// traits just don't work anywhere...?
970d7e83 447//impl Map<Name,SyntaxExtension> for MapChain {
223e47cc
LB
448
449 fn contains_key (&self, key: &K) -> bool {
450 match *self {
451 BaseMapChain (ref map) => map.contains_key(key),
452 ConsMapChain (ref map,ref rest) =>
453 (map.contains_key(key)
454 || rest.contains_key(key))
455 }
456 }
457 // should each_key and each_value operate on shadowed
458 // names? I think not.
459 // delaying implementing this....
460 fn each_key (&self, _f: &fn (&K)->bool) {
970d7e83 461 fail!("unimplemented 2013-02-15T10:01");
223e47cc
LB
462 }
463
464 fn each_value (&self, _f: &fn (&V) -> bool) {
970d7e83 465 fail!("unimplemented 2013-02-15T10:02");
223e47cc
LB
466 }
467
468 // Returns a copy of the value that the name maps to.
469 // Goes down the chain 'til it finds one (or bottom out).
470 fn find (&self, key: &K) -> Option<@V> {
471 match self.get_map().find (key) {
472 Some(ref v) => Some(**v),
473 None => match *self {
474 BaseMapChain (_) => None,
475 ConsMapChain (_,ref rest) => rest.find(key)
476 }
477 }
478 }
479
970d7e83
LB
480 fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
481 let map = match *self {
482 BaseMapChain(ref map) => map,
483 ConsMapChain(ref map,_) => map
484 };
485 // strip one layer of indirection off the pointer.
486 map.find(key).map(|r| {**r})
487 }
488
223e47cc 489 // insert the binding into the top-level map
970d7e83 490 fn insert (&mut self, key: K, ext: @V) -> bool {
223e47cc
LB
491 // can't abstract over get_map because of flow sensitivity...
492 match *self {
493 BaseMapChain (~ref mut map) => map.insert(key, ext),
494 ConsMapChain (~ref mut map,_) => map.insert(key,ext)
495 }
496 }
970d7e83
LB
497 // insert the binding into the topmost frame for which the binding
498 // associated with 'n' exists and satisfies pred
499 // ... there are definitely some opportunities for abstraction
500 // here that I'm ignoring. (e.g., manufacturing a predicate on
501 // the maps in the chain, and using an abstract "find".
502 fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) {
503 match *self {
504 BaseMapChain (~ref mut map) => {
505 if satisfies_pred(map,&n,pred) {
506 map.insert(key,ext);
507 } else {
508 fail!(~"expected map chain containing satisfying frame")
509 }
510 },
511 ConsMapChain (~ref mut map, rest) => {
512 if satisfies_pred(map,&n,|v|pred(v)) {
513 map.insert(key,ext);
514 } else {
515 rest.insert_into_frame(key,ext,n,pred)
516 }
517 }
518 }
519 }
520}
223e47cc 521
970d7e83
LB
522// returns true if the binding for 'n' satisfies 'pred' in 'map'
523fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
524 n: &K,
525 pred: &fn(&V)->bool)
526 -> bool {
527 match map.find(n) {
528 Some(ref v) => (pred(*v)),
529 None => false
530 }
223e47cc
LB
531}
532
533#[cfg(test)]
534mod test {
535 use super::MapChain;
970d7e83 536 use std::hashmap::HashMap;
223e47cc
LB
537
538 #[test] fn testenv () {
970d7e83
LB
539 let mut a = HashMap::new();
540 a.insert (@"abc",@15);
223e47cc 541 let m = MapChain::new(~a);
970d7e83
LB
542 m.insert (@"def",@16);
543 // FIXME: #4492 (ICE) assert_eq!(m.find(&@"abc"),Some(@15));
544 // .... assert_eq!(m.find(&@"def"),Some(@16));
545 assert_eq!(*(m.find(&@"abc").get()),15);
546 assert_eq!(*(m.find(&@"def").get()),16);
223e47cc
LB
547 let n = m.push_frame();
548 // old bindings are still present:
970d7e83
LB
549 assert_eq!(*(n.find(&@"abc").get()),15);
550 assert_eq!(*(n.find(&@"def").get()),16);
551 n.insert (@"def",@17);
223e47cc 552 // n shows the new binding
970d7e83
LB
553 assert_eq!(*(n.find(&@"abc").get()),15);
554 assert_eq!(*(n.find(&@"def").get()),17);
223e47cc 555 // ... but m still has the old ones
970d7e83
LB
556 // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15));
557 // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16));
558 assert_eq!(*(m.find(&@"abc").get()),15);
559 assert_eq!(*(m.find(&@"def").get()),16);
223e47cc
LB
560 }
561}