Skip to content

Commit

Permalink
feat: tried to implement generics a bit more
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukáš Chudíček committed Oct 1, 2023
1 parent c09d3f5 commit aec8b05
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 98 deletions.
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ mod tests {
println!("start element {:?}", name);
if name.local_name == "apply" {
println!("parsing apply");
let expression = super::Expression::try_from_xml(&mut xml);
let expression = super::Expression::<u8>::try_from_xml(&mut xml);
println!("parsed apply {:?}", expression);
}
}
Expand Down Expand Up @@ -165,7 +165,7 @@ mod tests {
indent += 1;
if name.local_name == "transition" {
println!("parsing transition");
let update_fn = super::UpdateFn::try_from_xml(&mut xml);
let update_fn = super::UpdateFn::<u8>::try_from_xml(&mut xml);
println!("update fn: {:?}", update_fn);
return;
}
Expand Down
77 changes: 48 additions & 29 deletions src/prototype/expression.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use std::io::BufRead;
use std::{io::BufRead, str::FromStr};
use thiserror::Error;
use xml::reader::{EventReader, XmlEvent};

#[derive(Debug)]
pub enum Expression {
Terminal(Proposition),
Not(Box<Expression>),
And(Box<Expression>, Box<Expression>),
Or(Box<Expression>, Box<Expression>),
Xor(Box<Expression>, Box<Expression>),
Implies(Box<Expression>, Box<Expression>),
pub enum Expression<T> {
Terminal(Proposition<T>),
Not(Box<Expression<T>>),
And(Box<Expression<T>>, Box<Expression<T>>),
Or(Box<Expression<T>>, Box<Expression<T>>),
Xor(Box<Expression<T>>, Box<Expression<T>>),
Implies(Box<Expression<T>>, Box<Expression<T>>),
}

enum LogicOp {
Expand Down Expand Up @@ -43,10 +43,11 @@ impl LogicOp {
}
}

impl Expression {
// todo there should be constraint on the type of T: FromStr
impl<T: FromStr> Expression<T> {
// todo consider iterative approach instead of recursive?
pub fn try_from_xml<T: BufRead>(
xml: &mut EventReader<T>,
pub fn try_from_xml<BR: BufRead>(
xml: &mut EventReader<BR>,
) -> Result<Self, Box<dyn std::error::Error>> {
loop {
match xml.next() {
Expand Down Expand Up @@ -76,20 +77,20 @@ impl Expression {
}
}

fn terminal_from_xml(
fn terminal_from_xml<T: FromStr>(
op: CmpOp,
xml: &mut EventReader<impl BufRead>,
) -> Result<Expression, Box<dyn std::error::Error>> {
) -> Result<Expression<T>, Box<dyn std::error::Error>> {
expect_closure_of(&op.its_string(), xml)?; // close the cmp op tag
let prp = parse_terminal_ops(xml)?;
expect_closure_of("apply", xml)?;
Ok(Expression::Terminal(Proposition::new(op, prp)))
}

fn logical_from_xml<T: BufRead>(
fn logical_from_xml<T: FromStr, BR: BufRead>(
op: LogicOp,
xml: &mut EventReader<T>,
) -> Result<Expression, Box<dyn std::error::Error>> {
xml: &mut EventReader<BR>,
) -> Result<Expression<T>, Box<dyn std::error::Error>> {
expect_closure_of(&op.its_string(), xml)?; // self closing tag must be "closed"
match op {
LogicOp::Not => {
Expand Down Expand Up @@ -160,16 +161,16 @@ fn expect_closure_of<T: BufRead>(
}
}

pub enum TerminalOps {
Standard(String, u16),
Flipped(u16, String),
pub enum TerminalOps<T> {
Standard(String, T),
Flipped(T, String),
}

/// expects input xml to be in such state that the next two xml nodes are either
/// ci and then cn, or cn and then ci
pub fn parse_terminal_ops<T: BufRead>(
xml: &mut EventReader<T>,
) -> Result<TerminalOps, Box<dyn std::error::Error>> {
pub fn parse_terminal_ops<T: FromStr, BR: BufRead>(
xml: &mut EventReader<BR>,
) -> Result<TerminalOps<T>, Box<dyn std::error::Error>> {
let elem = expect_opening(xml)?;
if elem == "ci" {
let ci;
Expand All @@ -185,7 +186,16 @@ pub fn parse_terminal_ops<T: BufRead>(
expect_opening_of("cn", xml)?;
let cn;
if let XmlEvent::Characters(content) = xml.next()? {
cn = content.trim().parse::<u16>()?;
cn = match content.trim().parse::<T>() {
Ok(it) => it,
Err(_) => {
return Err(format!(
"could not parse to specified type; got {}",
content.trim().to_string()
)
.into())
}
}
} else {
return Err("cn must be followed by characters - the variable name".into());
}
Expand All @@ -197,7 +207,16 @@ pub fn parse_terminal_ops<T: BufRead>(
if elem == "cn" {
let cn;
if let XmlEvent::Characters(content) = xml.next()? {
cn = content.trim().parse::<u16>()?;
cn = match content.trim().parse::<T>() {
Ok(it) => it,
Err(_) => {
return Err(format!(
"could not parse to specified type; got {}",
content.trim().to_string()
)
.into())
}
}
} else {
return Err("cn must be followed by characters - the variable name".into());
}
Expand Down Expand Up @@ -284,17 +303,17 @@ impl CmpOp {
pub struct ParseCmpOpError(String);

/// sbml proposition normalized ie in the form of `var op const`
#[derive(Debug, Clone)]
pub struct Proposition {
#[derive(Clone, Debug)]
pub struct Proposition<T> {
pub cmp: CmpOp,
pub ci: String, // the variable name
pub cn: u16, // the constant value
pub cn: T, // the constant value
}

impl Proposition {
impl<T> Proposition<T> {
/// if the input terminal operands are flipped, the returning value will flip the operands as well
/// as the comparison operator in order to normalize the proposition
pub fn new(cmp_op: CmpOp, ops: TerminalOps) -> Self {
pub fn new(cmp_op: CmpOp, ops: TerminalOps<T>) -> Self {
match ops {
TerminalOps::Standard(lhs, rhs) => Self {
cmp: cmp_op,
Expand Down
54 changes: 27 additions & 27 deletions src/prototype/update_fn.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
use crate::{
expect_closure_of, expect_opening_of, process_list, StartElementWrapper, UnaryIntegerDomain,
};
use crate::{expect_closure_of, expect_opening_of, process_list, StartElementWrapper};

use super::expression::Expression;
use super::utils::expect_opening;
use std::io::BufRead;
use xml::reader::{EventReader, XmlEvent};
use std::{io::BufRead, str::FromStr};
use xml::reader::EventReader;

/// represents collection of tuples of the result values and the associated conditions. there is also the default value.
/// todo think about how the functions should be evaluated - should we allow the conditions to "overlap" and say that the first one counts?
/// (would not be hard to implement, just (!all_previous && current); the default would then be analogically (!all_previous && true)).
/// in that case, the !all_previous should be somehow cached and passed to the next ofc
#[derive(Debug)]
pub struct UpdateFn {
pub struct UpdateFn<T> {
pub input_vars_names: Vec<String>,
pub target_var_name: String,
// todo should likely be in bdd repr already;
// that should be done for the intermediate repr of Expression as well;
// will do that once i can parse the whole xml
pub terms: Vec<(u8, Expression)>,
pub default: u8,
pub terms: Vec<(T, Expression<T>)>,
pub default: T,
}

impl UpdateFn {
impl<T> UpdateFn<T> {
pub fn new(
input_vars_names: Vec<String>,
target_var_name: String,
terms: Vec<(u8, Expression)>,
default: u8,
terms: Vec<(T, Expression<T>)>,
default: T,
) -> Self {
Self {
input_vars_names,
Expand All @@ -36,9 +34,11 @@ impl UpdateFn {
default,
}
}
}

pub fn try_from_xml<T: BufRead>(
xml: &mut EventReader<T>,
impl<T: FromStr> UpdateFn<T> {
pub fn try_from_xml<BR: BufRead>(
xml: &mut EventReader<BR>,
) -> Result<Self, Box<dyn std::error::Error>> {
let some_start_element = expect_opening(xml)?;
if !matches!(
Expand Down Expand Up @@ -82,8 +82,8 @@ impl UpdateFn {
}
}

fn process_input_var_name_item<T: BufRead>(
xml: &mut EventReader<T>,
fn process_input_var_name_item<BR: BufRead>(
xml: &mut EventReader<BR>,
current: StartElementWrapper,
) -> Result<String, Box<dyn std::error::Error>> {
let mut qualitative_species = current.attributes.iter().filter_map(|att| {
Expand All @@ -103,8 +103,8 @@ fn process_input_var_name_item<T: BufRead>(
Ok(item)
}

fn process_output_var_name_item<T: BufRead>(
xml: &mut EventReader<T>,
fn process_output_var_name_item<BR: BufRead>(
xml: &mut EventReader<BR>,
current: StartElementWrapper,
) -> Result<String, Box<dyn std::error::Error>> {
let mut qualitative_species = current.attributes.iter().filter_map(|att| {
Expand All @@ -127,18 +127,18 @@ fn process_output_var_name_item<T: BufRead>(
/// currently only one output for given update function is supported
/// but // todo; requested to generalize
/// expects the xml to be at the element `<qual:listOfOutputs>` when this fction called
fn get_target_var_name<T: BufRead>(
_xml: &mut EventReader<T>,
fn get_target_var_name<BR: BufRead>(
_xml: &mut EventReader<BR>,
) -> Result<String, Box<dyn std::error::Error>> {
// let xd = expect_opening_of("output", xml)?;
// // todo read the thing
// let lol = expect_closure_of("output", xml)?;
unimplemented!();
}

fn get_default_and_list_of_terms<T: BufRead>(
xml: &mut EventReader<T>,
) -> Result<(u8, Vec<(u8, Expression)>), Box<dyn std::error::Error>> {
fn get_default_and_list_of_terms<T: FromStr, BR: BufRead>(
xml: &mut EventReader<BR>,
) -> Result<(T, Vec<(T, Expression<T>)>), Box<dyn std::error::Error>> {
// firs should be the default
let default_element = expect_opening_of("defaultTerm", xml)?;
let default_val = result_level_from_attributes(&default_element)
Expand All @@ -157,10 +157,10 @@ fn get_default_and_list_of_terms<T: BufRead>(
Ok((default_val, values_and_expressions))
}

fn process_function_term_item<T: BufRead>(
xml: &mut EventReader<T>,
fn process_function_term_item<T: FromStr, BR: BufRead>(
xml: &mut EventReader<BR>,
current: StartElementWrapper,
) -> Result<(u8, Expression), Box<dyn std::error::Error>> {
) -> Result<(T, Expression<T>), Box<dyn std::error::Error>> {
let res_lvl = result_level_from_attributes(&current)
.ok_or("expected \"resultLevel\" with numeric argument in functionTerm but none found")?;

Expand All @@ -176,10 +176,10 @@ fn process_function_term_item<T: BufRead>(
Ok((res_lvl, exp))
}

fn result_level_from_attributes(elem: &StartElementWrapper) -> Option<u8> {
fn result_level_from_attributes<T: FromStr>(elem: &StartElementWrapper) -> Option<T> {
elem.attributes.iter().find_map(|attr| {
if attr.name.local_name == "resultLevel" {
attr.value.parse::<u8>().ok()
attr.value.parse::<T>().ok()
} else {
None
}
Expand Down
Loading

0 comments on commit aec8b05

Please sign in to comment.