diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index 17a92a2..1a5e1da 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -49,8 +49,10 @@ match { } else { r"([[:alpha:]])([[:word:]])*" => WORD // r"(\p{L}|\p{Emoji})(\p{L}|\p{Emoji}|\p{Dash}|\p{N})*" => WORD, +// } else { +// r#"'([^']+)'"# => SEPARATOR, } else { - r#"".*""# => PATH, + r#""[^"]+""# => PATH, // " } else { _ } @@ -233,9 +235,11 @@ pub Experiment: (Vec, Vec) = { } - +// ~~~~~~~~~~~~~~~~~~~ // Instruction Parsing +// ~~~~~~~~~~~~~~~~~~~ +/// Decides whetherer to print to stdout or to save to file Helper_SO: presets::SaveOptions = { "Print" => presets::SaveOptions {print: true, save: None}, @@ -243,6 +247,7 @@ Helper_SO: presets::SaveOptions = { presets::SaveOptions {print: false, save: Some(vec![p])} } +/// we could need to save to multiple files SaveOptions: presets::SaveOptions = { > => { p.into_iter() @@ -251,7 +256,7 @@ SaveOptions: presets::SaveOptions = { } } - +/// Match for strings between nodes formatters LiteralSeparatorNode: presets::NodeDisplay = { PATH => presets::NodeDisplay::Separator( @@ -259,6 +264,7 @@ LiteralSeparatorNode: presets::NodeDisplay = { ) }; +/// Match for strings between edge formatters LiteralSeparatorEdge: presets::EdgeDisplay = { PATH => presets::EdgeDisplay::Separator( @@ -274,6 +280,7 @@ NodeDisplay: presets::NodeDisplay = { "Context" => presets::NodeDisplay::Display(graph::GraphMapNodes::Context) } +/// Node display formatters separated by arbitrary strings in quotes SeparatorNode: Vec = { => vec![v], )+> => @@ -306,6 +313,7 @@ EdgeDisplay: presets::EdgeDisplay = { "MaskEntitiesAdded" => presets::EdgeDisplay::Display(graph::GraphMapEdges::MaskEntitiesAdded{ mask }), } +/// Edge display formatters separated by arbitrary strings in quotes SeparatorEdge: Vec = { => vec![v], )+> => @@ -325,6 +333,10 @@ GraphSaveOptions: presets::GraphSaveOptions = { => presets::GraphSaveOptions::Dot { node_display: s_node, edge_display: s_edge, + node_color: graph::NodeColor { + conditionals: vec![], + base_color: "white".into() + }, so }, "GraphML" "|" ">" => diff --git a/src/rsprocess/graph.rs b/src/rsprocess/graph.rs index 233ffc2..1b4540b 100644 --- a/src/rsprocess/graph.rs +++ b/src/rsprocess/graph.rs @@ -4,7 +4,7 @@ use petgraph::{Graph, Directed}; use std::collections::HashMap; use super::structure::{RSlabel, RSsystem, RSset, RSprocess}; use super::support_structures::TransitionsIterator; -use super::translator; +use super::translator::{self, IdType}; use std::rc::Rc; type RSgraph = Graph; @@ -368,39 +368,176 @@ use petgraph::visit::{IntoNodeReferences, IntoEdgeReferences, EdgeRef}; type RSdotGraph = Graph; type RSformatNodeTy = - dyn Fn(&RSdotGraph, <&RSdotGraph as IntoNodeReferences>::NodeRef) -> String; + dyn Fn( + &RSdotGraph, + <&RSdotGraph as IntoNodeReferences>::NodeRef + ) -> Option; type RSformatEdgeTy = - dyn Fn(&RSdotGraph, <&RSdotGraph as IntoEdgeReferences>::EdgeRef) -> String; + dyn Fn( + &RSdotGraph, + <&RSdotGraph as IntoEdgeReferences>::EdgeRef + ) -> String; -pub fn default_node_formatter( - original_graph: Rc -) -> Box -{ - Box::new( - move |_g, n| - String::from( - match original_graph.node_weight(n.0).unwrap().context_process - { - RSprocess::Nill => - ", fillcolor=white", - RSprocess::RecursiveIdentifier { identifier: _ } => - ", fillcolor=\"#BBFF99\"", - RSprocess::EntitySet { entities: _, next_process: _ } => - ", fillcolor=\"#AAEEFF\"", - RSprocess::NondeterministicChoice { children: _ } => - ", fillcolor=\"#FFEE99\"", - RSprocess::Summation { children: _ } => - ", fillcolor=\"#CC99FF\"", - RSprocess::WaitEntity - { repeat: _, repeated_process: _, next_process: _ } => - ", fillcolor=\"#FF99AA\"", - } - ) - ) +#[derive(Clone, Copy)] +pub enum OperationType { + Equals, + Subset, + SubsetEqual, + Superset, + SupersetEqual } -pub fn default_edge_formatter( +impl OperationType { + pub fn evaluate(&self, a: &RSset, b: &RSset) -> bool { + match self { + Self::Equals => { + a.is_subset(b) && b.is_subset(a) + }, + Self::Subset => { + a.is_subset(b) && !b.is_subset(a) + }, + Self::SubsetEqual => { + a.is_subset(b) + }, + Self::Superset => { + b.is_subset(a) && !a.is_subset(b) + }, + Self::SupersetEqual => { + b.is_subset(a) + } + } + } +} + +#[derive(Clone)] +pub enum ContextColorConditional { + Nill, + RecursiveIdentifier(IdType), + EntitySet(OperationType, RSset), + NonDeterministicChoice, + Summation, + WaitEntity +} + + +#[derive(Clone)] +pub enum NodeColorConditional { + ContextConditional(ContextColorConditional), + EntitiesConditional(OperationType, RSset) +} + +#[derive(Clone)] +pub struct NodeColor { + pub conditionals: Vec<(NodeColorConditional, String)>, + pub base_color: String +} + +pub fn node_formatter( + original_graph: Rc, + rule: NodeColorConditional, + color: String, + star: Option, +) -> Box +{ + match rule { + NodeColorConditional::ContextConditional(ccc) => { + match ccc { + ContextColorConditional::Nill => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + if rssystem.context_process == RSprocess::Nill { + Some(", fillcolor=".to_string() + &color) + } else { + None + } + } + ) + }, + ContextColorConditional::RecursiveIdentifier(s) => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + match (Some(s) == star, &rssystem.context_process) { + (true, RSprocess::RecursiveIdentifier { identifier: _ }) => { + Some(", fillcolor=".to_string() + &color) + }, + (false, RSprocess::RecursiveIdentifier { identifier: id }) if id == &s => { + Some(", fillcolor=".to_string() + &color) + }, + _ => {None} + } + } + ) + }, + ContextColorConditional::EntitySet(ot, set) => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + match &rssystem.context_process { + RSprocess::EntitySet { entities, next_process: _ } if ot.evaluate(entities, &set) => { + Some(", fillcolor=".to_string() + &color) + }, + _ => {None} + } + } + ) + }, + ContextColorConditional::NonDeterministicChoice => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + if let RSprocess::NondeterministicChoice { children: _ } = rssystem.context_process { + Some(", fillcolor=".to_string() + &color) + } else { + None + } + } + ) + }, + ContextColorConditional::Summation => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + if let RSprocess::Summation { children: _ } = rssystem.context_process { + Some(", fillcolor=".to_string() + &color) + } else { + None + } + } + ) + }, + ContextColorConditional::WaitEntity => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + if let RSprocess::WaitEntity { repeat: _, repeated_process: _, next_process: _ } = &rssystem.context_process { + Some(", fillcolor=".to_string() + &color) + } else { + None + } + } + ) + }, + } + }, + NodeColorConditional::EntitiesConditional(ot, set) => { + Box::new( + move |_, n| { + let rssystem = original_graph.node_weight(n.0).unwrap(); + if ot.evaluate(&rssystem.available_entities, &set) { + Some(", fillcolor=".to_string() + &color) + } else { + None + } + } + ) + }, + } +} + +pub fn edge_formatter( original_graph: Rc ) -> Box { diff --git a/src/rsprocess/presets.rs b/src/rsprocess/presets.rs index a2e1699..a20b624 100644 --- a/src/rsprocess/presets.rs +++ b/src/rsprocess/presets.rs @@ -64,6 +64,7 @@ pub enum NodeDisplay { Display(graph::GraphMapNodes), } + #[derive(Clone)] pub enum EdgeDisplay { Separator(String), @@ -74,6 +75,7 @@ pub enum GraphSaveOptions { Dot { node_display: Vec, edge_display: Vec, + node_color: graph::NodeColor, so: SaveOptions, }, GraphML { @@ -195,10 +197,24 @@ where ParseError::UnrecognizedToken { token: (l, t, r), expected, - } => Err(format!( - "Unrecognized token \"{t}\" \ - between positions {l} and {r}. Expected: {expected:?}" - )), + } => { + let mut err = format!( + "Unrecognized token \"{t}\" \ + between positions {l} and {r}. Expected: " + ); + let mut it = expected.iter().peekable(); + while let Some(s) = it.next() { + err.push('('); + err.push_str(&s); + err.push(')'); + if it.peek().is_some() { + err.push(','); + err.push(' '); + } + + } + Err(err) + }, ParseError::User { error } => Err(error.to_string()), } } @@ -534,11 +550,37 @@ fn generate_edge_pringting_fn<'a>( accumulator } +use petgraph::visit::IntoNodeReferences; +#[allow(clippy::type_complexity)] +fn generate_node_color_fn<'a>( + node_color: &'a graph::NodeColor, + original_graph: Rc>, + translator: Rc, +) -> Box, <&Graph as IntoNodeReferences>::NodeRef) -> String + 'a> { + Box::new( + move |i, n| { + let cloned_node_color = node_color.clone(); + for (rule, color) in cloned_node_color.conditionals { + if let Some(s) = graph::node_formatter( + original_graph.clone(), + rule, + color, + translator.encode_not_mut("*") + )(i, n) { + return s + } + } + node_color.base_color.to_string() + } + ) +} + /// Writes the specified graph to a file in .dot format. pub fn dot( system: &EvaluatedSystem, node_display: Vec, edge_display: Vec, + node_color: &graph::NodeColor ) -> Result { match system { EvaluatedSystem::System { @@ -554,20 +596,20 @@ pub fn dot( generate_node_pringting_fn(&node_display, Rc::clone(&rc_translator)), generate_edge_pringting_fn(&edge_display, - rc_translator), + Rc::clone(&rc_translator)), ); let graph = Rc::new(graph.to_owned()); - let edge_formatter = - graph::default_edge_formatter(Rc::clone(&graph)); + // let edge_formatter = + // graph::default_edge_formatter(Rc::clone(&graph)); let node_formatter = - graph::default_node_formatter(Rc::clone(&graph)); + generate_node_color_fn(node_color, graph, rc_translator); let dot = rsdot::RSDot::with_attr_getters( &modified_graph, &[], - &edge_formatter, + &|_, _| String::new(), // &edge_formatter, &node_formatter, ); @@ -577,7 +619,11 @@ pub fn dot( } /// Writes the specified graph to a file in .graphml format. -pub fn graphml(system: &EvaluatedSystem) -> Result { +pub fn graphml( + system: &EvaluatedSystem, + node_display: Vec, + edge_display: Vec, +) -> Result { match system { EvaluatedSystem::System { sys: _, @@ -588,24 +634,10 @@ pub fn graphml(system: &EvaluatedSystem) -> Result { // map each value to the corresponding value we want to display let modified_graph = graph.map( - |id, node| { - graph::GraphMapNodesTy::from( - graph::GraphMapNodes::Entities, - Rc::clone(&rc_translator), - ) - .get()(id, node) - + "; " - + &graph::GraphMapNodesTy::from( - graph::GraphMapNodes::Context, - Rc::clone(&rc_translator), - ) - .get()(id, node) - }, - graph::GraphMapEdgesTy::from( - graph::GraphMapEdges::EntitiesAdded, - Rc::clone(&rc_translator), - ) - .get(), + generate_node_pringting_fn(&node_display, + Rc::clone(&rc_translator)), + generate_edge_pringting_fn(&edge_display, + rc_translator), ); use petgraph_graphml::GraphMl; @@ -726,16 +758,17 @@ fn execute( GraphSaveOptions::Dot { node_display: nd, edge_display: ed, + node_color: nc, so, } => { - save_options!(dot(system, nd, ed)?, so); + save_options!(dot(system, nd, ed, &nc)?, so); } GraphSaveOptions::GraphML { - node_display: _, - edge_display: _, + node_display: nd, + edge_display: ed, so, } => { - save_options!(graphml(system)?, so); + save_options!(graphml(system, nd, ed)?, so); } GraphSaveOptions::Serialize { path } => { serialize(system, path)?; diff --git a/src/rsprocess/translator.rs b/src/rsprocess/translator.rs index d598230..972c569 100644 --- a/src/rsprocess/translator.rs +++ b/src/rsprocess/translator.rs @@ -20,9 +20,9 @@ pub struct Translator { impl Translator { pub fn new() -> Self { Translator { - strings: HashMap::new(), - reverse: HashMap::new(), - last_id: 0, + strings: HashMap::from([("*".into(), 0)]), + reverse: HashMap::from([(0, "*".into())]), + last_id: 1, } } } diff --git a/testing/first.system b/testing/first.system index fc24bf4..760a9f3 100644 --- a/testing/first.system +++ b/testing/first.system @@ -4,4 +4,5 @@ Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)] Reactions: ([{a,b}, {c}, {b}]) -Digraph > Dot Entities | Context "; " MaskDifference {a, b} > Print \ No newline at end of file +Digraph > Dot Entities ", " Entities | Context "; " Difference > Print, +LimitFrequency("testing/first.experiment") > Print \ No newline at end of file