Skip to content

Commit

Permalink
Add support for terminal states (issue #2862)
Browse files Browse the repository at this point in the history
  • Loading branch information
eholk committed Jul 16, 2012
1 parent c46bf6e commit d39f6fc
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 48 deletions.
6 changes: 6 additions & 0 deletions src/libsyntax/ext/pipes/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ impl ast_builder for ext_ctxt {
span: empty_span()}
}

fn ty_nil() -> @ast::ty {
@{id: self.next_id(),
node: ast::ty_nil,
span: empty_span()}
}

fn item_ty_poly(name: ident,
ty: @ast::ty,
+params: ~[ast::ty_param]) -> @ast::item {
Expand Down
56 changes: 34 additions & 22 deletions src/libsyntax/ext/pipes/parse_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,45 @@ impl proto_parser for parser {
self.parse_unspanned_seq(
token::LBRACE, token::RBRACE,
{sep: some(token::COMMA), trailing_sep_allowed: true},
|self| {
let mname = self.parse_ident();
|self| self.parse_message(state));
}

let args = if self.token == token::LPAREN {
self.parse_unspanned_seq(token::LPAREN,
token::RPAREN,
{sep: some(token::COMMA),
trailing_sep_allowed: true},
|p| p.parse_ty(false))
}
else { ~[] };
fn parse_message(state: state) {
let mname = self.parse_ident();

self.expect(token::RARROW);
let args = if self.token == token::LPAREN {
self.parse_unspanned_seq(token::LPAREN,
token::RPAREN,
{sep: some(token::COMMA),
trailing_sep_allowed: true},
|p| p.parse_ty(false))
}
else { ~[] };

let next = self.parse_ident();
self.expect(token::RARROW);

let ntys = if self.token == token::LT {
self.parse_unspanned_seq(token::LT,
token::GT,
{sep: some(token::COMMA),
trailing_sep_allowed: true},
|p| p.parse_ty(false))
}
else { ~[] };
let next = alt copy self.token {
token::IDENT(_, _) {
let name = self.parse_ident();
let ntys = if self.token == token::LT {
self.parse_unspanned_seq(token::LT,
token::GT,
{sep: some(token::COMMA),
trailing_sep_allowed: true},
|p| p.parse_ty(false))
}
else { ~[] };
some({state: name, tys: ntys})
}
token::NOT {
// -> !
self.bump();
none
}
_ { self.fatal(~"invalid next state") }
};

state.add_message(mname, args, next, ntys);
state.add_message(mname, args, next);

});
}
}
93 changes: 71 additions & 22 deletions src/libsyntax/ext/pipes/pipec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ impl methods for direction {
}
}

type next_state = option<{state: ident, tys: ~[@ast::ty]}>;

enum message {
// name, data, current state, next state, next tys
message(ident, ~[@ast::ty], state, ident, ~[@ast::ty])
// name, data, current state, next state
message(ident, ~[@ast::ty], state, next_state)
}

