// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use core::prelude::*; use ast; use codemap::span; use ext::base::ext_ctxt; use ext::pipes::ast_builder::{append_types, ext_ctxt_ast_builder, path}; use core::cmp; use core::dvec::DVec; use core::to_str::ToStr; #[deriving_eq] pub enum direction { send, recv } pub impl ToStr for direction { pure fn to_str(&self) -> ~str { match *self { send => ~"Send", recv => ~"Recv" } } } pub impl direction { fn reverse(&self) -> direction { match *self { send => recv, recv => send } } } pub struct next_state { state: ~str, tys: ~[@ast::Ty], } pub enum message { // name, span, data, current state, next state message(~str, span, ~[@ast::Ty], state, Option) } pub impl message { fn name(&self) -> ~str { match *self { message(ref id, _, _, _, _) => (*id) } } fn span(&self) -> span { match *self { message(_, span, _, _, _) => span } } /// Return the type parameters actually used by this message fn get_params(&self) -> ~[ast::ty_param] { match *self { message(_, _, _, this, _) => this.ty_params } } } pub type state = @state_; pub struct state_ { id: uint, name: ~str, ident: ast::ident, span: span, dir: direction, ty_params: ~[ast::ty_param], messages: DVec, proto: protocol } pub impl state_ { fn add_message(@self, name: ~str, span: span, +data: ~[@ast::Ty], next: Option) { self.messages.push(message(name, span, data, self, next)); } fn filename(&self) -> ~str { self.proto.filename() } fn data_name(&self) -> ast::ident { self.ident } /// Returns the type that is used for the messages. fn to_ty(&self, cx: ext_ctxt) -> @ast::Ty { cx.ty_path_ast_builder (path(~[cx.ident_of(self.name)],self.span).add_tys( cx.ty_vars(self.ty_params))) } /// Iterate over the states that can be reached in one message /// from this state. fn reachable(&self, f: fn(state) -> bool) { for self.messages.each |m| { match *m { message(_, _, _, _, Some(next_state { state: ref id, _ })) => { let state = self.proto.get_state((*id)); if !f(state) { break } } _ => () } } } } pub type protocol = @protocol_; pub fn protocol(name: ~str, +span: span) -> protocol { @protocol_(name, span) } pub fn protocol_(name: ~str, span: span) -> protocol_ { protocol_ { name: name, span: span, states: DVec(), bounded: None } } pub struct protocol_ { name: ~str, span: span, states: DVec, mut bounded: Option, } pub impl protocol_ { /// Get a state. fn get_state(&self, name: ~str) -> state { self.states.find(|i| i.name == name).get() } fn get_state_by_id(&self, id: uint) -> state { self.states[id] } fn has_state(&self, name: ~str) -> bool { self.states.find(|i| i.name == name).is_some() } fn filename(&self) -> ~str { ~"proto://" + self.name } fn num_states(&self) -> uint { self.states.len() } fn has_ty_params(&self) -> bool { for self.states.each |s| { if s.ty_params.len() > 0 { return true; } } false } fn is_bounded(&self) -> bool { let bounded = self.bounded.get(); bounded } } pub impl protocol { fn add_state_poly(&self, name: ~str, ident: ast::ident, dir: direction, +ty_params: ~[ast::ty_param]) -> state { let messages = DVec(); let state = @state_ { id: self.states.len(), name: name, ident: ident, span: self.span, dir: dir, ty_params: ty_params, messages: messages, proto: *self }; self.states.push(state); state } } pub trait visitor { fn visit_proto(&self, proto: protocol, st: &[Tstate]) -> Tproto; fn visit_state(&self, state: state, m: &[Tmessage]) -> Tstate; fn visit_message(&self, name: ~str, spane: span, tys: &[@ast::Ty], this: state, next: Option) -> Tmessage; } pub fn visit>( proto: protocol, visitor: V) -> Tproto { // the copy keywords prevent recursive use of dvec let states = do (copy proto.states).map_to_vec |&s| { let messages = do (copy s.messages).map_to_vec |&m| { let message(name, span, tys, this, next) = m; visitor.visit_message(name, span, tys, this, next) }; visitor.visit_state(s, messages) }; visitor.visit_proto(proto, states) }