custom grammar errors, better handling of user facing errors

fuckery for modules in grammar, maybe fixable?
This commit is contained in:
elvis
2025-09-16 23:09:20 +02:00
parent e41d92ac36
commit d458787a81
8 changed files with 624 additions and 345 deletions

View File

@ -2,6 +2,7 @@ use execution::presets;
use lalrpop_util::ParseError; use lalrpop_util::ParseError;
use std::fmt::Display; use std::fmt::Display;
use grammar::grammar; use grammar::grammar;
use ::grammar::user_error::{UserError, UserErrorTypes};
pub struct Parsers {} pub struct Parsers {}
@ -27,37 +28,41 @@ impl presets::FileParsers for Parsers {
} }
} }
fn reformat_error<T, S>( fn create_error<S, T>(
e: ParseError<usize, T, &'static str>,
input_str: &str, input_str: &str,
l: usize,
t: T,
r: usize,
expected: Option<Vec<String>>,
error: Option<UserErrorTypes>,
) -> Result<S, String> ) -> Result<S, String>
where where
T: Display, 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 {}{}{} \ "Unrecognized token {}{}{} \
between positions {l} and {r}.", between positions {l} and {r}.",
"\"".red(), "\"".red(),
t.to_string().red(), t.to_string().red(),
"\"".red(), "\"".red(),
); )
}
};
{
if let Some(expected) = expected {
// Temporary debug. // Temporary debug.
err.push_str("\nExpected: "); err.push_str("\nExpected: ");
let mut it = expected.iter().peekable(); let mut it = expected.iter().peekable();
@ -70,6 +75,8 @@ where
err.push(' '); err.push(' ');
} }
} }
}
}
let right_new_line = input_str[l..] let right_new_line = input_str[l..]
.find("\n") .find("\n")
.map(|pos| pos + l) .map(|pos| pos + l)
@ -107,7 +114,35 @@ where
} }
Err(err) Err(err)
}
fn reformat_error<T, S>(
e: ParseError<usize, T, UserError>,
input_str: &str,
) -> Result<S, String>
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()),
} }
} }

View File