impl methods for message {
fn name() -> ident {
alt self {
message(id, _, _, _, _) {
message(id, _, _, _) {
id
}
}
Expand All @@ -57,15 +59,17 @@ impl methods for message {
// Return the type parameters actually used by this message
fn get_params() -> ~[ast::ty_param] {
alt self {
message(_, _, this, _, _) {
message(_, _, this, _) {
this.ty_params
}
}
}

fn gen_send(cx: ext_ctxt) -> @ast::item {
#debug("pipec: gen_send");
alt self {
message(id, tys, this, next, next_tys) {
message(id, tys, this, some({state: next, tys: next_tys})) {
#debug("pipec: next state exists");
let next = this.proto.get_state(next);
assert next_tys.len() == next.ty_params.len();
let arg_names = tys.mapi(|i, _ty| @(~"x_" + i.to_str()));
Expand Down Expand Up @@ -107,6 +111,45 @@ impl methods for message {
self.get_params(),
cx.expr_block(body))
}

message(id, tys, this, none) {
#debug("pipec: no next state");
let arg_names = tys.mapi(|i, _ty| @(~"x_" + i.to_str()));

let args_ast = (arg_names, tys).map(
|n, t| cx.arg_mode(n, t, ast::by_copy)
);

let args_ast = vec::append(
~[cx.arg_mode(@~"pipe",
cx.ty_path(path(this.data_name())
.add_tys(cx.ty_vars(this.ty_params))),
ast::by_copy)],
args_ast);

let message_args = if arg_names.len() == 0 {
~""
}
else {
~"(" + str::connect(arg_names.map(|x| *x), ~", ") + ~")"
};

let mut body = ~"{ ";
body += #fmt("let message = %s::%s%s;\n",
*this.proto.name,
*self.name(),
message_args);
body += #fmt("pipes::send(pipe, message);\n");
body += ~" }";

let body = cx.parse_expr(body);

cx.item_fn_poly(self.name(),
args_ast,
cx.ty_nil(),
self.get_params(),
cx.expr_block(body))
}
}
}
}
Expand All @@ -122,9 +165,9 @@ enum state {
}

impl methods for state {
fn add_message(name: ident, +data: ~[@ast::ty], next: ident,
+next_tys: ~[@ast::ty]) {
self.messages.push(message(name, data, self, next, next_tys));
fn add_message(name: ident, +data: ~[@ast::ty], next: next_state) {
self.messages.push(message(name, data, self,
next));
}

fn filename() -> ~str {
Expand All @@ -140,6 +183,7 @@ impl methods for state {
}

fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item] {
#debug("pipec: to_type_decls");
// This compiles into two different type declarations. Say the
// state is called ping. This will generate both `ping` and
// `ping_message`. The first contains data that the user cares
Expand All @@ -151,22 +195,26 @@ impl methods for state {
let mut items_msg = ~[];

for self.messages.each |m| {
let message(_, tys, this, next, next_tys) = m;

let name = m.name();
let next = this.proto.get_state(next);
let next_name = next.data_name();

let dir = alt this.dir {
send { @~"server" }
recv { @~"client" }
let message(name, tys, this, next) = m;

let tys = alt next {
some({state: next, tys: next_tys}) {
let next = this.proto.get_state(next);
let next_name = next.data_name();

let dir = alt this.dir {
send { @~"server" }
recv { @~"client" }
};

vec::append_one(tys,
cx.ty_path((dir + next_name)
.add_tys(next_tys)))
}
none { tys }
};

let v = cx.variant(name,
vec::append_one(
tys,
cx.ty_path((dir + next_name)
.add_tys(next_tys))));
let v = cx.variant(name, tys);

vec::push(items_msg, v);
}
Expand All @@ -175,6 +223,7 @@ impl methods for state {
}

fn to_endpoint_decls(cx: ext_ctxt, dir: direction) -> ~[@ast::item] {
#debug("pipec: to_endpoint_decls");
let dir = alt dir {
send { (*self).dir }
recv { (*self).dir.reverse() }
Expand Down
7 changes: 3 additions & 4 deletions src/test/run-pass/pipe-select.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// xfail-test
// xfail-pretty
// xfail-win32

Expand All @@ -10,7 +9,7 @@ import pipes::{recv, select};

proto! oneshot {
waiting:send {
signal -> signaled
signal -> !
}

signaled:send { }
Expand Down Expand Up @@ -86,14 +85,14 @@ fn test_select2() {
either::right(*) { fail }
}

stream::client::send(bc, "abc");
stream::client::send(bc, ~"abc");

#error("done with first select2");

let (ac, ap) = stream::init();
let (bc, bp) = stream::init();

stream::client::send(bc, "abc");
stream::client::send(bc, ~"abc");

alt pipes::select2(ap, bp) {
either::left(*) { fail }
Expand Down

0 comments on commit d39f6fc

Please sign in to comment.