From d458787a81509a0fec7b44b5470d76c3336aecc6 Mon Sep 17 00:00:00 2001 From: elvis Date: Tue, 16 Sep 2025 23:09:20 +0200 Subject: [PATCH] custom grammar errors, better handling of user facing errors fuckery for modules in grammar, maybe fixable? --- analysis/src/helper.rs | 141 ++++--- execution/src/presets.rs | 690 +++++++++++++++++++-------------- grammar/src/custom_error.rs | 25 ++ grammar/src/grammar.lalrpop | 49 ++- grammar/src/lib.rs | 6 + rsprocess/src/system.rs | 46 +++ testing/examples/run.system | 4 +- testing/examples/target.system | 8 +- 8 files changed, 624 insertions(+), 345 deletions(-) create mode 100644 grammar/src/custom_error.rs diff --git a/analysis/src/helper.rs b/analysis/src/helper.rs index 9e33a8e..aacbfff 100644 --- a/analysis/src/helper.rs +++ b/analysis/src/helper.rs @@ -2,6 +2,7 @@ use execution::presets; use lalrpop_util::ParseError; use std::fmt::Display; use grammar::grammar; +use ::grammar::user_error::{UserError, UserErrorTypes}; pub struct Parsers {} @@ -27,37 +28,41 @@ impl presets::FileParsers for Parsers { } } -fn reformat_error( - e: ParseError, +fn create_error( input_str: &str, + l: usize, + t: T, + r: usize, + expected: Option>, + error: Option, ) -> Result where T: Display, { - match e { - | ParseError::ExtraToken { token: (l, t, r) } => Err(format!( - "Unexpected token \"{t}\" between positions {l} and {r}." - )), - | ParseError::UnrecognizedEof { - location: _, - expected: _, - } => Err("End of file encountered while parsing.".into()), - | ParseError::InvalidToken { location } => - Err(format!("Invalid token at position {location}.")), - | ParseError::UnrecognizedToken { - token: (l, t, r), - expected, - } => { - use colored::Colorize; + use colored::Colorize; - let mut err = format!( + let mut err = { + if let Some(error) = error { + format!( + "{error} {}{}{} \ + between positions {l} and {r}.", + "\"".red(), + t.to_string().red(), + "\"".red(), + ) + } else { + format!( "Unrecognized token {}{}{} \ between positions {l} and {r}.", "\"".red(), t.to_string().red(), "\"".red(), - ); + ) + } + }; + { + if let Some(expected) = expected { // Temporary debug. err.push_str("\nExpected: "); let mut it = expected.iter().peekable(); @@ -70,44 +75,74 @@ where err.push(' '); } } - let right_new_line = input_str[l..] - .find("\n") - .map(|pos| pos + l) - .unwrap_or(input_str.len()); - let left_new_line = input_str[..r] - .rfind("\n") - .map(|pos| pos + 1) - .unwrap_or_default(); + } + } + let right_new_line = input_str[l..] + .find("\n") + .map(|pos| pos + l) + .unwrap_or(input_str.len()); + let left_new_line = input_str[..r] + .rfind("\n") + .map(|pos| pos + 1) + .unwrap_or_default(); - let line_number = input_str[..l].match_indices('\n').count() + 1; - let pre_no_color = format!("{line_number} |"); - let pre = format!("{}", pre_no_color.blue()); + let line_number = input_str[..l].match_indices('\n').count() + 1; + let pre_no_color = format!("{line_number} |"); + let pre = format!("{}", pre_no_color.blue()); - let line_pos_l = l - left_new_line; - let line_pos_r = r - left_new_line; + let line_pos_l = l - left_new_line; + let line_pos_r = r - left_new_line; - err.push_str(&format!( - "\nLine {} position {} to {}:\n{}{}{}{}", - line_number, - line_pos_l, - line_pos_r, - &pre, - &input_str[left_new_line..l].green(), - &input_str[l..r].red(), - &input_str[r..right_new_line], - )); - err.push('\n'); - err.push_str(&" ".repeat(pre_no_color.len() - 1)); - err.push_str(&format!("{}", "|".blue())); - err.push_str(&" ".repeat(l - left_new_line)); - err.push_str(&format!("{}", &"↑".red())); - if r - l > 2 { - err.push_str(&" ".repeat(r - l - 2)); - err.push_str(&format!("{}", &"↑".red())); - } + err.push_str(&format!( + "\nLine {} position {} to {}:\n{}{}{}{}", + line_number, + line_pos_l, + line_pos_r, + &pre, + &input_str[left_new_line..l].green(), + &input_str[l..r].red(), + &input_str[r..right_new_line], + )); + err.push('\n'); + err.push_str(&" ".repeat(pre_no_color.len() - 1)); + err.push_str(&format!("{}", "|".blue())); + err.push_str(&" ".repeat(l - left_new_line)); + err.push_str(&format!("{}", &"↑".red())); + if r - l > 2 { + err.push_str(&" ".repeat(r - l - 2)); + err.push_str(&format!("{}", &"↑".red())); + } - Err(err) + Err(err) +} + +fn reformat_error( + e: ParseError, + input_str: &str, +) -> Result +where + T: Display, +{ + match e { + | ParseError::ExtraToken { token: (l, t, r) } => + Err(format!("Unexpected extra token \"{t}\" between positions {l} \ + and {r}.")), + | ParseError::UnrecognizedEof { + location: _, + expected: _, + } => Err("End of file encountered while parsing.".into()), + | ParseError::InvalidToken { location } => + Err(format!("Invalid token at position {location}.")), + | ParseError::UnrecognizedToken { + token: (l, t, r), + expected, + } => { + create_error(input_str, l, t, r, Some(expected), None) + }, + | ParseError::User { + error: UserError { token: (l, t, r), error } + } => { + create_error(input_str, l, t, r, None, Some(error)) }, - | ParseError::User { error } => Err(error.to_string()), } } diff --git a/execution/src/presets.rs b/execution/src/presets.rs index bf9ac04..9e94593 100644 --- a/execution/src/presets.rs +++ b/execution/src/presets.rs @@ -92,9 +92,11 @@ pub enum Instruction { }, Target { so: SaveOptions, + limit: Option, }, Run { so: SaveOptions, + limit: Option, }, Loop { symbol: String, @@ -132,43 +134,45 @@ pub enum System { impl System { /// Deserialize the graph if applicable. pub fn compute( - &self, + self, translator: Translator, ) -> Result { match self { - | Self::System { sys } => Ok(EvaluatedSystem::System { - sys: sys.to_owned(), - translator, - }), + | Self::System { sys } => + Ok(EvaluatedSystem::from_sys(sys, translator)), | Self::Deserialize { path } => { - let (graph, translator) = deserialize(path.into())?; - Ok(EvaluatedSystem::Graph { graph, translator }) + let (graph, translator) = deserialize(path)?; + Ok(EvaluatedSystem::from_graph(graph, translator)) }, } } } #[derive(Clone)] -pub enum EvaluatedSystem { - Graph { - graph: graph::SystemGraph, - translator: Translator, - }, - System { - sys: system::System, - translator: Translator, - }, +pub struct EvaluatedSystem { + sys: Option, + graph: Option, + positive: Option, + translator: Translator, } impl EvaluatedSystem { pub fn get_translator(&mut self) -> &mut Translator { - match self { - | EvaluatedSystem::Graph { - graph: _, - translator, - } => translator, - | EvaluatedSystem::System { sys: _, translator } => translator, - } + &mut self.translator + } + + pub fn from_graph( + graph: graph::SystemGraph, + translator: Translator + ) -> Self { + Self { sys: None, graph: Some(graph), positive: None, translator } + } + + pub fn from_sys( + sys: system::System, + translator: Translator, + ) -> Self { + Self { sys: Some(sys), graph: None, positive: None, translator } } } @@ -248,65 +252,127 @@ fn save_file(contents: &String, path_string: String) -> Result<(), String> { /// Prints statistics of the system. /// Equivalent main_do(stat) or main_do(stat, MissingE) pub fn stats(system: &EvaluatedSystem) -> Result { - match system { - | EvaluatedSystem::System { sys, translator } => - Ok(sys.statistics(translator)), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - Ok(sys.statistics(translator)) - }, + if let Some(sys) = &system.sys { + Ok(sys.statistics(&system.translator)) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; + Ok(sys.statistics(&system.translator)) + } else { + Err("Statistics not available for supplied system.".into()) } } /// Prints a final set of entities in a terminating Reaction System. /// The system needs to terminate to return. /// Equivalent to main_do(target, E) -pub fn target(system: &EvaluatedSystem) -> Result { - let (res, translator) = match system { - | EvaluatedSystem::System { sys, translator } => - (sys.target()?, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - (sys.target()?, translator) - }, - }; - Ok(format!( - "After {} steps we arrive at state:\n{}", - res.0, - translator::Formatter::from(translator, &res.1) - )) +pub fn target( + system: &EvaluatedSystem, + limit: Option +) -> Result { + if let Some(sys) = &system.sys { + let res = if let Some(limit) = limit { + sys.target_limit(limit)? + } else { + sys.target()? + }; + Ok(format!( + "After {} steps we arrive at state:\n{}", + res.0, + translator::Formatter::from(&system.translator, &res.1) + )) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; + let res = if let Some(limit) = limit { + sys.target_limit(limit)? + } else { + sys.target()? + }; + Ok(format!( + "After {} steps we arrive at state:\n{}", + res.0, + translator::Formatter::from(&system.translator, &res.1) + )) + } else if let Some(positive) = &system.positive { + let res = if let Some(limit) = limit { + positive.target_limit(limit)? + } else { + positive.target()? + }; + Ok(format!( + "After {} steps we arrive at state:\n{}", + res.0, + translator::Formatter::from(&system.translator, &res.1) + )) + } else { + Err("Target not available for supplied system.".into()) + } } /// Finds the list of traversed states in a (deterministic) terminating /// reaction. /// The system needs to terminate to return. /// equivalent to main_do(run,Es) -pub fn traversed(system: &EvaluatedSystem) -> Result { - let (res, translator) = match system { - | EvaluatedSystem::System { sys, translator } => - (sys.run_separated()?, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - (sys.run_separated()?, translator) - }, - }; - +pub fn traversed( + system: &EvaluatedSystem, + limit: Option +) -> Result { let mut output = String::new(); - output.push_str("The trace is composed by the set of entities: "); - for (e, _c, _t) in res { - output.push_str(&format!( - "{}", - translator::Formatter::from(translator, &e) - )); + if let Some(sys) = &system.sys { + let res = if let Some(limit) = limit { + sys.run_separated_limit(limit)? + } else { + sys.run_separated()? + }; + + output.push_str("The trace is composed by the set of entities: "); + for (e, _c, _t) in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } + Ok(output) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; + let res = if let Some(limit) = limit { + sys.run_separated_limit(limit)? + } else { + sys.run_separated()? + }; + + output.push_str("The trace is composed by the set of entities: "); + for (e, _c, _t) in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } + Ok(output) + } else if let Some(positive) = &system.positive { + let res = if let Some(limit) = limit { + positive.run_separated_limit(limit)? + } else { + positive.run_separated()? + }; + + output.push_str("The trace is composed by the set of entities: "); + for (e, _c, _t) in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } + Ok(output) + } else { + Err("Run not available for supplied system.".into()) } - Ok(output) } /// Finds the looping list of states in a reaction system with a perpetual @@ -318,38 +384,68 @@ pub fn hoop( symbol: String, ) -> Result { use system::LoopSystem; - - let (res, translator) = match system { - | EvaluatedSystem::System { sys, translator } => (sys, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - (sys, translator) - }, - }; - // we retrieve the id for "x" and use it to find the corresponding loop - let Some(id) = translator.encode_not_mut(&symbol) else { + // we retrieve the id for the input symbol, error if not found. + let Some(id) = system.translator.encode_not_mut(&symbol) else { return Err(format!("Symbol {symbol} not found.")); }; - let res = match res.lollipops_only_loop_named(id) { - | Some(o) => o, - | None => { - return Err("No loop found.".into()); - }, - }; - let mut output = String::new(); - output.push_str("The loop is composed by the sets: "); - for e in res { - output.push_str(&format!( - "{}", - translator::Formatter::from(translator, &e) - )); - } + if let Some(sys) = &system.sys { + let res = match sys.lollipops_only_loop_named(id) { + | Some(o) => o, + | None => { + return Err("No loop found.".into()); + }, + }; + output.push_str("The loop is composed by the sets: "); + for e in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } - Ok(output) + Ok(output) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; + let res = match sys.lollipops_only_loop_named(id) { + | Some(o) => o, + | None => { + return Err("No loop found.".into()); + }, + }; + + output.push_str("The loop is composed by the sets: "); + for e in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } + + Ok(output) + } else if let Some(positive) = &system.positive { + let res = match positive.lollipops_only_loop_named(id) { + | Some(o) => o, + | None => { + return Err("No loop found.".into()); + }, + }; + + output.push_str("The loop is composed by the sets: "); + for e in res { + output.push_str(&format!( + "{}", + translator::Formatter::from(&system.translator, &e) + )); + } + + Ok(output) + } else { + Err("Loop not available for supplied system.".into()) + } } /// Finds the frequency of each entity in the traversed states for a @@ -358,22 +454,33 @@ pub fn hoop( pub fn freq(system: &EvaluatedSystem) -> Result { use frequency::BasicFrequency; - let (sys, translator) = match system { - | EvaluatedSystem::System { sys, translator } => (sys, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - (sys, translator) - }, - }; + if let Some(sys) = &system.sys { + let res = frequency::Frequency::naive_frequency(sys)?; - let res = frequency::Frequency::naive_frequency(sys)?; + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; + let res = frequency::Frequency::naive_frequency(sys)?; - Ok(format!( - "Frequency of encountered symbols:\n{}", - translator::Formatter::from(translator, &res) - )) + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(positive) = &system.positive { + let res = frequency::PositiveFrequency::naive_frequency(positive)?; + + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else { + Err("Frequency not available for supplied system.".into()) + } } /// Finds the frequency of each entity in the limit loop of a nonterminating @@ -389,33 +496,65 @@ where { use frequency::BasicFrequency; - let (sys, translator): (&system::System, &mut Translator) = match system { - | EvaluatedSystem::System { sys, translator } => (sys, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph.".into()); - }; - (sys, translator) - }, - }; + let (_, sets) = + read_file(&mut system.translator, experiment, parser_experiment)?; - let (_, sets) = read_file(translator, experiment, parser_experiment)?; + if let Some(sys) = &system.sys { + let res = match frequency::Frequency::limit_frequency( + &sets, + &sys.reaction_rules, + &sys.available_entities, + ) { + | Some(e) => e, + | None => { + return Err("Error calculating frequency.".into()); + }, + }; - let res = match frequency::Frequency::limit_frequency( - &sets, - &sys.reaction_rules, - &sys.available_entities, - ) { - | Some(e) => e, - | None => { - return Err("Error calculating frequency.".into()); - }, - }; + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph.".into()); + }; - Ok(format!( - "Frequency of encountered symbols:\n{}", - translator::Formatter::from(translator, &res) - )) + let res = match frequency::Frequency::limit_frequency( + &sets, + &sys.reaction_rules, + &sys.available_entities, + ) { + | Some(e) => e, + | None => { + return Err("Error calculating frequency.".into()); + }, + }; + + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(_positive) = &system.positive { + todo!() + // let res = match frequency::PositiveFrequency::limit_frequency( + // &sets, + // &positive.reaction_rules, + // &positive.available_entities, + // ) { + // | Some(e) => e, + // | None => { + // return Err("Error calculating frequency.".into()); + // }, + // }; + + // Ok(format!( + // "Frequency of encountered symbols:\n{}", + // translator::Formatter::from(&system.translator, &res) + // )) + } else { + Err("LimitFrequency not available for supplied system.".into()) + } } /// Finds the frequency of each entity in the traversed loops of a terminating @@ -433,48 +572,69 @@ where { use frequency::BasicFrequency; - let (sys, translator): (&system::System, &mut Translator) = match system { - | EvaluatedSystem::System { sys, translator } => (sys, translator), - | EvaluatedSystem::Graph { graph, translator } => { - let Some(sys) = graph.node_weights().next() else { - return Err("No node found in graph".into()); - }; - (sys, translator) - }, - }; + let (weights, sets) = + read_file(&mut system.translator, experiment, parser_experiment)?; - let (weights, sets) = read_file(translator, experiment, parser_experiment)?; + if let Some(sys) = &system.sys { + let res = match frequency::Frequency::fast_frequency( + &sets, + &sys.reaction_rules, + &sys.available_entities, + &weights, + ) { + | Some(e) => e, + | None => { + return Err("Error calculating frequency.".into()); + }, + }; - let res = match frequency::Frequency::fast_frequency( - &sets, - &sys.reaction_rules, - &sys.available_entities, - &weights, - ) { - | Some(e) => e, - | None => { - return Err("Error calculating frequency.".into()); - }, - }; + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(graph) = &system.graph { + let Some(sys) = graph.node_weights().next() else { + return Err("No node found in graph".into()); + }; - Ok(format!( - "Frequency of encountered symbols:\n{}", - translator::Formatter::from(translator, &res) - )) + let res = match frequency::Frequency::fast_frequency( + &sets, + &sys.reaction_rules, + &sys.available_entities, + &weights, + ) { + | Some(e) => e, + | None => { + return Err("Error calculating frequency.".into()); + }, + }; + + Ok(format!( + "Frequency of encountered symbols:\n{}", + translator::Formatter::from(&system.translator, &res) + )) + } else if let Some(_positive) = &system.positive { + todo!() + } else { + Err("FastFrequency not available for supplied system.".into()) + } } /// Computes the LTS. /// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs) pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> { - if let EvaluatedSystem::System { sys, translator } = system { - *system = EvaluatedSystem::Graph { - graph: sys.digraph()?, - translator: translator.to_owned(), - }; + if let Some(sys) = &system.sys && system.graph.is_none() { + let graph = sys.digraph()?; + system.graph = Some(graph); + } else if let Some(positive) = &system.positive && system.graph.is_none() { + let _graph = positive.digraph()?; + todo!() } Ok(()) } +/// Given a graph and a function that identifies nodes of the graph, merges +/// nodes with the same identifier. pub fn grouping( system: &mut EvaluatedSystem, group: &assert::grouping::Assert, @@ -482,9 +642,9 @@ pub fn grouping( let mut buckets = HashMap::new(); let mut leader: HashMap = HashMap::new(); - if let EvaluatedSystem::Graph { graph, translator } = system { + if let Some(graph) = &mut system.graph { for node in graph.node_indices() { - let val = group.execute(graph, &node, translator)?; + let val = group.execute(graph, &node, &mut system.translator)?; println!("node: {node:?} -> val: {val:?}"); buckets.entry(val.clone()).or_insert(vec![]).push(node); let l = buckets.get(&val).unwrap().first().unwrap(); @@ -541,59 +701,37 @@ where { use assert::relabel::AssertReturnValue; - let system_b = read_file( - system_a.get_translator(), - system_b.to_string(), - parser_instructions, - )?; - let mut system_b = match system_b + let system_b = read_file(&mut system_a.translator, + system_b.to_string(), + parser_instructions)?; + + let mut system_b = system_b .system - .compute(system_a.get_translator().clone())? - { - | EvaluatedSystem::System { sys, translator } => - EvaluatedSystem::System { sys, translator }, - | EvaluatedSystem::Graph { graph, translator } => { - if translator != *system_a.get_translator() { - return Err("Bisimilarity not implemented for systems with \ - different encodings. Serialize the systems \ - with the same translator." - .into()); - } - EvaluatedSystem::Graph { graph, translator } - }, - }; + .compute(system_a.get_translator().clone())?; + + if system_b.translator != system_a.translator { + return Err("Bisimilarity not implemented for systems with different \ + encodings. Serialize the systems with the same translator." + .into()); + } digraph(system_a)?; digraph(&mut system_b)?; - // since we ran digraph on both they have to be graphs - match (system_a, &mut system_b) { - | ( - EvaluatedSystem::Graph { - graph: a, - translator: _, - }, - EvaluatedSystem::Graph { - graph: b, - translator: translator_b, - }, - ) => { - let a: Graph = - a.map_edges(edge_relabeler, translator_b)?; - let b: Graph = - b.map_edges(edge_relabeler, translator_b)?; - Ok(format!( - "{}", - // bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(& - // &a, &&b) - // bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b) - bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b) - )) - }, - | _ => { - unreachable!() - }, - } + // since we ran digraph on both they have to have valid graphs + + let a: Graph = + system_a.graph.as_ref().unwrap() + .map_edges(edge_relabeler, &mut system_a.translator)?; + let b: Graph = + system_b.graph.unwrap().map_edges(edge_relabeler, &mut system_b.translator)?; + Ok(format!( + "{}", + // bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(& + // &a, &&b) + // bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b) + bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b) + )) } // ----------------------------------------------------------------------------- @@ -608,33 +746,29 @@ pub fn dot( node_color: graph::NodeColor, edge_color: graph::EdgeColor, ) -> Result { - match system { - | EvaluatedSystem::System { - sys: _, - translator: _, - } => Err("Supplied system is not a graph".into()), - | EvaluatedSystem::Graph { graph, translator } => { - let rc_translator = Rc::new(translator.clone()); - let modified_graph = graph.map( - node_display.generate(Rc::clone(&rc_translator), graph), - edge_display.generate(Rc::clone(&rc_translator), graph), - ); + if let Some(graph) = &system.graph { + let rc_translator = Rc::new(system.translator.clone()); + let modified_graph = graph.map( + node_display.generate(Rc::clone(&rc_translator), graph), + edge_display.generate(Rc::clone(&rc_translator), graph), + ); - let graph = Rc::new(graph.to_owned()); + let graph = Rc::new(graph.to_owned()); - let node_formatter = node_color - .generate(Rc::clone(&graph), translator.encode_not_mut("*")); - let edge_formatter = edge_color.generate(Rc::clone(&graph)); + let node_formatter = node_color + .generate(Rc::clone(&graph), system.translator.encode_not_mut("*")); + let edge_formatter = edge_color.generate(Rc::clone(&graph)); - let dot = dot::Dot::with_attr_getters( - &modified_graph, - &[], - &edge_formatter, - &node_formatter, - ); + let dot = dot::Dot::with_attr_getters( + &modified_graph, + &[], + &edge_formatter, + &node_formatter, + ); - Ok(format!("{dot}")) - }, + Ok(format!("{dot}")) + } else { + Err("Supplied system is not a graph".into()) } } @@ -644,60 +778,52 @@ pub fn graphml( node_display: graph::NodeDisplay, edge_display: graph::EdgeDisplay, ) -> Result { - match system { - | EvaluatedSystem::System { - sys: _, - translator: _, - } => Err("Supplied system is not a graph".into()), - | EvaluatedSystem::Graph { graph, translator } => { - let rc_translator = Rc::new(translator.to_owned()); + if let Some(graph) = &system.graph { + let rc_translator = Rc::new(system.translator.to_owned()); - // map each value to the corresponding value we want to display - let modified_graph = graph.map( - node_display.generate(Rc::clone(&rc_translator), graph), - edge_display.generate(rc_translator, graph), - ); + // map each value to the corresponding value we want to display + let modified_graph = graph.map( + node_display.generate(Rc::clone(&rc_translator), graph), + edge_display.generate(rc_translator, graph), + ); - use petgraph_graphml::GraphMl; - let graphml = GraphMl::new(&modified_graph) - .pretty_print(true) - .export_node_weights_display() - .export_edge_weights_display(); + use petgraph_graphml::GraphMl; + let graphml = GraphMl::new(&modified_graph) + .pretty_print(true) + .export_node_weights_display() + .export_edge_weights_display(); - Ok(format!("{graphml}")) - }, + Ok(format!("{graphml}")) + } else { + Err("Supplied system is not a graph".into()) } } -/// Writes the specified graph, translator tuple to file. +/// Writes the specified graph and translator to file. /// N.B. graph size in memory might be much larger after serialization and /// deserialization. pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> { - match system { - | EvaluatedSystem::System { - sys: _, - translator: _, - } => Err("Supplied system is not a graph".into()), - | EvaluatedSystem::Graph { graph, translator } => { - // relative path - let mut path = std::path::PathBuf::from(path); - path.set_extension("cbor"); + if let Some(graph) = &system.graph { + // relative path + let mut path = std::path::PathBuf::from(path); + path.set_extension("cbor"); - let f = match fs::File::create(&path) { - | Ok(f) => f, - | Err(_) => { - return Err(format!( - "Error creating file {}.", - path.to_str().unwrap() - )); - }, - }; + let f = match fs::File::create(&path) { + | Ok(f) => f, + | Err(_) => { + return Err(format!( + "Error creating file {}.", + path.to_str().unwrap() + )); + }, + }; - match serialize::ser(f, graph, translator) { - | Ok(_) => Ok(()), - | Err(_) => Err("Error during serialization.".into()), - } - }, + match serialize::ser(f, graph, &system.translator) { + | Ok(_) => Ok(()), + | Err(_) => Err("Error during serialization.".into()), + } + } else { + Err("Supplied system is not a graph".into()) } } @@ -758,11 +884,11 @@ fn execute( | Instruction::Stats { so } => { save_options!(stats(system)?, so); }, - | Instruction::Target { so } => { - save_options!(target(system)?, so); + | Instruction::Target { so, limit } => { + save_options!(target(system, limit)?, so); }, - | Instruction::Run { so } => { - save_options!(traversed(system)?, so); + | Instruction::Run { so, limit } => { + save_options!(traversed(system, limit)?, so); }, | Instruction::Loop { symbol, so } => { save_options!(hoop(system, symbol)?, so); diff --git a/grammar/src/custom_error.rs b/grammar/src/custom_error.rs new file mode 100644 index 0000000..969c47f --- /dev/null +++ b/grammar/src/custom_error.rs @@ -0,0 +1,25 @@ +use std::fmt::Display; + +pub enum UserErrorTypes { + NumberTooBigUsize, + NumberTooBigi64, +} + +impl Display for UserErrorTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NumberTooBigUsize => + write!(f, "Specified number is too big (greater than {})", + usize::MAX), + Self::NumberTooBigi64 => + write!(f, "Specified number is too big (lesser than {} or \ + greater than {})", + i64::MIN, i64::MAX), + } + } +} + +pub struct UserError { + pub token: (usize, String, usize), + pub error: UserErrorTypes, +} diff --git a/grammar/src/grammar.lalrpop b/grammar/src/grammar.lalrpop index ba370af..273efa7 100644 --- a/grammar/src/grammar.lalrpop +++ b/grammar/src/grammar.lalrpop @@ -8,9 +8,14 @@ use rsprocess::element::IdType; use rsprocess::translator::Translator; use execution::presets; use rsprocess::graph; +use crate::custom_error; grammar(translator: &mut Translator); +extern { + type Error = custom_error::UserError; +} + // ----------------------------------------------------------------------------- // Helpers // ----------------------------------------------------------------------------- @@ -31,7 +36,7 @@ match { "Print", "Save", "Dot", "GraphML", "Serialize", "Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency", - "FastFrequency", "Digraph", "Bisimilarity", + "FastFrequency", "Digraph", "Bisimilarity", "Limit", "Deserialize", "?", "Hide", @@ -160,10 +165,38 @@ LiteralProcess: String = { "relabel" => <>.into(), }; -// all numbers are i64 Num: i64 = { - NUMBER =>? i64::from_str(<>) - .map_err(|_| ParseError::User { error: "Number is too big" }) + =>? { + if sign.is_some() { + i64::from_str(n) + .map(|n| -n) + .map_err(|_| ParseError::User { + error: custom_error::UserError { + token: (start, n.into(), end), + error: custom_error::UserErrorTypes::NumberTooBigi64 + } + }) + } else { + i64::from_str(n) + .map_err(|_| ParseError::User { + error: custom_error::UserError { + token: (start, n.into(), end), + error: custom_error::UserErrorTypes::NumberTooBigi64 + } + }) + } + + } +}; + +NumUsize: usize = { + =>? usize::from_str(n) + .map_err(|_| ParseError::User { + error: custom_error::UserError { + token: (start, n.into(), end), + error: custom_error::UserErrorTypes::NumberTooBigUsize + } + }) }; Path: String = { @@ -1037,12 +1070,12 @@ Instruction: presets::Instruction = { "Stats" ">" => presets::Instruction::Stats { so }, - "Target" + "Target" ">" => - presets::Instruction::Target { so }, - "Run" + presets::Instruction::Target { so, limit: limit.map(|l| l.3) }, + "Run" ">" => - presets::Instruction::Run { so }, + presets::Instruction::Run { so, limit: limit.map(|l| l.3) }, "Loop" "(" ")" ">" => presets::Instruction::Loop { symbol, so }, diff --git a/grammar/src/lib.rs b/grammar/src/lib.rs index 343b94a..5e25530 100644 --- a/grammar/src/lib.rs +++ b/grammar/src/lib.rs @@ -1,3 +1,9 @@ +mod custom_error; + +pub mod user_error { + pub use crate::custom_error::*; +} + lalrpop_util::lalrpop_mod!( #[allow(clippy::uninlined_format_args)] pub grammar, // name of module "/grammar.rs" // location of parser diff --git a/rsprocess/src/system.rs b/rsprocess/src/system.rs index 67cbd4c..bd4c883 100644 --- a/rsprocess/src/system.rs +++ b/rsprocess/src/system.rs @@ -73,11 +73,19 @@ pub trait ExtensionsSystem: BasicSystem { fn target(&self) -> Result<(i64, Self::Set), String>; + fn target_limit(&self, limit: usize) -> Result<(i64, Self::Set), String>; + #[allow(clippy::type_complexity)] fn run_separated( &self, ) -> Result, String>; + #[allow(clippy::type_complexity)] + fn run_separated_limit( + &self, + limit: usize + ) -> Result, String>; + fn traces(self, n: usize) -> Result>, String>; } @@ -162,6 +170,20 @@ impl ExtensionsSystem for T { Ok((n, current.available_entities().clone())) } + fn target_limit(&self, limit: usize) -> Result<(i64, Self::Set), String> { + let current = self.one_transition()?; + if current.is_none() { + return Ok((0, self.available_entities().clone())); + } + let mut n = 1; + let mut current = current.unwrap().1; + while let Some((_, next)) = current.one_transition()? && n < limit { + current = next; + n += 1; + } + Ok((n as i64, current.available_entities().clone())) + } + /// see smartOneRunECT, smartRunECT fn run_separated( &self, @@ -183,6 +205,30 @@ impl ExtensionsSystem for T { Ok(res) } + /// see smartOneRunECT, smartRunECT + fn run_separated_limit( + &self, + limit: usize + ) -> Result, String> { + let mut limit = limit; + let mut res = vec![]; + let current = self.one_transition()?; + if current.is_none() { + return Ok(res); + } + let current = current.unwrap(); + let (available_entities, context, t) = current.0.get_context(); + res.push((available_entities.clone(), context.clone(), t.clone())); + let mut current = current.1; + while let Some((label, next)) = current.one_transition()? && limit > 1 { + limit -= 1; + current = next; + let (available_entities, context, t) = label.get_context(); + res.push((available_entities.clone(), context.clone(), t.clone())); + } + Ok(res) + } + /// Return the first n traces. Equivalent to visiting the execution tree /// depth first and returning the first n leaf nodes and their path to the /// root. diff --git a/testing/examples/run.system b/testing/examples/run.system index c567ac5..f76c76b 100644 --- a/testing/examples/run.system +++ b/testing/examples/run.system @@ -3,4 +3,6 @@ Initial Entities: {a, b} Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)] Reactions: ([{a,b}, {c}, {b}]) -Run > Print +Run > Print, + +Run (Limit: 2) > Print diff --git a/testing/examples/target.system b/testing/examples/target.system index a9d89d4..dfef71f 100644 --- a/testing/examples/target.system +++ b/testing/examples/target.system @@ -3,4 +3,10 @@ Initial Entities: {a, b} Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)] Reactions: ([{a,b}, {c}, {b}]) -Target > Print +Target > Print, + +Target (Limit: 7) > Print, + +Target (Limit: 6) > Print, + +Target (Limit: 5) > Print