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