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.
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.
11 // A protocol compiler for Rust.
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::*;
26 use core::to_str::ToStr;
30 fn gen_send(&self, cx: ext_ctxt, try: bool) -> @ast::item;
31 fn to_ty(&self, cx: ext_ctxt) -> @ast::Ty;
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];
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;
49 pub impl gen_send for message {
50 fn gen_send(&self, cx: ext_ctxt, try: bool) -> @ast::item {
51 debug!("pipec: gen_send");
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()));
60 let args_ast = (arg_names, tys).map(
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"),
72 let mut body = ~"{\n";
73 body += fmt!("use super::%s;\n", self.name());
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")
83 body += ~"let b = pipe.reuse_buffer();\n";
84 body += fmt!("let %s = ::pipes::SendPacketBuffered(\
85 ::ptr::addr_of(&(b.buffer.data.%s)));\n",
87 body += fmt!("let %s = ::pipes::RecvPacketBuffered(\
88 ::ptr::addr_of(&(b.buffer.data.%s)));\n",
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)"
99 body += fmt!("let %s = ::pipes::entangle();\n", pat);
101 body += fmt!("let message = %s(%s);\n",
103 str::connect(vec::append_one(
104 arg_names.map(|x| cx.str_of(*x)),
108 body += fmt!("::pipes::send(pipe, message);\n");
109 // return the new channel
113 body += fmt!("if ::pipes::send(pipe, message) {\n \
114 ::pipes::rt::make_some(c) \
115 } else { ::pipes::rt::make_none() } }");
118 let body = cx.parse_expr(body);
120 let mut rty = cx.ty_path_ast_builder(path(~[next.data_name()],
124 rty = cx.ty_option(rty);
127 let name = cx.ident_of(if try { ~"try_" + self.name()
128 } else { self.name() } );
130 cx.item_fn_poly(name,
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()));
141 let args_ast = (arg_names, tys).map(
142 |n, t| cx.arg(cx.ident_of(*n), *t)
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(
153 let message_args = if arg_names.len() == 0 {
157 ~"(" + str::connect(arg_names.map(|x| *x),
161 let mut body = ~"{ ";
162 body += fmt!("use super::%s;\n", self.name());
163 body += fmt!("let message = %s%s;\n",
168 body += fmt!("::pipes::send(pipe, message);\n");
171 body += fmt!("if ::pipes::send(pipe, message) \
173 ::pipes::rt::make_some(()) \
175 ::pipes::rt::make_none() \
179 let body = cx.parse_expr(body);
182 ~"try_" + self.name()
184 else { self.name() };
186 cx.item_fn_poly(cx.ident_of(name),
189 cx.ty_option(cx.ty_nil_ast_builder())
191 cx.ty_nil_ast_builder()
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())))
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.
214 let name = self.data_name();
216 let mut items_msg = ~[];
218 for self.messages.each |m| {
219 let message(name, span, tys, this, next) = *m;
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());
226 let dir = match this.dir {
232 cx.ty_path_ast_builder(
233 path(~[cx.ident_of(dir),
234 cx.ident_of(next_name)], span)
240 let v = cx.variant(cx.ident_of(name), span, tys);
249 ast::enum_def(enum_def_ {
252 cx.strip_bounds(self.ty_params)
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 {
262 recv => (*self).dir.reverse()
265 for self.messages.each |m| {
267 items.push(m.gen_send(cx, true));
268 items.push(m.gen_send(cx, false));
272 if !self.proto.is_bounded() {
277 cx.ty_path_ast_builder(
278 path_global(~[cx.ident_of(~"pipes"),
279 cx.ident_of(dir.to_str() + ~"Packet")],
281 .add_ty(cx.ty_path_ast_builder(
282 path(~[cx.ident_of(~"super"),
285 .add_tys(cx.ty_vars_global(self.ty_params))))),
286 cx.strip_bounds(self.ty_params)));
293 cx.ty_path_ast_builder(
294 path_global(~[cx.ident_of(~"pipes"),
295 cx.ident_of(dir.to_str()
296 + ~"PacketBuffered")],
298 .add_tys(~[cx.ty_path_ast_builder(
299 path(~[cx.ident_of(~"super"),
302 .add_tys(cx.ty_vars_global(self.ty_params))),
303 self.proto.buffer_ty_path(cx)])),
304 cx.strip_bounds(self.ty_params)));
310 pub impl gen_init for protocol {
311 fn gen_init(&self, cx: ext_ctxt) -> @ast::item {
315 let start_state = self.states[0];
317 let body = if !self.is_bounded() {
318 match start_state.dir {
319 send => quote_expr!( ::pipes::entangle() ),
322 let (s, c) = ::pipes::entangle();
329 let body = self.gen_init_bounded(ext_cx);
330 match start_state.dir {
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),
349 fn gen_buffer_init(&self, ext_cx: ext_ctxt) -> @ast::expr {
350 ext_cx.struct_expr(path(~[ext_cx.ident_of(~"__Buffer")],
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),
356 ::pipes::mk_packet::<$fty>()
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,
369 let entangle_body = ext_cx.block_expr(
371 self.states.map_to_vec(
372 |s| ext_cx.parse_stmt(
373 fmt!("data.%s.set_buffer(buffer)",
376 fmt!("::ptr::addr_of(&(data.%s))",
377 self.states[0].name))));
380 let buffer = $buffer;
381 do ::pipes::entangle_buffer(buffer) |buffer, data| {
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),
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)))
403 fn gen_buffer_type(&self, cx: ext_ctxt) -> @ast::item {
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),
413 let ty = s.to_ty(cx);
414 let fty = quote_ty!( ::pipes::Packet<$ty> );
417 node: ast::struct_field_ {
418 kind: ast::named_field(
420 ast::struct_immutable,
430 cx.ident_of(~"__Buffer"),
437 cx.strip_bounds(params))
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 = ~[];
445 for (copy self.states).each |s| {
446 items += s.to_type_decls(cx);
448 client_states += s.to_endpoint_decls(cx, send);
449 server_states += s.to_endpoint_decls(cx, recv);
452 if self.is_bounded() {
453 items.push(self.gen_buffer_type(cx))
456 items.push(cx.item_mod(cx.ident_of(~"client"),
459 items.push(cx.item_mod(cx.ident_of(~"server"),
463 cx.item_mod(cx.ident_of(self.name), self.span, items)