diff --git a/src/system.rs b/src/system.rs index 87d2983..54e246c 100644 --- a/src/system.rs +++ b/src/system.rs @@ -2,22 +2,22 @@ pub mod variable_update_function { use crate::expression_components::expression::Expression; pub struct VariableUpdateFn { - // pub input_vars_names: Vec, // 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, + pub target_var_name: String, pub terms: Vec<(T, Expression)>, pub default: T, } impl VariableUpdateFn { pub fn new( - // input_vars_names: Vec, - // target_var_name: String, + input_vars_names: Vec, + target_var_name: String, terms: Vec<(T, Expression)>, default: T, ) -> Self { Self { - // input_vars_names, - // target_var_name, + input_vars_names, + target_var_name, terms, default, } diff --git a/src/xml_parsing/expression_parser.rs b/src/xml_parsing/expression_parser.rs index 19b4d75..ca88cc1 100644 --- a/src/xml_parsing/expression_parser.rs +++ b/src/xml_parsing/expression_parser.rs @@ -54,6 +54,7 @@ impl ToString for LogicalOperator { } impl Expression { + // todo likely should not be an associated fn - or make it consistant with `variable_update_fn` - from-xml-parser pub fn try_from_xml(xml: &mut XR) -> Result where XR: XmlReader
, @@ -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; @@ -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, }) } diff --git a/src/xml_parsing/utils.rs b/src/xml_parsing/utils.rs index fa1c042..69956b3 100644 --- a/src/xml_parsing/utils.rs +++ b/src/xml_parsing/utils.rs @@ -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 { @@ -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 + ) + } } } } @@ -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, BR: BufRead>( - xml: &mut XR, - expected_name: &str, -) -> Result<(), XmlReadingError> { +pub fn find_start_of(xml: &mut XR, expected_name: &str) -> Result<(), XmlReadingError> +where + XR: XmlReader
, + BR: BufRead, +{ loop { match xml.next()? { xml::reader::XmlEvent::StartElement { name: n, .. } @@ -258,3 +273,42 @@ pub fn find_start_of, 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( + xml: &mut XR, + element_name: &str, +) -> Result<(), XmlReadingError> +where + XR: XmlReader
, + 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, + } + } +} diff --git a/src/xml_parsing/variable_update_fn_parser.rs b/src/xml_parsing/variable_update_fn_parser.rs index e419713..79309f3 100644 --- a/src/xml_parsing/variable_update_fn_parser.rs +++ b/src/xml_parsing/variable_update_fn_parser.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] // todo remove + use std::{io::BufRead, str::FromStr}; use xml::reader::XmlEvent; @@ -13,72 +15,64 @@ use super::{ xml_reader::XmlReader, }; -/// expects the xml reader to be at the start of the element -impl VariableUpdateFn { - pub fn try_from_xml, BR: BufRead>( - xml: &mut XR, - ) -> Result { - 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 XML element into a VariableUpdateFn struct. +/// Expects the parameter `xml` to be at the start of the XML element. +pub fn try_variable_update_fn_from_xml( + xml: &mut XR, +) -> Result, XmlReadingError> +where + XR: XmlReader
, + 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, BR: BufRead>( @@ -95,7 +89,6 @@ fn process_input_var_name_item, 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 @@ -133,19 +126,18 @@ type Out = (T, Vec<(T, Expression)>); fn get_default_and_list_of_terms, BR: BufRead>( xml: &mut XR, ) -> Result, 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, ¤t), )?; Ok((default_val, values_and_expressions)) @@ -153,14 +145,13 @@ fn get_default_and_list_of_terms, BR: BufRead>( fn process_function_term_item, BR: BufRead>( xml: &mut XR, - current: StartElementWrapper, + current: &StartElementWrapper, ) -> Result<(T, Expression), XmlReadingError> { - let res_lvl = result_level_from_attributes(¤t)?; + 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")?; @@ -181,5 +172,5 @@ fn result_level_from_attributes( attribute_with_result_lvl .value .parse::() - .map_err(|_| XmlReadingError::ParsingError(attribute_with_result_lvl.value)) + .map_err(|_| XmlReadingError::ParsingError(attribute_with_result_lvl.value.clone())) }