@ -92,9 +92,11 @@ pub enum Instruction {
}, },
Target { Target {
so: SaveOptions, so: SaveOptions,
limit: Option<usize>,
}, },
Run { Run {
so: SaveOptions, so: SaveOptions,
limit: Option<usize>,
}, },
Loop { Loop {
symbol: String, symbol: String,
@ -132,43 +134,45 @@ pub enum System {
impl System { impl System {
/// Deserialize the graph if applicable. /// Deserialize the graph if applicable.
pub fn compute( pub fn compute(
&self, self,
translator: Translator, translator: Translator,
) -> Result<EvaluatedSystem, String> { ) -> Result<EvaluatedSystem, String> {
match self { match self {
| Self::System { sys } => Ok(EvaluatedSystem::System { | Self::System { sys } =>
sys: sys.to_owned(), Ok(EvaluatedSystem::from_sys(sys, translator)),
translator,
}),
| Self::Deserialize { path } => { | Self::Deserialize { path } => {
let (graph, translator) = deserialize(path.into())?; let (graph, translator) = deserialize(path)?;
Ok(EvaluatedSystem::Graph { graph, translator }) Ok(EvaluatedSystem::from_graph(graph, translator))
}, },
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub enum EvaluatedSystem { pub struct EvaluatedSystem {
Graph { sys: Option<system::System>,
graph: graph::SystemGraph, graph: Option<graph::SystemGraph>,
positive: Option<system::PositiveSystem>,
translator: Translator, translator: Translator,
},
System {
sys: system::System,
translator: Translator,
},
} }
impl EvaluatedSystem { impl EvaluatedSystem {
pub fn get_translator(&mut self) -> &mut Translator { pub fn get_translator(&mut self) -> &mut Translator {
match self { &mut self.translator
| EvaluatedSystem::Graph {
graph: _,
translator,
} => translator,
| EvaluatedSystem::System { sys: _, translator } => 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. /// Prints statistics of the system.
/// Equivalent main_do(stat) or main_do(stat, MissingE) /// Equivalent main_do(stat) or main_do(stat, MissingE)
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> { pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
match system { if let Some(sys) = &system.sys {
| EvaluatedSystem::System { sys, translator } => Ok(sys.statistics(&system.translator))
Ok(sys.statistics(translator)), } else if let Some(graph) = &system.graph {
| EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else { let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph.".into()); return Err("No node found in graph.".into());
}; };
Ok(sys.statistics(translator)) 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. /// Prints a final set of entities in a terminating Reaction System.
/// The system needs to terminate to return. /// The system needs to terminate to return.
/// Equivalent to main_do(target, E) /// Equivalent to main_do(target, E)
pub fn target(system: &EvaluatedSystem) -> Result<String, String> { pub fn target(
let (res, translator) = match system { system: &EvaluatedSystem,
| EvaluatedSystem::System { sys, translator } => limit: Option<usize>
(sys.target()?, translator), ) -> Result<String, String> {
| EvaluatedSystem::Graph { graph, translator } => { if let Some(sys) = &system.sys {
let Some(sys) = graph.node_weights().next() else { let res = if let Some(limit) = limit {
return Err("No node found in graph.".into()); sys.target_limit(limit)?
}; } else {
(sys.target()?, translator) sys.target()?
},
}; };
Ok(format!( Ok(format!(
"After {} steps we arrive at state:\n{}", "After {} steps we arrive at state:\n{}",
res.0, res.0,
translator::Formatter::from(translator, &res.1) 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 /// Finds the list of traversed states in a (deterministic) terminating
/// reaction. /// reaction.
/// The system needs to terminate to return. /// The system needs to terminate to return.
/// equivalent to main_do(run,Es) /// equivalent to main_do(run,Es)
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> { pub fn traversed(
let (res, translator) = match system { system: &EvaluatedSystem,
| EvaluatedSystem::System { sys, translator } => limit: Option<usize>
(sys.run_separated()?, translator), ) -> Result<String, String> {
| EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph.".into());
};
(sys.run_separated()?, translator)
},
};
let mut output = String::new(); let mut output = String::new();
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: "); output.push_str("The trace is composed by the set of entities: ");
for (e, _c, _t) in res { for (e, _c, _t) in res {
output.push_str(&format!( output.push_str(&format!(
"{}", "{}",
translator::Formatter::from(translator, &e) 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 = 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())
}
} }
/// Finds the looping list of states in a reaction system with a perpetual /// Finds the looping list of states in a reaction system with a perpetual
@ -318,38 +384,68 @@ pub fn hoop(
symbol: String, symbol: String,
) -> Result<String, String> { ) -> Result<String, String> {
use system::LoopSystem; use system::LoopSystem;
// 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 mut output = String::new();
let (res, translator) = match system { if let Some(sys) = &system.sys {
| EvaluatedSystem::System { sys, translator } => (sys, translator), let res = match sys.lollipops_only_loop_named(id) {
| EvaluatedSystem::Graph { graph, translator } => { | 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(graph) = &system.graph {
let Some(sys) = graph.node_weights().next() else { let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph.".into()); return Err("No node found in graph.".into());
}; };
(sys, translator) let res = match sys.lollipops_only_loop_named(id) {
},
};
// we retrieve the id for "x" and use it to find the corresponding loop
let Some(id) = 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, | Some(o) => o,
| None => { | None => {
return Err("No loop found.".into()); 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(&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: "); output.push_str("The loop is composed by the sets: ");
for e in res { for e in res {
output.push_str(&format!( output.push_str(&format!(
"{}", "{}",
translator::Formatter::from(translator, &e) translator::Formatter::from(&system.translator, &e)
)); ));
} }
Ok(output) Ok(output)
} else {
Err("Loop not available for supplied system.".into())
}
} }
/// Finds the frequency of each entity in the traversed states for a /// 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<String, String> { pub fn freq(system: &EvaluatedSystem) -> Result<String, String> {
use frequency::BasicFrequency; use frequency::BasicFrequency;
let (sys, translator) = match system { if let Some(sys) = &system.sys {
| 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 res = frequency::Frequency::naive_frequency(sys)?; let res = frequency::Frequency::naive_frequency(sys)?;
Ok(format!( Ok(format!(
"Frequency of encountered symbols:\n{}", "Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res) 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(&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 /// Finds the frequency of each entity in the limit loop of a nonterminating
@ -389,17 +496,29 @@ where
{ {
use frequency::BasicFrequency; use frequency::BasicFrequency;
let (sys, translator): (&system::System, &mut Translator) = match system { let (_, sets) =
| EvaluatedSystem::System { sys, translator } => (sys, translator), read_file(&mut system.translator, experiment, parser_experiment)?;
| EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else { if let Some(sys) = &system.sys {
return Err("No node found in graph.".into()); let res = match frequency::Frequency::limit_frequency(
}; &sets,
(sys, translator) &sys.reaction_rules,
&sys.available_entities,
) {
| Some(e) => e,
| None => {
return Err("Error calculating frequency.".into());
}, },
}; };
let (_, sets) = read_file(translator, experiment, parser_experiment)?; 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 = match frequency::Frequency::limit_frequency( let res = match frequency::Frequency::limit_frequency(
&sets, &sets,
@ -414,8 +533,28 @@ where
Ok(format!( Ok(format!(
"Frequency of encountered symbols:\n{}", "Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res) 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 /// Finds the frequency of each entity in the traversed loops of a terminating
@ -433,17 +572,30 @@ where
{ {
use frequency::BasicFrequency; use frequency::BasicFrequency;
let (sys, translator): (&system::System, &mut Translator) = match system { let (weights, sets) =
| EvaluatedSystem::System { sys, translator } => (sys, translator), read_file(&mut system.translator, experiment, parser_experiment)?;
| EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else { if let Some(sys) = &system.sys {
return Err("No node found in graph".into()); let res = match frequency::Frequency::fast_frequency(
}; &sets,
(sys, translator) &sys.reaction_rules,
&sys.available_entities,
&weights,
) {
| Some(e) => e,
| None => {
return Err("Error calculating frequency.".into());
}, },
}; };
let (weights, sets) = read_file(translator, experiment, parser_experiment)?; 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 = match frequency::Frequency::fast_frequency( let res = match frequency::Frequency::fast_frequency(
&sets, &sets,
@ -459,22 +611,30 @@ where
Ok(format!( Ok(format!(
"Frequency of encountered symbols:\n{}", "Frequency of encountered symbols:\n{}",
translator::Formatter::from(translator, &res) 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. /// Computes the LTS.
/// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs) /// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs)
pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> { pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> {
if let EvaluatedSystem::System { sys, translator } = system { if let Some(sys) = &system.sys && system.graph.is_none() {
*system = EvaluatedSystem::Graph { let graph = sys.digraph()?;
graph: sys.digraph()?, system.graph = Some(graph);
translator: translator.to_owned(), } else if let Some(positive) = &system.positive && system.graph.is_none() {
}; let _graph = positive.digraph()?;
todo!()
} }
Ok(()) Ok(())
} }
/// Given a graph and a function that identifies nodes of the graph, merges
/// nodes with the same identifier.
pub fn grouping( pub fn grouping(
system: &mut EvaluatedSystem, system: &mut EvaluatedSystem,
group: &assert::grouping::Assert, group: &assert::grouping::Assert,
@ -482,9 +642,9 @@ pub fn grouping(
let mut buckets = HashMap::new(); let mut buckets = HashMap::new();
let mut leader: HashMap<petgraph::prelude::NodeIndex, _> = HashMap::new(); let mut leader: HashMap<petgraph::prelude::NodeIndex, _> = HashMap::new();
if let EvaluatedSystem::Graph { graph, translator } = system { if let Some(graph) = &mut system.graph {
for node in graph.node_indices() { 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:?}"); println!("node: {node:?} -> val: {val:?}");
buckets.entry(val.clone()).or_insert(vec![]).push(node); buckets.entry(val.clone()).or_insert(vec![]).push(node);
let l = buckets.get(&val).unwrap().first().unwrap(); let l = buckets.get(&val).unwrap().first().unwrap();
@ -541,47 +701,30 @@ where
{ {
use assert::relabel::AssertReturnValue; use assert::relabel::AssertReturnValue;
let system_b = read_file( let system_b = read_file(&mut system_a.translator,
system_a.get_translator(),
system_b.to_string(), system_b.to_string(),
parser_instructions, parser_instructions)?;
)?;
let mut system_b = match system_b let mut system_b = system_b
.system .system
.compute(system_a.get_translator().clone())? .compute(system_a.get_translator().clone())?;
{
| EvaluatedSystem::System { sys, translator } => if system_b.translator != system_a.translator {
EvaluatedSystem::System { sys, translator }, return Err("Bisimilarity not implemented for systems with different \
| EvaluatedSystem::Graph { graph, translator } => { encodings. Serialize the systems with the same 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()); .into());
} }
EvaluatedSystem::Graph { graph, translator }
},
};
digraph(system_a)?; digraph(system_a)?;
digraph(&mut system_b)?; digraph(&mut system_b)?;
// since we ran digraph on both they have to be graphs // since we ran digraph on both they have to have valid graphs
match (system_a, &mut system_b) {
| (
EvaluatedSystem::Graph {
graph: a,
translator: _,
},
EvaluatedSystem::Graph {
graph: b,
translator: translator_b,
},
) => {
let a: Graph<system::System, AssertReturnValue> = let a: Graph<system::System, AssertReturnValue> =
a.map_edges(edge_relabeler, translator_b)?; system_a.graph.as_ref().unwrap()
.map_edges(edge_relabeler, &mut system_a.translator)?;
let b: Graph<system::System, AssertReturnValue> = let b: Graph<system::System, AssertReturnValue> =
b.map_edges(edge_relabeler, translator_b)?; system_b.graph.unwrap().map_edges(edge_relabeler, &mut system_b.translator)?;
Ok(format!( Ok(format!(
"{}", "{}",
// bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(& // bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&
@ -589,11 +732,6 @@ where
// bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b) // bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b)
bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b) bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b)
)) ))
},
| _ => {
unreachable!()
},
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -608,13 +746,8 @@ pub fn dot(
node_color: graph::NodeColor, node_color: graph::NodeColor,
edge_color: graph::EdgeColor, edge_color: graph::EdgeColor,
) -> Result<String, String> { ) -> Result<String, String> {
match system { if let Some(graph) = &system.graph {
| EvaluatedSystem::System { let rc_translator = Rc::new(system.translator.clone());
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( let modified_graph = graph.map(
node_display.generate(Rc::clone(&rc_translator), graph), node_display.generate(Rc::clone(&rc_translator), graph),
edge_display.generate(Rc::clone(&rc_translator), graph), edge_display.generate(Rc::clone(&rc_translator), graph),
@ -623,7 +756,7 @@ pub fn dot(
let graph = Rc::new(graph.to_owned()); let graph = Rc::new(graph.to_owned());
let node_formatter = node_color let node_formatter = node_color
.generate(Rc::clone(&graph), translator.encode_not_mut("*")); .generate(Rc::clone(&graph), system.translator.encode_not_mut("*"));
let edge_formatter = edge_color.generate(Rc::clone(&graph)); let edge_formatter = edge_color.generate(Rc::clone(&graph));
let dot = dot::Dot::with_attr_getters( let dot = dot::Dot::with_attr_getters(
@ -634,7 +767,8 @@ pub fn dot(
); );
Ok(format!("{dot}")) Ok(format!("{dot}"))
}, } else {
Err("Supplied system is not a graph".into())
} }
} }
@ -644,13 +778,8 @@ pub fn graphml(
node_display: graph::NodeDisplay, node_display: graph::NodeDisplay,
edge_display: graph::EdgeDisplay, edge_display: graph::EdgeDisplay,
) -> Result<String, String> { ) -> Result<String, String> {
match system { if let Some(graph) = &system.graph {
| EvaluatedSystem::System { let rc_translator = Rc::new(system.translator.to_owned());
sys: _,
translator: _,
} => Err("Supplied system is not a graph".into()),
| EvaluatedSystem::Graph { graph, translator } => {
let rc_translator = Rc::new(translator.to_owned());
// map each value to the corresponding value we want to display // map each value to the corresponding value we want to display
let modified_graph = graph.map( let modified_graph = graph.map(
@ -665,20 +794,16 @@ pub fn graphml(
.export_edge_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 /// N.B. graph size in memory might be much larger after serialization and
/// deserialization. /// deserialization.
pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> { pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> {
match system { if let Some(graph) = &system.graph {
| EvaluatedSystem::System {
sys: _,
translator: _,
} => Err("Supplied system is not a graph".into()),
| EvaluatedSystem::Graph { graph, translator } => {
// relative path // relative path
let mut path = std::path::PathBuf::from(path); let mut path = std::path::PathBuf::from(path);
path.set_extension("cbor"); path.set_extension("cbor");
@ -693,11 +818,12 @@ pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> {
}, },
}; };
match serialize::ser(f, graph, translator) { match serialize::ser(f, graph, &system.translator) {
| Ok(_) => Ok(()), | Ok(_) => Ok(()),
| Err(_) => Err("Error during serialization.".into()), | Err(_) => Err("Error during serialization.".into()),
} }
}, } else {
Err("Supplied system is not a graph".into())
} }
} }
@ -758,11 +884,11 @@ fn execute<P: FileParsers>(
| Instruction::Stats { so } => { | Instruction::Stats { so } => {
save_options!(stats(system)?, so); save_options!(stats(system)?, so);
}, },
| Instruction::Target { so } => { | Instruction::Target { so, limit } => {
save_options!(target(system)?, so); save_options!(target(system, limit)?, so);
}, },
| Instruction::Run { so } => { | Instruction::Run { so, limit } => {
save_options!(traversed(system)?, so); save_options!(traversed(system, limit)?, so);
}, },
| Instruction::Loop { symbol, so } => { | Instruction::Loop { symbol, so } => {
save_options!(hoop(system, symbol)?, so); save_options!(hoop(system, symbol)?, so);

View File

@ -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,
}

View File

@ -8,9 +8,14 @@ use rsprocess::element::IdType;
use rsprocess::translator::Translator; use rsprocess::translator::Translator;
use execution::presets; use execution::presets;
use rsprocess::graph; use rsprocess::graph;
use crate::custom_error;
grammar(translator: &mut Translator); grammar(translator: &mut Translator);
extern {
type Error = custom_error::UserError;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Helpers // Helpers
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -31,7 +36,7 @@ match {
"Print", "Save", "Print", "Save",
"Dot", "GraphML", "Serialize", "Dot", "GraphML", "Serialize",
"Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency", "Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency",
"FastFrequency", "Digraph", "Bisimilarity", "FastFrequency", "Digraph", "Bisimilarity", "Limit",
"Deserialize", "Deserialize",
"?", "?",
"Hide", "Hide",
@ -160,10 +165,38 @@ LiteralProcess: String = {
"relabel" => <>.into(), "relabel" => <>.into(),
}; };
// all numbers are i64
Num: i64 = { Num: i64 = {
NUMBER =>? i64::from_str(<>) <sign: "-"?> <start: @L> <n: NUMBER> <end: @R> =>? {
.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 = {
<start: @L> <n: NUMBER> <end: @R> =>? usize::from_str(n)
.map_err(|_| ParseError::User {
error: custom_error::UserError {
token: (start, n.into(), end),
error: custom_error::UserErrorTypes::NumberTooBigUsize
}
})
}; };
Path: String = { Path: String = {
@ -1037,12 +1070,12 @@ Instruction: presets::Instruction = {
"Stats" "Stats"
">" <so: SaveOptions> => ">" <so: SaveOptions> =>
presets::Instruction::Stats { so }, presets::Instruction::Stats { so },
"Target" "Target" <limit: ("(" "Limit" ":" NumUsize ")")?>
">" <so: SaveOptions> => ">" <so: SaveOptions> =>
presets::Instruction::Target { so }, presets::Instruction::Target { so, limit: limit.map(|l| l.3) },
"Run" "Run" <limit: ("(" "Limit" ":" NumUsize ")")?>
">" <so: SaveOptions> => ">" <so: SaveOptions> =>
presets::Instruction::Run { so }, presets::Instruction::Run { so, limit: limit.map(|l| l.3) },
"Loop" "(" <symbol: Literal> ")" "Loop" "(" <symbol: Literal> ")"
">" <so: SaveOptions> => ">" <so: SaveOptions> =>
presets::Instruction::Loop { symbol, so }, presets::Instruction::Loop { symbol, so },

View File

@ -1,3 +1,9 @@
mod custom_error;
pub mod user_error {
pub use crate::custom_error::*;
}
lalrpop_util::lalrpop_mod!( lalrpop_util::lalrpop_mod!(
#[allow(clippy::uninlined_format_args)] pub grammar, // name of module #[allow(clippy::uninlined_format_args)] pub grammar, // name of module
"/grammar.rs" // location of parser "/grammar.rs" // location of parser

View File

@ -73,11 +73,19 @@ pub trait ExtensionsSystem: BasicSystem {
fn target(&self) -> Result<(i64, Self::Set), String>; fn target(&self) -> Result<(i64, Self::Set), String>;
fn target_limit(&self, limit: usize) -> Result<(i64, Self::Set), String>;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn run_separated( fn run_separated(
&self, &self,
) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, String>; ) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, String>;
#[allow(clippy::type_complexity)]
fn run_separated_limit(
&self,
limit: usize
) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, String>;
fn traces(self, n: usize) -> Result<Vec<Trace<Self::Label, Self>>, String>; fn traces(self, n: usize) -> Result<Vec<Trace<Self::Label, Self>>, String>;
} }
@ -162,6 +170,20 @@ impl<T: BasicSystem> ExtensionsSystem for T {
Ok((n, current.available_entities().clone())) 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 /// see smartOneRunECT, smartRunECT
fn run_separated( fn run_separated(
&self, &self,
@ -183,6 +205,30 @@ impl<T: BasicSystem> ExtensionsSystem for T {
Ok(res) Ok(res)
} }
/// see smartOneRunECT, smartRunECT
fn run_separated_limit(
&self,
limit: usize
) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, 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 /// 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 /// depth first and returning the first n leaf nodes and their path to the
/// root. /// root.

View File

@ -3,4 +3,6 @@ Initial Entities: {a, b}
Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)] Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)]
Reactions: ([{a,b}, {c}, {b}]) Reactions: ([{a,b}, {c}, {b}])
Run > Print Run > Print,
Run (Limit: 2) > Print

View File

@ -3,4 +3,10 @@ Initial Entities: {a, b}
Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)] Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)]
Reactions: ([{a,b}, {c}, {b}]) Reactions: ([{a,b}, {c}, {b}])
Target > Print Target > Print,
Target (Limit: 7) > Print,
Target (Limit: 6) > Print,
Target (Limit: 5) > Print