1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
import std::map::hashmap;
import ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt,
tt_delim, tt_tok, item_mac};
import fold::*;
import ext::base::*;
import ext::qquote::{qq_helper};
import parse::{parser, parse_expr_from_source_str, new_parser_from_tt};
import codemap::{span, expanded_from};
fn expand_expr(exts: hashmap<~str, syntax_extension>, cx: ext_ctxt,
e: expr_, s: span, fld: ast_fold,
orig: fn@(expr_, span, ast_fold) -> (expr_, span))
-> (expr_, span)
{
return match e {
// expr_mac should really be expr_ext or something; it's the
// entry-point for all syntax extensions.
expr_mac(mac) => {
// Old-style macros, for compatibility, will erase this whole
// block once we've transitioned.
match mac.node {
mac_invoc(pth, args, body) => {
assert (vec::len(pth.idents) > 0u);
/* using idents and token::special_idents would make the
the macro names be hygienic */
let extname = cx.parse_sess().interner.get(pth.idents[0]);
match exts.find(*extname) {
none => {
cx.span_fatal(pth.span,
fmt!{"macro undefined: '%s'", *extname})
}
some(item_decorator(_)) => {
cx.span_fatal(
pth.span,
fmt!{"%s can only be used as a decorator", *extname});
}
some(normal({expander: exp, span: exp_sp})) => {
let expanded = exp(cx, mac.span, args, body);
cx.bt_push(expanded_from({call_site: s,
callie: {name: *extname, span: exp_sp}}));
//keep going, outside-in
let fully_expanded = fld.fold_expr(expanded).node;
cx.bt_pop();
(fully_expanded, s)
}
some(macro_defining(ext)) => {
let named_extension = ext(cx, mac.span, args, body);
exts.insert(named_extension.name, named_extension.ext);
(ast::expr_rec(~[], none), s)
}
some(expr_tt(_)) => {
cx.span_fatal(pth.span,
fmt!{"this tt-style macro should be \
invoked '%s!{...}'", *extname})
}
some(item_tt(*)) => {
cx.span_fatal(pth.span,
~"cannot use item macros in this context");
}
}
}
// Token-tree macros, these will be the only case when we're
// finished transitioning.
mac_invoc_tt(pth, tts) => {
assert (vec::len(pth.idents) == 1u);
/* using idents and token::special_idents would make the
the macro names be hygienic */
let extname = cx.parse_sess().interner.get(pth.idents[0]);
match exts.find(*extname) {
none => {
cx.span_fatal(pth.span,
fmt!{"macro undefined: '%s'", *extname})
}
some(expr_tt({expander: exp, span: exp_sp})) => {
let expanded = match exp(cx, mac.span, tts) {
mr_expr(e) => e,
_ => cx.span_fatal(
pth.span, fmt!{"non-expr macro in expr pos: %s",
*extname})
};
cx.bt_push(expanded_from({call_site: s,
callie: {name: *extname, span: exp_sp}}));
//keep going, outside-in
let fully_expanded = fld.fold_expr(expanded).node;
cx.bt_pop();
(fully_expanded, s)
}
some(normal({expander: exp, span: exp_sp})) => {
//convert the new-style invoc for the old-style macro
let arg = base::tt_args_to_original_flavor(cx, pth.span,
tts);
let expanded = exp(cx, mac.span, arg, none);
cx.bt_push(expanded_from({call_site: s,
callie: {name: *extname, span: exp_sp}}));
//keep going, outside-in
let fully_expanded = fld.fold_expr(expanded).node;
cx.bt_pop();
(fully_expanded, s)
}
_ => {
cx.span_fatal(pth.span,
fmt!{"'%s' is not a tt-style macro",
*extname})
}
}
}
_ => cx.span_bug(mac.span, ~"naked syntactic bit")
}
}
_ => orig(e, s, fld)
};
}
// This is a secondary mechanism for invoking syntax extensions on items:
// "decorator" attributes, such as #[auto_serialize]. These are invoked by an
// attribute prefixing an item, and are interpreted by feeding the item
// through the named attribute _as a syntax extension_ and splicing in the
// resulting item vec into place in favour of the decorator. Note that
// these do _not_ work for macro extensions, just item_decorator ones.
//
// NB: there is some redundancy between this and expand_item, below, and
// they might benefit from some amount of semantic and language-UI merger.
fn expand_mod_items(exts: hashmap<~str, syntax_extension>, cx: ext_ctxt,
module_: ast::_mod, fld: ast_fold,
orig: fn@(ast::_mod, ast_fold) -> ast::_mod)
-> ast::_mod
{
// Fold the contents first:
let module_ = orig(module_, fld);
// For each item, look through the attributes. If any of them are
// decorated with "item decorators", then use that function to transform
// the item into a new set of items.
let new_items = do vec::flat_map(module_.items) |item| {
do vec::foldr(item.attrs, ~[item]) |attr, items| {
let mname = match attr.node.value.node {
ast::meta_word(n) => n,
ast::meta_name_value(n, _) => n,
ast::meta_list(n, _) => n
};
match exts.find(mname) {
none | some(normal(_)) | some(macro_defining(_))
| some(expr_tt(_)) | some(item_tt(*)) => items,
some(item_decorator(dec_fn)) => {
dec_fn(cx, attr.span, attr.node.value, items)
}
}
}
};
return {items: new_items with module_};
}
// When we enter a module, record it, for the sake of `module!`
fn expand_item(exts: hashmap<~str, syntax_extension>,
cx: ext_ctxt, &&it: @ast::item, fld: ast_fold,
orig: fn@(&&@ast::item, ast_fold) -> option<@ast::item>)
-> option<@ast::item>
{
let is_mod = match it.node {
ast::item_mod(_) | ast::item_foreign_mod(_) => true,
_ => false
};
let maybe_it = match it.node {
ast::item_mac(*) => expand_item_mac(exts, cx, it, fld),
_ => some(it)
};
match maybe_it {
some(it) => {
if is_mod { cx.mod_push(it.ident); }
let ret_val = orig(it, fld);
if is_mod { cx.mod_pop(); }
return ret_val;
}
none => return none
}
}
// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
fn expand_item_mac(exts: hashmap<~str, syntax_extension>,
cx: ext_ctxt, &&it: @ast::item,
fld: ast_fold) -> option<@ast::item> {
match it.node {
item_mac({node: mac_invoc_tt(pth, tts), span}) => {
let extname = cx.parse_sess().interner.get(pth.idents[0]);
match exts.find(*extname) {
none => {
cx.span_fatal(pth.span,
fmt!{"macro undefined: '%s'", *extname})
}
some(item_tt(expand)) => {
let expanded = expand.expander(cx, it.span, it.ident, tts);
cx.bt_push(expanded_from({call_site: it.span,
callie: {name: *extname,
span: expand.span}}));
let maybe_it = match expanded {
mr_item(it) => fld.fold_item(it),
mr_expr(e) => cx.span_fatal(pth.span,
~"expr macro in item position: " +
*extname),
mr_def(mdef) => {
exts.insert(mdef.name, mdef.ext);
none
}
};
cx.bt_pop();
return maybe_it
}
_ => cx.span_fatal(it.span,
fmt!{"%s is not a legal here", *extname})
}
}
_ => cx.span_bug(it.span, ~"invalid item macro invocation")
}
}
fn new_span(cx: ext_ctxt, sp: span) -> span {
/* this discards information in the case of macro-defining macros */
return {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
}
// FIXME (#2247): this is a terrible kludge to inject some macros into
// the default compilation environment. When the macro-definition system
// is substantially more mature, these should move from here, into a
// compiled part of libcore at very least.
fn core_macros() -> ~str {
return
~"{
#macro[[#error[f, ...], log(core::error, #fmt[f, ...])]];
#macro[[#warn[f, ...], log(core::warn, #fmt[f, ...])]];
#macro[[#info[f, ...], log(core::info, #fmt[f, ...])]];
#macro[[#debug[f, ...], log(core::debug, #fmt[f, ...])]];
}";
}
fn expand_crate(parse_sess: parse::parse_sess,
cfg: ast::crate_cfg, c: @crate) -> @crate {
let exts = syntax_expander_table();
let afp = default_ast_fold();
let cx: ext_ctxt = mk_ctxt(parse_sess, cfg);
let f_pre =
@{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr),
fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod),
fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item),
new_span: |a|new_span(cx, a)
with *afp};
let f = make_fold(f_pre);
let cm = parse_expr_from_source_str(~"<core-macros>",
@core_macros(),
cfg,
parse_sess);
// This is run for its side-effects on the expander env,
// as it registers all the core macros as expanders.
f.fold_expr(cm);
let res = @f.fold_crate(*c);
return res;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
|