From 4b866b63da6f6d774a76b14a9306b6048d65e3c8 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 27 Jul 2025 19:08:27 +0200 Subject: [PATCH] Added way to mask common entities in labels --- src/rsprocess/grammar.lalrpop | 103 +++++++++++--- src/rsprocess/graph.rs | 261 ++++++++++++++++++++++++++-------- src/rsprocess/presets.rs | 30 ---- 3 files changed, 290 insertions(+), 104 deletions(-) diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index ac10de6..2fa9593 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -39,11 +39,17 @@ match { "Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency", "FastFrequency", "Digraph", "Bisimilarity", "Deserialize", - "Hide", "Entities", "MaskEntities", "MaskContext", - "Products", "MaskProducts", "Union", "MaskUnion", + "Hide", + "Entities", "MaskEntities", "UncommonEntities", "UncommonMaskEntities", + "MaskContext", "UncommonContext", "UncommonMaskContext", + "Products", "MaskProducts", "UncommonProducts", "UncommonMaskProducts", + "Union", "MaskUnion", "UncommonUnion", "UncommonMaskUnion", "Difference", "MaskDifference", + "UncommonDifference", "UncommonMaskDifference", "EntitiesDeleted", "MaskEntitiesDeleted", - "EntitiesAdded", "MaskEntitiesAdded" + "UncommonEntitiesDeleted", "UncommonMaskEntitiesDeleted", + "EntitiesAdded", "MaskEntitiesAdded", + "UncommonEntitiesAdded", "UncommonMaskEntitiesAdded", } else { r"[0-9]+" => NUMBER } else { @@ -320,34 +326,97 @@ SeparatorNode: graph::NodeDisplay = { EdgeDisplay: graph::EdgeDisplayBase = { "Hide" => graph::EdgeDisplayBase::Hide, + "Products" => - graph::EdgeDisplayBase::Products, + graph::EdgeDisplayBase::Products + { mask: None, filter_common: false }, "MaskProducts" => - graph::EdgeDisplayBase::MaskEntities{ mask }, + graph::EdgeDisplayBase::Entities + { mask: Some(mask), filter_common: false }, + "UncommonProducts" => + graph::EdgeDisplayBase::Products + { mask: None, filter_common: true }, + "UncommonMaskProducts" => + graph::EdgeDisplayBase::Entities + { mask: Some(mask), filter_common: true }, + "Entities" => - graph::EdgeDisplayBase::Entities, + graph::EdgeDisplayBase::Entities + { mask: None, filter_common: false }, "MaskEntities" => - graph::EdgeDisplayBase::MaskEntities{ mask }, + graph::EdgeDisplayBase::Entities + { mask: Some(mask), filter_common: false }, + "UncommonEntities" => + graph::EdgeDisplayBase::Entities + { mask: None, filter_common: true }, + "UncommonMaskEntities" => + graph::EdgeDisplayBase::Entities + { mask: Some(mask), filter_common: true }, + "Context" => - graph::EdgeDisplayBase::Context, + graph::EdgeDisplayBase::Context + { mask: None, filter_common: false }, "MaskContext" => - graph::EdgeDisplayBase::MaskContext{ mask }, + graph::EdgeDisplayBase::Context + { mask: Some(mask), filter_common: false }, + "UncommonContext" => + graph::EdgeDisplayBase::Context + { mask: None, filter_common: true }, + "UncommonMaskContext" => + graph::EdgeDisplayBase::Context + { mask: Some(mask), filter_common: true }, + "Union" => - graph::EdgeDisplayBase::Union, + graph::EdgeDisplayBase::Union + { mask: None, filter_common: false }, "MaskUnion" => - graph::EdgeDisplayBase::MaskUnion{ mask }, + graph::EdgeDisplayBase::Union + { mask: Some(mask), filter_common: false }, + "UncommonUnion" => + graph::EdgeDisplayBase::Union + { mask: None, filter_common: true }, + "UncommonMaskUnion" => + graph::EdgeDisplayBase::Union + { mask: Some(mask), filter_common: true }, + "Difference" => - graph::EdgeDisplayBase::Difference, + graph::EdgeDisplayBase::Difference + { mask: None, filter_common: false }, "MaskDifference" => - graph::EdgeDisplayBase::MaskDifference{ mask }, + graph::EdgeDisplayBase::Difference + { mask: Some(mask), filter_common: false }, + "UncommonDifference" => + graph::EdgeDisplayBase::Difference + { mask: None, filter_common: true }, + "UncommonMaskDifference" => + graph::EdgeDisplayBase::Difference + { mask: Some(mask), filter_common: true }, + "EntitiesDeleted" => - graph::EdgeDisplayBase::EntitiesDeleted, + graph::EdgeDisplayBase::EntitiesDeleted + { mask: None, filter_common: false }, "MaskEntitiesDeleted" => - graph::EdgeDisplayBase::MaskEntitiesDeleted{ mask }, + graph::EdgeDisplayBase::EntitiesDeleted + { mask: Some(mask), filter_common: false }, + "UncommonEntitiesDeleted" => + graph::EdgeDisplayBase::EntitiesDeleted + { mask: None, filter_common: true }, + "UncommonMaskEntitiesDeleted" => + graph::EdgeDisplayBase::EntitiesDeleted + { mask: Some(mask), filter_common: true }, + "EntitiesAdded" => - graph::EdgeDisplayBase::EntitiesAdded, + graph::EdgeDisplayBase::EntitiesAdded + { mask: None, filter_common: false }, "MaskEntitiesAdded" => - graph::EdgeDisplayBase::MaskEntitiesAdded{ mask }, + graph::EdgeDisplayBase::EntitiesAdded + { mask: Some(mask), filter_common: false }, + "UncommonEntitiesAdded" => + graph::EdgeDisplayBase::EntitiesAdded + { mask: None, filter_common: true }, + "UncommonMaskEntitiesAdded" => + graph::EdgeDisplayBase::EntitiesAdded + { mask: Some(mask), filter_common: true }, } /// Edge display formatters separated by arbitrary strings in quotes diff --git a/src/rsprocess/graph.rs b/src/rsprocess/graph.rs index cdf5689..73f2dab 100644 --- a/src/rsprocess/graph.rs +++ b/src/rsprocess/graph.rs @@ -39,7 +39,7 @@ pub fn digraph( } -pub fn common_entities(graph: &RSgraph) -> RSset { +fn common_system_entities(graph: &RSgraph) -> RSset { graph.node_references().fold( None, |acc, node| @@ -50,6 +50,71 @@ pub fn common_entities(graph: &RSgraph) -> RSset { ).unwrap_or(RSset::new()) } +macro_rules! common_label { + ( + $name:ident, + [$edge_name:ident, $acc_name:ident], + $empty_expr:expr, + $some_expr:expr + ) => { + fn $name(graph: &RSgraph) -> RSset { + graph.edge_references().fold( + None, + |$acc_name, $edge_name| { + let $edge_name = $edge_name.weight(); + match $acc_name { + None => Some($empty_expr), + Some($acc_name) => Some($some_expr) + } + } + ).unwrap_or(RSset::new()) + } + }; +} + +common_label!( + common_label_products, + [edge, acc], + edge.products.clone(), + edge.products.intersection(&acc) +); +common_label!( + common_label_entities, + [edge, acc], + edge.available_entities.clone(), + edge.available_entities.intersection(&acc) +); +common_label!( + common_label_context, + [edge, acc], + edge.context.clone(), + edge.context.intersection(&acc) +); +common_label!( + common_label_union, + [edge, acc], + edge.t.clone(), + edge.t.intersection(&acc) +); +common_label!( + common_label_difference, + [edge, acc], + edge.context.subtraction(&edge.available_entities), + edge.context.subtraction(&edge.available_entities).intersection(&acc) +); +common_label!( + common_label_entities_deleted, + [edge, acc], + edge.available_entities.subtraction(&edge.products), + edge.available_entities.subtraction(&edge.products).intersection(&acc) +); +common_label!( + common_label_entities_added, + [edge, acc], + edge.products.subtraction(&edge.available_entities), + edge.products.subtraction(&edge.available_entities).intersection(&acc) +); + // ----------------------------------------------------------------------------- // helper functions // ----------------------------------------------------------------------------- @@ -131,7 +196,7 @@ impl NodeDisplay { ) -> Box> { let common_entities = if self.contains_uncommon() { - Rc::new(common_entities(current_graph)) + Rc::new(common_system_entities(current_graph)) } else { Rc::new(RSset::new()) }; @@ -155,38 +220,41 @@ impl NodeDisplay { // Edges ----------------------------------------------------------------------- -/// Helper structure that specifies what information to display for edges #[derive(Clone)] pub enum EdgeDisplayBase { String { string: String }, Hide, - Products, - MaskProducts { mask: RSset }, - Entities, - MaskEntities { mask: RSset }, - Context, - MaskContext { mask: RSset }, - Union, - MaskUnion { mask: RSset }, - Difference, - MaskDifference { mask: RSset }, - EntitiesDeleted, - MaskEntitiesDeleted { mask: RSset }, - EntitiesAdded, - MaskEntitiesAdded { mask: RSset }, + Products { mask: Option, filter_common: bool }, + Entities { mask: Option, filter_common: bool }, + Context { mask: Option, filter_common: bool }, + Union { mask: Option, filter_common: bool }, + Difference { mask: Option, filter_common: bool }, + EntitiesDeleted { mask: Option, filter_common: bool }, + EntitiesAdded { mask: Option, filter_common: bool }, } pub struct EdgeDisplay { - pub base: Vec + pub base: Vec, } type GraphMapEdgesFnTy<'a> = dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a; +#[derive(Default, Clone)] +struct CommonEntities { + common_products: RSset, + common_entities: RSset, + common_context: RSset, + common_union: RSset, + common_difference: RSset, + common_entities_deleted: RSset, + common_entities_added: RSset, +} fn match_edge_display<'a>( base: &'a EdgeDisplayBase, - translator: Rc + translator: Rc, + common: CommonEntities ) -> Box> { use EdgeDisplayBase::*; use super::format_helpers::graph_map_edges_ty_from::*; @@ -198,65 +266,144 @@ fn match_edge_display<'a>( Hide => { format_hide(translator) }, - Products => { - format_products(translator) + Products { mask, filter_common } => { + if *filter_common { + format_products(translator, mask.clone(), + Some(common.common_products)) + } else { + format_products(translator, mask.clone(), None) + } }, - MaskProducts { mask } => { - format_mask_products(translator, mask.clone()) + Entities { mask, filter_common } => { + if *filter_common { + format_entities(translator, mask.clone(), + Some(common.common_entities)) + } else { + format_entities(translator, mask.clone(), None) + } }, - Entities => { - format_entities(translator) + Context { mask, filter_common } => { + if *filter_common { + format_context(translator, mask.clone(), + Some(common.common_context)) + } else { + format_context(translator, mask.clone(), None) + } }, - MaskEntities { mask } => { - format_mask_entities(translator, mask.clone()) + Union { mask, filter_common } => { + if *filter_common { + format_union(translator, mask.clone(), + Some(common.common_union)) + } else { + format_union(translator, mask.clone(), None) + } }, - Context => { - format_context(translator) + Difference { mask, filter_common } => { + if *filter_common { + format_difference(translator, mask.clone(), + Some(common.common_difference)) + } else { + format_difference(translator, mask.clone(), None) + } }, - MaskContext { mask } => { - format_mask_context(translator, mask.clone()) + EntitiesDeleted { mask, filter_common } => { + if *filter_common { + format_entities_deleted(translator, mask.clone(), + Some(common.common_entities_deleted)) + } else { + format_entities_deleted(translator, mask.clone(), None) + } }, - Union => { - format_union(translator) - }, - MaskUnion { mask } => { - format_mask_union(translator, mask.clone()) - }, - Difference => { - format_difference(translator) - }, - MaskDifference { mask } => { - format_mask_difference(translator, mask.clone()) - }, - EntitiesDeleted => { - format_entities_deleted(translator) - }, - MaskEntitiesDeleted { mask } => { - format_mask_entities_deleted(translator, mask.clone()) - }, - EntitiesAdded => { - format_entities_added(translator) - }, - MaskEntitiesAdded { mask } => { - format_mask_entities_added(translator, mask.clone()) + EntitiesAdded { mask, filter_common } => { + if *filter_common { + format_entities_added(translator, mask.clone(), + Some(common.common_entities_added)) + } else { + format_entities_added(translator, mask.clone(), None) + } }, } } +macro_rules! common_entity { + ($name:ident, $match:pat, $filter_common:ident) => { + fn $name(&self) -> bool { + self.base.iter().any( + |b| + if let $match = b { + *$filter_common + } else { + false + } + ) + } + }; +} + impl EdgeDisplay { + common_entity!(common_products, + EdgeDisplayBase::Products {mask: _, filter_common}, + filter_common); + common_entity!(common_entities, + EdgeDisplayBase::Entities {mask: _, filter_common}, + filter_common); + common_entity!(common_context, + EdgeDisplayBase::Context {mask: _, filter_common}, + filter_common); + common_entity!(common_union, + EdgeDisplayBase::Union {mask: _, filter_common}, + filter_common); + common_entity!(common_difference, + EdgeDisplayBase::Difference {mask: _, filter_common}, + filter_common); + common_entity!(common_entities_deleted, + EdgeDisplayBase::EntitiesDeleted {mask: _, filter_common}, + filter_common); + common_entity!(common_entities_added, + EdgeDisplayBase::EntitiesAdded {mask: _, filter_common}, + filter_common); + + pub fn generate<'a>( self, translator: Rc, - _current_graph: &RSgraph + current_graph: &RSgraph ) -> Box> { + // create the structure for common entities if required + let common = { + let mut tmp = CommonEntities::default(); + if self.common_products() { + tmp.common_products = common_label_products(current_graph); + } + if self.common_entities() { + tmp.common_entities = common_label_entities(current_graph); + } + if self.common_context() { + tmp.common_context = common_label_context(current_graph); + } + if self.common_union() { + tmp.common_union = common_label_union(current_graph); + } + if self.common_difference() { + tmp.common_difference = common_label_difference(current_graph); + } + if self.common_entities_deleted() { + tmp.common_entities_deleted = common_label_entities_deleted(current_graph); + } + if self.common_entities_added() { + tmp.common_entities_added = common_label_entities_added(current_graph); + } + tmp + }; + Box::new( move |i, n| { let mut accumulator = String::new(); for b in &self.base { let f = match_edge_display(b, - Rc::clone(&translator)); - + Rc::clone(&translator), + common.clone()); accumulator.push_str(&(f)(i, n)); } accumulator diff --git a/src/rsprocess/presets.rs b/src/rsprocess/presets.rs index e4be0c0..57a04d7 100644 --- a/src/rsprocess/presets.rs +++ b/src/rsprocess/presets.rs @@ -546,36 +546,6 @@ pub fn bisimilar( // Output Functions // ----------------------------------------------------------------------------- -// type GraphMapEdgesFnTy<'a> = -// dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a; -// fn generate_edge_printing_fn<'a>( -// edge_display: graph::EdgeDisplay, -// translator: Rc, -// ) -> Box> { -// // The type cannot be aliased since rust doesnt like generics. -// // We are iterating over the edge_display and constructing a function -// // (accumulator) that prints out our formatted nodes. So at each step we -// // call the previous function and add the next string or function. -// let edge_display = edge_display.iter().map( -// |e| { -// match e { -// EdgeDisplay::Display(d) => { -// d.clone() -// }, -// EdgeDisplay::Separator(s) => { -// graph::EdgeDisplayBase::String { string: s.clone() } -// } -// } -// } -// ).collect::>(); - -// let gmet = graph::GraphMapEdgesTy::from( -// (edge_display, Rc::clone(&translator)) -// ); - -// gmet.generate() -// } - /// Writes the specified graph to a file in .dot format. pub fn dot( system: &EvaluatedSystem,