]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/pipes/pipec.rs
libsyntax: Remove last use of structural records in pipes compiler.
[rust.git] / src / libsyntax / ext / pipes / pipec.rs
1 // Copyright 2012 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
11 // A protocol compiler for Rust.
12
13 use ast::ident;
14 use codemap::dummy_sp;
15 use ext::base::ext_ctxt;
16 use ext::pipes::ast_builder::{append_types, ext_ctxt_ast_builder, path};
17 use ext::pipes::ast_builder::{path_global};
18 use ext::pipes::proto::*;
19 use ext::quote::rt::*;
20 use parse::*;
21 use util::interner;
22
23 use core::dvec::DVec;
24 use core::prelude::*;
25 use core::str;
26 use core::to_str::ToStr;
27 use core::vec;
28
29 pub trait gen_send {
30     fn gen_send(cx: ext_ctxt, try: bool) -> @ast::item;
31     fn to_ty(cx: ext_ctxt) -> @ast::Ty;
32 }
33
34 pub trait to_type_decls {
35     fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item];
36     fn to_endpoint_decls(cx: ext_ctxt, dir: direction) -> ~[@ast::item];
37 }
38
39 pub trait gen_init {
40     fn gen_init(cx: ext_ctxt) -> @ast::item;
41     fn compile(cx: ext_ctxt) -> @ast::item;
42     fn buffer_ty_path(cx: ext_ctxt) -> @ast::Ty;
43     fn gen_buffer_type(cx: ext_ctxt) -> @ast::item;
44     fn gen_buffer_init(ext_cx: ext_ctxt) -> @ast::expr;
45     fn gen_init_bounded(ext_cx: ext_ctxt) -> @ast::expr;
46 }
47
48 pub impl gen_send for message {
49     fn gen_send(cx: ext_ctxt, try: bool) -> @ast::item {
50         debug!("pipec: gen_send");
51         match self {
52           message(ref _id, span, tys, this,
53                   Some(next_state {state: ref next, tys: next_tys})) => {
54             debug!("pipec: next state exists");
55             let next = this.proto.get_state((*next));
56             assert next_tys.len() == next.ty_params.len();
57             let arg_names = tys.mapi(|i, _ty| cx.ident_of(~"x_"+i.to_str()));
58
59             let args_ast = (arg_names, tys).map(
60                 |n, t| cx.arg(*n, *t)
61             );
62
63             let pipe_ty = cx.ty_path_ast_builder(
64                 path(~[this.data_name()], span)
65                 .add_tys(cx.ty_vars_global(this.ty_params)));
66             let args_ast = vec::append(
67                 ~[cx.arg(cx.ident_of(~"pipe"),
68                               pipe_ty)],
69                 args_ast);
70
71             let mut body = ~"{\n";
72             body += fmt!("use super::%s;\n", self.name());
73
74             if this.proto.is_bounded() {
75                 let (sp, rp) = match (this.dir, next.dir) {
76                   (send, send) => (~"c", ~"s"),
77                   (send, recv) => (~"s", ~"c"),
78                   (recv, send) => (~"s", ~"c"),
79                   (recv, recv) => (~"c", ~"s")
80                 };
81
82                 body += ~"let b = pipe.reuse_buffer();\n";
83                 body += fmt!("let %s = ::pipes::SendPacketBuffered(\
84                               ::ptr::addr_of(&(b.buffer.data.%s)));\n",
85                              sp, next.name);
86                 body += fmt!("let %s = ::pipes::RecvPacketBuffered(\
87                               ::ptr::addr_of(&(b.buffer.data.%s)));\n",
88                              rp, next.name);
89             }
90             else {
91                 let pat = match (this.dir, next.dir) {
92                   (send, send) => "(c, s)",
93                   (send, recv) => "(s, c)",
94                   (recv, send) => "(s, c)",
95                   (recv, recv) => "(c, s)"
96                 };
97
98                 body += fmt!("let %s = ::pipes::entangle();\n", pat);
99             }
100             body += fmt!("let message = %s(%s);\n",
101                          self.name(),
102                          str::connect(vec::append_one(
103                            arg_names.map(|x| cx.str_of(*x)),
104                              ~"s"), ~", "));
105
106             if !try {
107                 body += fmt!("::pipes::send(pipe, message);\n");
108                 // return the new channel
109                 body += ~"c }";
110             }
111             else {
112                 body += fmt!("if ::pipes::send(pipe, message) {\n \
113                                   ::pipes::rt::make_some(c) \
114                               } else { ::pipes::rt::make_none() } }");
115             }
116
117             let body = cx.parse_expr(body);
118
119             let mut rty = cx.ty_path_ast_builder(path(~[next.data_name()],
120                                                       span)
121                                                  .add_tys(next_tys));
122             if try {
123                 rty = cx.ty_option(rty);
124             }
125
126             let name = cx.ident_of(if try { ~"try_" + self.name()
127                                           } else { self.name() } );
128
129             cx.item_fn_poly(name,
130                             args_ast,
131                             rty,
132                             self.get_params(),
133                             cx.expr_block(body))
134           }
135
136             message(ref _id, span, tys, this, None) => {
137                 debug!("pipec: no next state");
138                 let arg_names = tys.mapi(|i, _ty| (~"x_" + i.to_str()));
139
140                 let args_ast = (arg_names, tys).map(
141                     |n, t| cx.arg(cx.ident_of(*n), *t)
142                 );
143
144                 let args_ast = vec::append(
145                     ~[cx.arg(cx.ident_of(~"pipe"),
146                                   cx.ty_path_ast_builder(
147                                       path(~[this.data_name()], span)
148                                       .add_tys(cx.ty_vars_global(
149                                         this.ty_params))))],
150                     args_ast);
151
152                 let message_args = if arg_names.len() == 0 {
153                     ~""
154                 }
155                 else {
156                     ~"(" + str::connect(arg_names.map(|x| *x),
157                                         ~", ") + ~")"
158                 };
159
160                 let mut body = ~"{ ";
161                 body += fmt!("use super::%s;\n", self.name());
162                 body += fmt!("let message = %s%s;\n",
163                              self.name(),
164                              message_args);
165
166                 if !try {
167                     body += fmt!("::pipes::send(pipe, message);\n");
168                     body += ~" }";
169                 } else {
170                     body += fmt!("if ::pipes::send(pipe, message) \
171                                         { \
172                                       ::pipes::rt::make_some(()) \
173                                   } else { \
174                                     ::pipes::rt::make_none() \
175                                   } }");
176                 }
177
178                 let body = cx.parse_expr(body);
179
180                 let name = if try {
181                     ~"try_" + self.name()
182                 }
183                 else { self.name() };
184
185                 cx.item_fn_poly(cx.ident_of(name),
186                                 args_ast,
187                                 if try {
188                                     cx.ty_option(cx.ty_nil_ast_builder())
189                                 } else {
190                                     cx.ty_nil_ast_builder()
191                                 },
192                                 self.get_params(),
193                                 cx.expr_block(body))
194             }
195           }
196         }
197
198     fn to_ty(cx: ext_ctxt) -> @ast::Ty {
199         cx.ty_path_ast_builder(path(~[cx.ident_of(self.name())], self.span())
200           .add_tys(cx.ty_vars_global(self.get_params())))
201     }
202 }
203
204 pub impl to_type_decls for state {
205     fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item] {
206         debug!("pipec: to_type_decls");
207         // This compiles into two different type declarations. Say the
208         // state is called ping. This will generate both `ping` and
209         // `ping_message`. The first contains data that the user cares
210         // about. The second is the same thing, but extended with a
211         // next packet pointer, which is used under the covers.
212
213         let name = self.data_name();
214
215         let mut items_msg = ~[];
216
217         for self.messages.each |m| {
218             let message(name, span, tys, this, next) = *m;
219
220             let tys = match next {
221               Some(next_state { state: ref next, tys: next_tys }) => {
222                 let next = this.proto.get_state((*next));
223                 let next_name = cx.str_of(next.data_name());
224
225                 let dir = match this.dir {
226                   send => ~"server",
227                   recv => ~"client"
228                 };
229
230                 vec::append_one(tys,
231                                 cx.ty_path_ast_builder(
232                                     path(~[cx.ident_of(dir),
233                                            cx.ident_of(next_name)], span)
234                                     .add_tys(next_tys)))
235               }
236               None => tys
237             };
238
239             let v = cx.variant(cx.ident_of(name), span, tys);
240
241             items_msg.push(v);
242         }
243
244         ~[
245             cx.item_enum_poly(
246                 name,
247                 self.span,
248                 ast::enum_def(enum_def_ {
249                     variants: items_msg,
250                     common: None }),
251                 cx.strip_bounds(self.ty_params)
252             )
253         ]
254     }
255
256     fn to_endpoint_decls(cx: ext_ctxt, dir: direction) -> ~[@ast::item] {
257         debug!("pipec: to_endpoint_decls");
258         let dir = match dir {
259           send => (*self).dir,
260           recv => (*self).dir.reverse()
261         };
262         let mut items = ~[];
263         for self.messages.each |m| {
264             if dir == send {
265                 items.push(m.gen_send(cx, true));
266                 items.push(m.gen_send(cx, false));
267             }
268         }
269
270         if !self.proto.is_bounded() {
271             items.push(
272                 cx.item_ty_poly(
273                     self.data_name(),
274                     self.span,
275                     cx.ty_path_ast_builder(
276                         path_global(~[cx.ident_of(~"pipes"),
277                                       cx.ident_of(dir.to_str() + ~"Packet")],
278                              dummy_sp())
279                         .add_ty(cx.ty_path_ast_builder(
280                             path(~[cx.ident_of(~"super"),
281                                    self.data_name()],
282                                  dummy_sp())
283                             .add_tys(cx.ty_vars_global(self.ty_params))))),
284                     cx.strip_bounds(self.ty_params)));
285         }
286         else {
287             items.push(
288                 cx.item_ty_poly(
289                     self.data_name(),
290                     self.span,
291                     cx.ty_path_ast_builder(
292                         path_global(~[cx.ident_of(~"pipes"),
293                                       cx.ident_of(dir.to_str()
294                                                   + ~"PacketBuffered")],
295                              dummy_sp())
296                         .add_tys(~[cx.ty_path_ast_builder(
297                             path(~[cx.ident_of(~"super"),
298                                    self.data_name()],
299                                         dummy_sp())
300                             .add_tys(cx.ty_vars_global(self.ty_params))),
301                                    self.proto.buffer_ty_path(cx)])),
302                     cx.strip_bounds(self.ty_params)));
303         };
304         items
305     }
306 }
307
308 pub impl gen_init for protocol {
309     fn gen_init(cx: ext_ctxt) -> @ast::item {
310         let ext_cx = cx;
311
312         debug!("gen_init");
313         let start_state = self.states[0];
314
315         let body = if !self.is_bounded() {
316             match start_state.dir {
317               send => quote_expr!( ::pipes::entangle() ),
318               recv => {
319                 quote_expr!({
320                     let (s, c) = ::pipes::entangle();
321                     (c, s)
322                 })
323               }
324             }
325         }
326         else {
327             let body = self.gen_init_bounded(ext_cx);
328             match start_state.dir {
329               send => body,
330               recv => {
331                   quote_expr!({
332                       let (s, c) = $body;
333                       (c, s)
334                   })
335               }
336             }
337         };
338
339         cx.parse_item(fmt!("pub fn init%s() -> (client::%s, server::%s)\
340                             { use pipes::HasBuffer; %s }",
341                            start_state.ty_params.to_source(cx),
342                            start_state.to_ty(cx).to_source(cx),
343                            start_state.to_ty(cx).to_source(cx),
344                            body.to_source(cx)))
345     }
346
347     fn gen_buffer_init(ext_cx: ext_ctxt) -> @ast::expr {
348         ext_cx.struct_expr(path(~[ext_cx.ident_of(~"__Buffer")],
349                                 dummy_sp()),
350                       self.states.map_to_vec(|s| {
351             let fty = s.to_ty(ext_cx);
352             ext_cx.field_imm(ext_cx.ident_of(s.name),
353                              quote_expr!(
354                                  ::pipes::mk_packet::<$fty>()
355                              ))
356         }))
357     }
358
359     fn gen_init_bounded(ext_cx: ext_ctxt) -> @ast::expr {
360         debug!("gen_init_bounded");
361         let buffer_fields = self.gen_buffer_init(ext_cx);
362         let buffer = quote_expr!(~::pipes::Buffer {
363             header: ::pipes::BufferHeader(),
364             data: $buffer_fields,
365         });
366
367         let entangle_body = ext_cx.block_expr(
368             ext_cx.block(
369                 self.states.map_to_vec(
370                     |s| ext_cx.parse_stmt(
371                         fmt!("data.%s.set_buffer(buffer)",
372                              s.name))),
373                 ext_cx.parse_expr(
374                     fmt!("::ptr::addr_of(&(data.%s))",
375                          self.states[0].name))));
376
377         quote_expr!({
378             let buffer = $buffer;
379             do ::pipes::entangle_buffer(buffer) |buffer, data| {
380                 $entangle_body
381             }
382         })
383     }
384
385     fn buffer_ty_path(cx: ext_ctxt) -> @ast::Ty {
386         let mut params: ~[ast::ty_param] = ~[];
387         for (copy self.states).each |s| {
388             for s.ty_params.each |tp| {
389                 match params.find(|tpp| tp.ident == tpp.ident) {
390                   None => params.push(*tp),
391                   _ => ()
392                 }
393             }
394         }
395
396         cx.ty_path_ast_builder(path(~[cx.ident_of(~"super"),
397                                       cx.ident_of(~"__Buffer")], self.span)
398                                .add_tys(cx.ty_vars_global(params)))
399     }
400
401     fn gen_buffer_type(cx: ext_ctxt) -> @ast::item {
402         let ext_cx = cx;
403         let mut params: ~[ast::ty_param] = ~[];
404         let fields = do (copy self.states).map_to_vec |s| {
405             for s.ty_params.each |tp| {
406                 match params.find(|tpp| tp.ident == tpp.ident) {
407                   None => params.push(*tp),
408                   _ => ()
409                 }
410             }
411             let ty = s.to_ty(cx);
412             let fty = quote_ty!( ::pipes::Packet<$ty> );
413
414             @spanned {
415                 node: ast::struct_field_ {
416                     kind: ast::named_field(
417                             cx.ident_of(s.name),
418                             ast::struct_immutable,
419                             ast::inherited),
420                     id: cx.next_id(),
421                     ty: fty
422                 },
423                 span: dummy_sp()
424             }
425         };
426
427         cx.item_struct_poly(
428             cx.ident_of(~"__Buffer"),
429             dummy_sp(),
430             ast::struct_def {
431                 fields: fields,
432                 dtor: None,
433                 ctor_id: None
434             },
435             cx.strip_bounds(params))
436     }
437
438     fn compile(cx: ext_ctxt) -> @ast::item {
439         let mut items = ~[self.gen_init(cx)];
440         let mut client_states = ~[];
441         let mut server_states = ~[];
442
443         for (copy self.states).each |s| {
444             items += s.to_type_decls(cx);
445
446             client_states += s.to_endpoint_decls(cx, send);
447             server_states += s.to_endpoint_decls(cx, recv);
448         }
449
450         if self.is_bounded() {
451             items.push(self.gen_buffer_type(cx))
452         }
453
454         items.push(cx.item_mod(cx.ident_of(~"client"),
455                                self.span,
456                                client_states));
457         items.push(cx.item_mod(cx.ident_of(~"server"),
458                                self.span,
459                                server_states));
460
461         cx.item_mod(cx.ident_of(self.name), self.span, items)
462     }
463 }