Skip to content

Commit 2012b5e

Browse files
committed
---
yaml --- r: 14831 b: refs/heads/try c: bdd0c93 h: refs/heads/master i: 14829: d589582 14827: 669fdca 14823: 28fe3dc 14815: adb5c0a v: v3
1 parent be6939e commit 2012b5e

File tree

7 files changed

+450
-1
lines changed

7 files changed

+450
-1
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
refs/heads/master: 61b1875c16de39c166b0f4d54bba19f9c6777d1a
33
refs/heads/snap-stage1: e33de59e47c5076a89eadeb38f4934f58a3618a6
44
refs/heads/snap-stage3: 4a81779abd786ff22d71434c6d9a5917ea4cdfff
5-
refs/heads/try: c3516f091bbf051f8f9d40ea0dda9ab8c4584e74
5+
refs/heads/try: bdd0c9387bb10e0db01c00dcd47ea3e6bc343e45
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
/*
2+
3+
The compiler code necessary to implement the #[auto_serialize]
4+
extension. The idea here is that type-defining items may be tagged
5+
with #[auto_serialize], which will cause us to generate a little
6+
companion module with the same name as the item.
7+
8+
For example, a type like:
9+
10+
type node_id = uint;
11+
12+
would generate a companion module like:
13+
14+
mod node_id {
15+
use std;
16+
import std::serialization::serializer;
17+
import std::serialization::deserializer;
18+
fn serialize<S: serializer>(s: S, v: node_id) {
19+
s.emit_uint(v);
20+
}
21+
fn deserializer<D: deserializer>(d: D) -> node_id {
22+
d.read_uint()
23+
}
24+
}
25+
26+
Other interesting scenarios are whe the item has type parameters or
27+
references other non-built-in types. A type definition like:
28+
29+
type spanned<T> = {node: T, span: span};
30+
31+
would yield a helper module like:
32+
33+
mod spanned {
34+
use std;
35+
import std::serialization::serializer;
36+
import std::serialization::deserializer;
37+
fn serialize<S: serializer,T>(s: S, t: fn(T), v: spanned<T>) {
38+
s.emit_rec(2u) {||
39+
s.emit_rec_field("node", 0u) {||
40+
t(s.node);
41+
};
42+
s.emit_rec_field("span", 1u) {||
43+
span::serialize(s, s.span);
44+
};
45+
}
46+
}
47+
fn deserializer<D: deserializer>(d: D, t: fn() -> T) -> node_id {
48+
d.read_rec(2u) {||
49+
{node: d.read_rec_field("node", 0u, t),
50+
span: d.read_rec_field("span", 1u) {||span::deserialize(d)}}
51+
}
52+
}
53+
}
54+
55+
In general, the code to serialize an instance `v` of a non-built-in
56+
type a::b::c<T0,...,Tn> looks like:
57+
58+
a::b::c::serialize(s, {|v| c_T0}, ..., {|v| c_Tn}, v)
59+
60+
where `c_Ti` is the code to serialize an instance `v` of the type
61+
`Ti`.
62+
63+
Similarly, the code to deserialize an instance of a non-built-in type
64+
`a::b::c<T0,...,Tn>` using the deserializer `d` looks like:
65+
66+
a::b::c::deserialize(d, {|| c_T0}, ..., {|| c_Tn})
67+
68+
where `c_Ti` is the code to deserialize an instance of `Ti` using the
69+
deserializer `d`.
70+
71+
TODO--Hygiene. Search for "__" strings.
72+
73+
*/
74+
import base::*;
75+
import driver::session::session;
76+
import codemap::span;
77+
import std::map;
78+
79+
export expand_auto_serialize;
80+
81+
enum ser_cx = {
82+
ext_cx: ext_ctxt,
83+
tps: map::map<str, fn@(@ast::expr) -> [@ast::stmt]>
84+
};
85+
86+
fn expand_auto_serialize(cx: ext_ctxt,
87+
span: span,
88+
mitem: ast::meta_item,
89+
in_items: [@ast::item]) -> [@ast::item] {
90+
vec::flat_map(in_items) {|in_item|
91+
alt in_item.node {
92+
ast::item_ty(ty, tps) {
93+
[in_item, ty_module(cx, in_item.ident, ty, tps)]
94+
}
95+
96+
ast::item_enum(variants, tps) {
97+
[in_item, enum_module(cx, in_item.ident, variants, tps)]
98+
}
99+
100+
_ {
101+
cx.session().span_err(span, "#[auto_serialize] can only be \
102+
applied to type and enum \
103+
definitions");
104+
[in_item]
105+
}
106+
}
107+
}
108+
}
109+
110+
impl helpers for ser_cx {
111+
fn session() -> session { self.ext_cx.session() }
112+
113+
fn next_id() -> ast::node_id { self.session().next_node_id() }
114+
115+
fn path(span: span, strs: [str]) -> @ast::path {
116+
@{node: {global: false,
117+
idents: strs + ["serialize"],
118+
types: []},
119+
span: span}
120+
}
121+
122+
fn expr(span: span, node: ast::expr_) -> @ast::expr {
123+
@{id: self.next_id(), node: node, span: span}
124+
}
125+
126+
fn ty_path(span: span, strs: [str]) -> @ast::ty {
127+
@{node: ast::ty_path(self.path(span, strs), self.next_id()),
128+
span: span}
129+
}
130+
131+
fn var_ref(span: span, name: str) -> @ast::expr {
132+
self.expr(span, ast::expr_path(self.path(span, [name])))
133+
}
134+
135+
fn blk(span: span, stmts: [@ast::stmt]) -> ast::blk {
136+
{node: {view_items: [],
137+
stmts: stmts,
138+
expr: none,
139+
id: self.next_id(),
140+
rules: ast::default_blk},
141+
span: span}
142+
}
143+
144+
fn binder_pat(span: span, nm: str) -> @ast::pat {
145+
let path = @{node: {global: false,
146+
idents: [nm],
147+
types: []},
148+
span: span};
149+
@{id: self.next_id(),
150+
node: ast::pat_ident(path, none),
151+
span: span}
152+
}
153+
154+
fn stmt(expr: @ast::expr) -> @ast::stmt {
155+
@{node: ast::stmt_semi(expr, self.next_id()),
156+
span: expr.span}
157+
}
158+
159+
fn alt_stmt(arms: [ast::arm], span: span, -v: @ast::expr) -> @ast::stmt {
160+
self.stmt(
161+
self.expr(
162+
span,
163+
ast::expr_alt(v, arms, ast::alt_exhaustive)))
164+
}
165+
166+
fn clone(v: @ast::expr) -> @ast::expr {
167+
let fld = fold::make_fold({
168+
new_id: {|_id| self.next_id()}
169+
with *fold::default_ast_fold()
170+
});
171+
fld.fold_expr(v)
172+
}
173+
174+
fn clone_ty_param(v: ast::ty_param) -> ast::ty_param {
175+
let fld = fold::make_fold({
176+
new_id: {|_id| self.next_id()}
177+
with *fold::default_ast_fold()
178+
});
179+
fold::fold_ty_param(v, fld)
180+
}
181+
182+
fn at(span: span, expr: @ast::expr) -> @ast::expr {
183+
fn repl_sp(old_span: span, repl_span: span, with_span: span) -> span {
184+
if old_span == repl_span {
185+
with_span
186+
} else {
187+
old_span
188+
}
189+
}
190+
191+
let fld = fold::make_fold({
192+
new_span: repl_sp(_, ast_util::dummy_sp(), span)
193+
with *fold::default_ast_fold()
194+
});
195+
196+
fld.fold_expr(expr)
197+
}
198+
}
199+
200+
fn serialize_path(cx: ser_cx, path: @ast::path, -s: @ast::expr, -v: @ast::expr)
201+
-> [@ast::stmt] {
202+
let ext_cx = cx.ext_cx;
203+
204+
// We want to take a path like a::b::c<...> and generate a call
205+
// like a::b::c::serialize(s, ...), as described above.
206+
207+
let callee =
208+
cx.expr(
209+
path.span,
210+
ast::expr_path(
211+
cx.path(path.span, path.node.idents + ["serialize"])));
212+
213+
let ty_args = vec::map(path.node.types) {|ty|
214+
let sv = serialize_ty(cx, ty, s, #ast(expr){"__v"});
215+
cx.at(ty.span, #ast(expr){"{|__v| $(sv)}"})
216+
};
217+
218+
[cx.stmt(
219+
cx.expr(
220+
path.span,
221+
ast::expr_call(callee, [s] + ty_args + [v], false)))]
222+
}
223+
224+
fn serialize_variant(cx: ser_cx,
225+
tys: [@ast::ty],
226+
span: span,
227+
-s: @ast::expr,
228+
pfn: fn([@ast::pat]) -> ast::pat_) -> ast::arm {
229+
let vnames = vec::init_fn(vec::len(tys)) {|i| #fmt["__v%u", i]};
230+
let pats = vec::init_fn(vec::len(tys)) {|i|
231+
cx.binder_pat(tys[i].span, vnames[i])
232+
};
233+
let pat: @ast::pat = @{id: cx.next_id(), node: pfn(pats), span: span};
234+
let stmts = vec::init_fn(vec::len(tys)) {|i|
235+
let v = cx.var_ref(span, vnames[i]);
236+
serialize_ty(cx, tys[i], cx.clone(s), v)
237+
};
238+
{pats: [pat], guard: none, body: cx.blk(span, vec::concat(stmts))}
239+
}
240+
241+
fn serialize_ty(cx: ser_cx, ty: @ast::ty, -s: @ast::expr, -v: @ast::expr)
242+
-> [@ast::stmt] {
243+
let ext_cx = cx.ext_cx;
244+
245+
alt ty.node {
246+
ast::ty_nil | ast::ty_bot {
247+
[]
248+
}
249+
250+
ast::ty_box(mt) |
251+
ast::ty_uniq(mt) |
252+
ast::ty_ptr(mt) {
253+
serialize_ty(cx, mt.ty, s, #ast(expr){"*$(v)"})
254+
}
255+
256+
ast::ty_rec(flds) {
257+
vec::flat_map(flds) {|fld|
258+
let vf = cx.expr(
259+
fld.span,
260+
ast::expr_field(cx.clone(v), fld.node.ident, []));
261+
serialize_ty(cx, fld.node.mt.ty, cx.clone(s), vf)
262+
}
263+
}
264+
265+
ast::ty_fn(_, _) {
266+
cx.session().span_err(
267+
ty.span, #fmt["Cannot serialize function types"]);
268+
[]
269+
}
270+
271+
ast::ty_tup(tys) {
272+
// Generate code like
273+
//
274+
// alt v {
275+
// (v1, v2, v3) {
276+
// .. serialize v1, v2, v3 ..
277+
// }
278+
// };
279+
280+
let arms = [
281+
serialize_variant(cx, tys, ty.span, s,
282+
{|pats| ast::pat_tup(pats)})
283+
];
284+
[cx.alt_stmt(arms, ty.span, v)]
285+
}
286+
287+
ast::ty_path(path, _) {
288+
if vec::len(path.node.idents) == 1u &&
289+
vec::is_empty(path.node.types) {
290+
let ident = path.node.idents[0];
291+
292+
alt cx.tps.find(ident) {
293+
some(f) { f(v) }
294+
none { serialize_path(cx, path, s, v) }
295+
}
296+
} else {
297+
serialize_path(cx, path, s, v)
298+
}
299+
}
300+
301+
ast::ty_constr(ty, _) {
302+
serialize_ty(cx, ty, s, v)
303+
}
304+
305+
ast::ty_mac(_) {
306+
cx.session().span_err(
307+
ty.span, #fmt["Cannot serialize macro types"]);
308+
[]
309+
}
310+
311+
ast::ty_infer {
312+
cx.session().span_err(
313+
ty.span, #fmt["Cannot serialize inferred types"]);
314+
[]
315+
}
316+
317+
ast::ty_vec(mt) {
318+
let ser_e =
319+
cx.expr(
320+
ty.span,
321+
expr_block(
322+
cx.blk(
323+
ty.span,
324+
serialize_ty(
325+
cx, mt.ty,
326+
cx.clone(s),
327+
cx.at(
328+
ty.span,
329+
#ast(expr){__e})))));
330+
[#ast(stmt){ $(s).emit_from_vec($(v), {|__e| $(ser_e) }) }]
331+
}
332+
}
333+
}
334+
335+
fn ty_module(ext_cx: ext_ctxt, name: str, -ty: @ast::ty, tps: [ast::ty_param])
336+
-> @ast::item {
337+
338+
let cx = ser_cx({ext_cx: ext_cx, tps: map::new_str_hash()});
339+
340+
let ser_inputs: [ast::arg] =
341+
[{mode: ast::expl(ast::by_ref),
342+
ty: cx.ty_path(ty.span, ["__S"]),
343+
ident: "__s",
344+
id: cx.next_id()},
345+
{mode: ast::expl(ast::by_ref),
346+
ty: ty,
347+
ident: "__v",
348+
id: cx.next_id()}] +
349+
vec::map(tps, {|tp|
350+
{mode: ast::expl(ast::by_ref),
351+
ty: cx.ty_path(ty.span, [tp.ident]),
352+
ident: "__v",
353+
id: cx.next_id()}});
354+
355+
let ser_bnds = @[ast::bound_iface(cx.ty_path(ty.span,
356+
["__std", "serialization",
357+
"serializer"]))];
358+
let ser_tps: [ast::ty_param] =
359+
[{ident: "__S",
360+
id: cx.next_id(),
361+
bounds: ser_bnds}] +
362+
vec::map(tps) {|tp| cx.clone_ty_param(tp) };
363+
364+
let ser_output: @ast::ty = @{node: ast::ty_nil,
365+
span: ty.span};
366+
367+
let ser_blk = cx.blk(ty.span,
368+
serialize_ty(cx, ty,
369+
#ast(expr){"__s"}, #ast(expr){"__v"}));
370+
371+
@{ident: "serialize",
372+
attrs: [],
373+
id: cx.next_id(),
374+
node: ast::item_fn({inputs: ser_inputs,
375+
output: ser_output,
376+
purity: ast::impure_fn,
377+
cf: ast::return_val,
378+
constraints: []},
379+
ser_tps,
380+
ser_blk),
381+
span: ty.span}
382+
}
383+
384+
fn enum_module(cx: ext_ctxt, name: str,
385+
variants: [ast::variant], tps: [ast::ty_param])
386+
-> @ast::item {
387+
388+
}

0 commit comments

Comments
 (0)