mod builder;
mod run;
use crate::grammar::*;
use crate::program_graph::{
Action as PgAction, Clock as PgClock, Location as PgLocation, Var as PgVar, *,
};
pub use builder::*;
use get_size2::GetSize;
pub use run::ChannelSystemRun;
use thiserror::Error;
type PgIndex = u16;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, GetSize)]
pub struct PgId(PgIndex);
impl From<PgId> for PgIndex {
#[inline]
fn from(val: PgId) -> Self {
val.0
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, GetSize)]
pub struct Channel(u16);
impl From<Channel> for u16 {
#[inline]
fn from(val: Channel) -> Self {
val.0
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Location(PgId, PgLocation);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Action(PgId, PgAction);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Var(PgId, PgVar);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, GetSize)]
pub struct Clock(PgId, PgClock);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, GetSize)]
pub enum Message {
Send,
Receive,
ProbeEmptyQueue,
ProbeFullQueue,
}
#[derive(Debug, Clone, Copy, Error)]
pub enum CsError {
#[error("error from program graph {0:?}")]
ProgramGraph(PgId, #[source] PgError),
#[error("program graph {0:?} does not belong to the channel system")]
MissingPg(PgId),
#[error("channel {0:?} is at full capacity")]
OutOfCapacity(Channel),
#[error("the channel still has free space {0:?}")]
NotFull(Channel),
#[error("channel {0:?} is empty")]
Empty(Channel),
#[error("channel {0:?} is not empty")]
NotEmpty(Channel),
#[error("communication {0:?} has not been defined")]
NoCommunication(Action),
#[error("action {0:?} does not belong to program graph {1:?}")]
ActionNotInPg(Action, PgId),
#[error("variable {0:?} does not belong to program graph {1:?}")]
VarNotInPg(Var, PgId),
#[error("location {0:?} does not belong to program graph {1:?}")]
LocationNotInPg(Location, PgId),
#[error("clock {0:?} does not belong to program graph {1:?}")]
ClockNotInPg(Clock, PgId),
#[error("program graphs {0:?} and {1:?} do not match")]
DifferentPgs(PgId, PgId),
#[error("action {0:?} is a communication")]
ActionIsCommunication(Action),
#[error("channel {0:?} does not exists")]
MissingChannel(Channel),
#[error("cannot probe handshake {0:?}")]
ProbingHandshakeChannel(Channel),
#[error("cannot probe for fullness the infinite capacity {0:?}")]
ProbingInfiniteQueue(Channel),
#[error("type error")]
Type(#[source] TypeError),
}
#[derive(Debug, Clone, PartialEq, GetSize)]
pub struct Event {
pub pg_id: PgId,
pub channel: Channel,
pub event_type: EventType,
}
#[derive(Debug, Clone, PartialEq, GetSize)]
pub enum EventType {
Send(Vec<Val>),
Receive(Vec<Val>),
ProbeEmptyQueue,
ProbeFullQueue,
}
#[derive(Debug, Clone, Copy, GetSize)]
pub enum ChannelCapacity {
Queue(Option<usize>),
Sink,
}
#[derive(Debug, Clone, GetSize)]
pub struct ChannelSystem {
channels: Vec<(Vec<Type>, ChannelCapacity)>,
communications: Vec<Option<(Channel, Message)>>,
communications_pg_idxs: Vec<usize>,
program_graphs: Vec<ProgramGraph>,
}
impl ChannelSystem {
pub fn new_instance<'def>(&'def self) -> ChannelSystemRun<'def> {
ChannelSystemRun::new(self)
}
#[inline]
fn communication(&self, pg_id: PgId, pg_action: PgAction) -> Option<(Channel, Message)> {
if pg_action == EPSILON {
None
} else {
let start = self.communications_pg_idxs[pg_id.0 as usize];
self.communications[start + ActionIdx::from(pg_action) as usize]
}
}
#[inline]
pub fn program_graphs(&self) -> &[ProgramGraph] {
&self.program_graphs
}
#[inline]
pub fn program_graph_ids(&self) -> impl Iterator<Item = PgId> {
(0..self.program_graphs.len()).map(|idx| PgId(idx as PgIndex))
}
#[inline]
pub fn channels(&self) -> &[(Vec<Type>, ChannelCapacity)] {
&self.channels
}
#[inline]
pub fn channel(&self, channel: Channel) -> Result<(&[Type], ChannelCapacity), CsError> {
self.channels
.get(channel.0 as usize)
.map(|(types, cap)| (types.as_slice(), *cap))
.ok_or(CsError::MissingChannel(channel))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder() {
let _cs: ChannelSystemBuilder = ChannelSystemBuilder::new();
}
#[test]
fn new_pg() {
let mut cs = ChannelSystemBuilder::new();
let _ = cs.new_program_graph();
}
#[test]
fn new_action() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let pg = cs.new_program_graph();
let _action = cs.new_action(pg)?;
Ok(())
}
#[test]
fn new_var() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let pg = cs.new_program_graph();
let _var1 = cs.new_var(pg, Val::from(false))?;
let _var2 = cs.new_var(pg, Val::from(0i64))?;
Ok(())
}
#[test]
fn add_effect() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let pg = cs.new_program_graph();
let action = cs.new_action(pg)?;
let var1 = cs.new_var(pg, Val::from(false))?;
let var2 = cs.new_var(pg, Val::from(0i64))?;
let effect_1 = CsExpression::from(2i64);
cs.add_effect(pg, action, var1, effect_1.clone())
.expect_err("type mismatch");
let effect_2 = CsExpression::from(true);
cs.add_effect(pg, action, var1, effect_2.clone())?;
cs.add_effect(pg, action, var2, effect_2)
.expect_err("type mismatch");
cs.add_effect(pg, action, var2, effect_1)?;
Ok(())
}
#[test]
fn new_location() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let pg = cs.new_program_graph();
let initial = cs.new_initial_location(pg)?;
let location = cs.new_location(pg)?;
assert_ne!(initial, location);
Ok(())
}
#[test]
fn add_transition() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let pg = cs.new_program_graph();
let initial = cs.new_initial_location(pg)?;
let action = cs.new_action(pg)?;
let var1 = cs.new_var(pg, Val::from(false))?;
let var2 = cs.new_var(pg, Val::from(0i64))?;
let effect_1 = CsExpression::from(0i64);
let effect_2 = CsExpression::from(true);
cs.add_effect(pg, action, var1, effect_2)?;
cs.add_effect(pg, action, var2, effect_1)?;
let post = cs.new_location(pg)?;
cs.add_transition(pg, initial, action, post, None)?;
Ok(())
}
#[test]
fn add_communication() -> Result<(), CsError> {
let mut cs = ChannelSystemBuilder::new();
let ch = cs.new_channel(vec![Type::Boolean], Some(1));
let pg1 = cs.new_program_graph();
let initial1 = cs.new_initial_location(pg1)?;
let post1 = cs.new_location(pg1)?;
let effect = CsExpression::from(true);
let send = cs.new_send(pg1, ch, vec![effect.clone()])?;
let _ = cs.new_send(pg1, ch, vec![effect])?;
cs.add_transition(pg1, initial1, send, post1, None)?;
let var1 = cs.new_var(pg1, Val::from(0i64))?;
let effect = CsExpression::from(0i64);
cs.add_effect(pg1, send, var1, effect)
.expect_err("send is a message so it cannot have effects");
let pg2 = cs.new_program_graph();
let initial2 = cs.new_initial_location(pg2)?;
let post2 = cs.new_location(pg2)?;
let var2 = cs.new_var(pg2, Val::from(false))?;
let receive = cs.new_receive(pg2, ch, vec![var2])?;
let _ = cs.new_receive(pg2, ch, vec![var2])?;
let _ = cs.new_receive(pg2, ch, vec![var2])?;
cs.add_transition(pg2, initial2, receive, post2, None)?;
let cs_def = cs.build();
let mut cs = cs_def.new_instance();
assert_eq!(cs.def().communications_pg_idxs, vec![0, 2, 5]);
cs.transition(pg1, send, &[post1])?;
cs.transition(pg2, receive, &[post2])?;
Ok(())
}
}