Skip to content

Commit

Permalink
refactor: parsing variable update fn
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukáš Chudíček committed Dec 5, 2023
1 parent 0c908f7 commit 7605c32
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 84 deletions.
12 changes: 6 additions & 6 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ pub mod variable_update_function {
use crate::expression_components::expression::Expression;

pub struct VariableUpdateFn<T> {
// pub input_vars_names: Vec<String>, // todo what for? remove by default if not needed; uncomment if needed
// pub target_var_name: String, // todo this should not care about this; the caller should keep this information
pub input_vars_names: Vec<String>,
pub target_var_name: String,
pub terms: Vec<(T, Expression<T>)>,
pub default: T,
}

impl<T> VariableUpdateFn<T> {
pub fn new(
// input_vars_names: Vec<String>,
// target_var_name: String,
input_vars_names: Vec<String>,
target_var_name: String,
terms: Vec<(T, Expression<T>)>,
default: T,
) -> Self {
Self {
// input_vars_names,
// target_var_name,
input_vars_names,
target_var_name,
terms,
default,
}
Expand Down
7 changes: 3 additions & 4 deletions src/xml_parsing/expression_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl ToString for LogicalOperator {
}

impl<T: FromStr> Expression<T> {
// todo likely should not be an associated fn - or make it consistant with `variable_update_fn` - from-xml-parser
pub fn try_from_xml<XR, BR>(xml: &mut XR) -> Result<Self, XmlReadingError>
where
XR: XmlReader<BR>,
Expand Down Expand Up @@ -143,7 +144,7 @@ where
loop {
match xml.next()? {
XmlEvent::Whitespace(_) => ( /* ignore */ ),
ref got @ XmlEvent::StartElement { ref name, .. /* bruh thats one hell of a line */} => {
ref got @ XmlEvent::StartElement { ref name, .. } => {
if name.local_name == "apply" {
acc.push(Expression::try_from_xml(xml)?);
continue;
Expand All @@ -166,9 +167,7 @@ where
}
other => {
return Err(XmlReadingError::UnexpectedEvent {
expected: super::utils::ExpectedXmlEvent::Start(
"start of ".to_string(),
),
expected: super::utils::ExpectedXmlEvent::Start("start of ".to_string()),
got: other,
})
}
Expand Down
62 changes: 58 additions & 4 deletions src/xml_parsing/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ pub enum XmlReadingError {
UnderlyingReaderError(#[from] xml::reader::Error),
ParsingError(String),
NoSuchAttribute(String),
WrongAmountOfElements {
expected_amount: usize,
found_items_string: String,
},
}

impl Display for XmlReadingError {
Expand All @@ -41,6 +45,16 @@ impl Display for XmlReadingError {
}
XmlReadingError::ParsingError(s) => write!(f, "Parsing error; could not parse {}", s),
XmlReadingError::NoSuchAttribute(s) => write!(f, "No such attribute: {}", s),
XmlReadingError::WrongAmountOfElements {
expected_amount,
found_items_string,
} => {
write!(
f,
"Wrong amount of elements. Expected: {}, found elements: [{}]",
expected_amount, found_items_string
)
}
}
}
}
Expand Down Expand Up @@ -237,10 +251,11 @@ where

/// iterates through the xml until it finds the first opening tag with the given name
/// (specifically, opening_element.name.local_name == expected_name)
pub fn find_start_of<XR: XmlReader<BR>, BR: BufRead>(
xml: &mut XR,
expected_name: &str,
) -> Result<(), XmlReadingError> {
pub fn find_start_of<XR, BR>(xml: &mut XR, expected_name: &str) -> Result<(), XmlReadingError>
where
XR: XmlReader<BR>,
BR: BufRead,
{
loop {
match xml.next()? {
xml::reader::XmlEvent::StartElement { name: n, .. }
Expand All @@ -258,3 +273,42 @@ pub fn find_start_of<XR: XmlReader<BR>, BR: BufRead>(
}
}
}

// todo this one is likely useless
/// Iterates through the xml until it finds the closing tag with the given name,
/// specifically, closing_element.name.local_name == expected_name.
///
/// Is also capable of working with recursive elements (elements that can contain themselves).
/// In that case, this function returns once it encounters the closing tag of the element
/// it is called from.
pub fn consume_the_rest_of_element<XR, BR>(
xml: &mut XR,
element_name: &str,
) -> Result<(), XmlReadingError>
where
XR: XmlReader<BR>,
BR: BufRead,
{
let mut depth = 0usize;
loop {
match xml.next()? {
xml::reader::XmlEvent::StartElement { name: n, .. } if n.local_name == element_name => {
depth += 1;
}
xml::reader::XmlEvent::EndElement { name: n } if n.local_name == element_name => {
if depth == 0 {
return Ok(());
} else {
depth -= 1;
}
}
xml::reader::XmlEvent::EndDocument => {
return Err(XmlReadingError::UnexpectedEvent {
expected: ExpectedXmlEvent::End(element_name.into()),
got: XmlEvent::EndDocument,
})
}
_ => continue,
}
}
}
131 changes: 61 additions & 70 deletions src/xml_parsing/variable_update_fn_parser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(dead_code)] // todo remove

use std::{io::BufRead, str::FromStr};

use xml::reader::XmlEvent;
Expand All @@ -13,72 +15,64 @@ use super::{
xml_reader::XmlReader,
};

/// expects the xml reader to be at the start of the <transition> element
impl<T: FromStr> VariableUpdateFn<T> {
pub fn try_from_xml<XR: XmlReader<BR>, BR: BufRead>(
xml: &mut XR,
) -> Result<Self, XmlReadingError> {
let some_start_element = expect_opening(xml)?;
if !matches!(
some_start_element.name.local_name.as_str(),
"listOfInputs" | "listOfOutputs"
) {
return Err(XmlReadingError::UnexpectedEvent {
expected: super::utils::ExpectedXmlEvent::Start(
"listOfInputs or listOfOutputs".to_string(),
),
got: XmlEvent::StartElement {
name: some_start_element.name,
attributes: some_start_element.attributes,
namespace: some_start_element.namespace,
},
});
}

// listOfInputs might not be present at all
let input_vars_names = if some_start_element.name.local_name == "listOfInputs" {
let aux = map_list(xml, "listOfInputs", "input", process_input_var_name_item)?;
expect_closure_of(xml, "listOfOutputs")?; // must be followed by listOfOutputs
aux
} else {
Vec::new()
};

// listOfOutputs must be present
let target_vars_names =
map_list(xml, "listOfOutputs", "output", process_output_var_name_item)?;
let mut target_vars_names = target_vars_names.iter();
let head = target_vars_names // todo head must be used; caller has no way of knowing which variable is the target otherwise
.next()
.ok_or(XmlReadingError::UnexpectedEvent {
expected: super::utils::ExpectedXmlEvent::Start("target var name".to_string()),
got: XmlEvent::EndElement {
name: some_start_element.name,
},
});
target_vars_names.next().map_or_else(
|| Ok::<(), ()>(()),
|_elem| {
// Err(XmlReadingError::UnexpectedEvent {
// expected: super::utils::ExpectedXmlEvent::End("list of vars names".to_string()),
// got: // ...,
// })
panic!("expected only one target var but found multiple; todo might want to change this")
/// Parses the <transition> XML element into a VariableUpdateFn struct.
/// Expects the parameter `xml` to be at the start of the <transition> XML element.
pub fn try_variable_update_fn_from_xml<XR, BR, T>(
xml: &mut XR,
) -> Result<VariableUpdateFn<T>, XmlReadingError>
where
XR: XmlReader<BR>,
BR: BufRead,
T: FromStr,
{
let some_start_element = expect_opening(xml)?;
if !matches!(
some_start_element.name.local_name.as_str(),
"listOfInputs" | "listOfOutputs"
) {
return Err(XmlReadingError::UnexpectedEvent {
expected: super::utils::ExpectedXmlEvent::Start(
"listOfInputs or listOfOutputs".to_string(),
),
got: XmlEvent::StartElement {
name: some_start_element.name,
attributes: some_start_element.attributes,
namespace: some_start_element.namespace,
},
);
// .ok_or(XmlReadingError::UnexpectedEvent { expected: super::utils::ExpectedXmlEvent::End("listOfOutputs".to_string()), got: () })
});
}

expect_opening_of(xml, "listOfFunctionTerms")?;
let (default, terms) = get_default_and_list_of_terms(xml)?;
// listOfInputs may or may not be present - either case is accepted
let input_vars_names = if some_start_element.name.local_name == "listOfInputs" {
let aux = map_list(xml, "listOfInputs", "input", process_input_var_name_item)?;
expect_opening_of(xml, "listOfOutputs")?; // must be followed by listOfOutputs
aux
} else {
Vec::new()
};

let target_vars_names = map_list(xml, "listOfOutputs", "output", process_output_var_name_item)?;
let target_variable_name = match target_vars_names.as_slice() {
[single_target_variable_name] => single_target_variable_name.clone(),
_ => {
return Err(XmlReadingError::WrongAmountOfElements {
expected_amount: 1,
found_items_string: target_vars_names.join(", "),
})
}
};

expect_closure_of(xml, "transition")?;
// Ok(Self::new(input_vars_names, head.into(), terms, default))
// Ok(VariableUpdateFn::new(terms, default))
expect_opening_of(xml, "listOfFunctionTerms")?;
let (default, terms) = get_default_and_list_of_terms(xml)?;

let xd = VariableUpdateFn::new(terms, default);
expect_closure_of(xml, "transition")?;

Ok(xd)
}
Ok(VariableUpdateFn::new(
input_vars_names,
target_variable_name,
terms,
default,
))
}

fn process_input_var_name_item<XR: XmlReader<BR>, BR: BufRead>(
Expand All @@ -95,7 +89,6 @@ fn process_input_var_name_item<XR: XmlReader<BR>, BR: BufRead>(

let item = qualitative_species
.next()
// .ok_or("expected \"qualitativeSpecies\" arg in input, but none found")?;
.ok_or(XmlReadingError::NoSuchAttribute(
"qualitativeSpecies".to_string(),
))?; // todo
Expand Down Expand Up @@ -133,34 +126,32 @@ type Out<T> = (T, Vec<(T, Expression<T>)>);
fn get_default_and_list_of_terms<T: FromStr, XR: XmlReader<BR>, BR: BufRead>(
xml: &mut XR,
) -> Result<Out<T>, XmlReadingError> {
// firs should be the default
let default_element = expect_opening_of(xml, "defaultTerm")?;

let default_val = result_level_from_attributes(&default_element)?;

expect_closure_of(xml, "defaultTerm")?;

// expect_opening_of("functionTerms", xml)?; // already inside "functionTerms" List; first item was default element
// expect_opening_of("listOfFunctionTerms", xml)?; // already inside "listOfFunctionTerms"; first item was the default element
let values_and_expressions = map_list(
xml,
"listOfFunctionTerms",
"functionTerm",
process_function_term_item,
|xml, current| process_function_term_item(xml, &current),
)?;

Ok((default_val, values_and_expressions))
}

fn process_function_term_item<T: FromStr, XR: XmlReader<BR>, BR: BufRead>(
xml: &mut XR,
current: StartElementWrapper,
current: &StartElementWrapper,
) -> Result<(T, Expression<T>), XmlReadingError> {
let res_lvl = result_level_from_attributes(&current)?;
let res_lvl = result_level_from_attributes(current)?;

expect_opening_of(xml, "math")?;
// try_from_xml expects to have the first apply already opened
expect_opening_of(xml, "apply")?;

expect_opening_of(xml, "apply")?; // open this tag to satisfy `Expression::try_from_xml`s precondition
let exp = Expression::try_from_xml(xml)?;

expect_closure_of(xml, "math")?;
Expand All @@ -181,5 +172,5 @@ fn result_level_from_attributes<T: FromStr>(
attribute_with_result_lvl
.value
.parse::<T>()
.map_err(|_| XmlReadingError::ParsingError(attribute_with_result_lvl.value))
.map_err(|_| XmlReadingError::ParsingError(attribute_with_result_lvl.value.clone()))
}

0 comments on commit 7605c32

Please sign in to comment.