use std::fmt::{Display, Error, Formatter};
use std::num::ParseFloatError;
use std::{collections::HashMap, str::FromStr};
use regex::{Captures, Match};
use human_regex::{
any, beginning, digit, end, exactly, multi_line_mode, named_capture, one_or_more, or,
printable, text, whitespace, word, zero_or_more, zero_or_one,
};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum OctaveType {
Scalar(f64),
Matrix(Vec<Vec<f64>>),
String(String),
CellArray(Vec<Vec<OctaveType>>),
Empty,
Error(String),
}
impl Default for OctaveType {
fn default() -> Self {
OctaveType::Empty
}
}
impl Display for OctaveType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match &self {
OctaveType::Scalar(scalar) => {
format!("{scalar}")
}
OctaveType::Matrix(vec) => {
format!("{vec:?}")
}
OctaveType::String(string) => {
format!("{string}")
}
OctaveType::CellArray(ot) => {
format!("{ot:?}")
}
OctaveType::Empty => {
format!("")
}
OctaveType::Error(message) => {
format!("Error: {message}")
}
}
)
}
}
impl OctaveType {
pub fn try_into_f64(self) -> Result<f64, ()> {
if let OctaveType::Scalar(value) = self {
return Ok(value.into());
} else {
Err(())
}
}
pub fn try_into_string(self) -> Result<String, ()> {
if let OctaveType::String(value) = self {
return Ok(value);
} else {
Err(())
}
}
pub fn try_into_vec_f64(self) -> Result<Vec<Vec<f64>>, ()> {
if let OctaveType::Matrix(value) = self {
return Ok(value);
} else {
Err(())
}
}
pub fn try_into_vec_octave_type(self) -> Result<Vec<Vec<OctaveType>>, ()> {
if let OctaveType::CellArray(value) = self {
return Ok(value);
} else {
Err(())
}
}
}
#[derive(Debug, Clone)]
pub struct InterpreterResults {
pub raw: String,
scalars: HashMap<String, f64>,
matrices: HashMap<String, Vec<Vec<f64>>>,
strings: HashMap<String, String>,
cell_arrays: HashMap<String, OctaveType>,
}
impl InterpreterResults {
pub fn get_unchecked(&self, name: &str) -> OctaveType {
match self.get_scalar_named(name) {
None => match self.get_matrix_named(name) {
None => match self.get_string_named(name) {
None => match self.get_cell_array_named(name) {
None => OctaveType::Empty,
Some(cell_array) => cell_array,
},
Some(string) => OctaveType::String(string),
},
Some(matrix) => OctaveType::Matrix(matrix),
},
Some(scalar) => OctaveType::Scalar(scalar),
}
}
pub fn get_scalar_named(&self, name: &str) -> Option<f64> {
self.scalars.get(name).cloned()
}
pub fn get_matrix_named(&self, name: &str) -> Option<Vec<Vec<f64>>> {
self.matrices.get(name).cloned()
}
pub fn get_string_named(&self, name: &str) -> Option<String> {
self.strings.get(name).cloned()
}
pub fn get_cell_array_named(&self, name: &str) -> Option<OctaveType> {
self.cell_arrays.get(name).cloned()
}
}
impl Default for InterpreterResults {
fn default() -> Self {
InterpreterResults {
raw: "".to_string(),
scalars: Default::default(),
matrices: Default::default(),
strings: Default::default(),
cell_arrays: Default::default(),
}
}
}
impl From<String> for InterpreterResults {
fn from(output: String) -> Self {
let mut results = InterpreterResults {
raw: output.clone(),
..Default::default()
};
let scalar_match = multi_line_mode(
beginning()
+ text("# name: ")
+ named_capture(one_or_more(word()), "name")
+ text("\n# type: scalar\n")
+ named_capture(exactly(1, beginning() + one_or_more(any()) + end()), "data"),
);
for capture in scalar_match.to_regex().captures_iter(&output) {
let (name, value) = parse_scalar_capture(capture);
results.scalars.insert(name, value);
}
let string_match = multi_line_mode(
beginning()
+ text("# name: ")
+ named_capture(one_or_more(word()), "name")
+ or(&[text("\n# type: sq_string"), text("\n# type: string")])
+ text("\n# elements: ")
+ named_capture(one_or_more(digit()), "elements")
+ text("\n# length: ")
+ named_capture(one_or_more(digit()), "length")
+ text("\n")
+ named_capture(exactly(1, beginning() + one_or_more(any()) + end()), "data"),
);
for capture in string_match.to_regex().captures_iter(&output) {
let (name, value) = parse_string_capture(capture);
results.strings.insert(name, value);
}
let matrix_match = multi_line_mode(
beginning()
+ text("# name: ")
+ named_capture(one_or_more(word()), "name")
+ or(&[text("\n# type: matrix"), text("\n# type: diagonal matrix")])
+ text("\n# rows: ")
+ named_capture(one_or_more(digit()), "rows")
+ text("\n# columns: ")
+ named_capture(one_or_more(digit()), "columns")
+ text("\n")
+ named_capture(zero_or_more(one_or_more(printable()) + text("\n")), "data"),
);
for capture in matrix_match.to_regex().captures_iter(&output) {
let (name, value) = parse_matrix_capture(capture);
results.matrices.insert(name, value);
}
let cell_array_match = multi_line_mode(
beginning()
+ text("# name: ")
+ named_capture(one_or_more(word()), "name")
+ text("\n# type: cell")
+ text("\n# rows: ")
+ named_capture(one_or_more(digit()), "rows")
+ text("\n# columns: ")
+ named_capture(one_or_more(digit()), "columns"),
);
let cell_element_match = multi_line_mode(
beginning()
+ named_capture(text("# name: <cell-element>\n"), "name")
+ text("# type: ")
+ named_capture(one_or_more(word()), "type")
+ text("\n# rows: ")
+ zero_or_more(named_capture(one_or_more(digit()), "rows"))
+ text("\n# columns: ")
+ zero_or_more(named_capture(one_or_more(digit()), "columns"))
+ text("\n# elements: ")
+ zero_or_more(named_capture(one_or_more(digit()), "elements"))
+ text("\n# length: ")
+ zero_or_more(named_capture(one_or_more(digit()), "length"))
+ zero_or_one(named_capture(
one_or_more(whitespace() + beginning() + one_or_more(any())),
"data",
)),
);
for capture in cell_array_match.to_regex().captures_iter(&output) {
let (name, value) = parse_cell_array_capture(
capture,
cell_element_match
.to_regex()
.captures_iter(&output)
.map(|x| x)
.collect::<Vec<Captures>>(),
);
results.cell_arrays.insert(name, value);
}
results
}
}
fn parse_scalar_capture(capture: Captures) -> (String, f64) {
(
capture
.name("name")
.expect("Name not found")
.as_str()
.to_string(),
f64::from_str(
&capture
.name("data")
.expect("No value for scalar data.")
.as_str()
.replace('\n', ""),
)
.expect("Could not parse f64 from string."),
)
}
fn parse_string_capture(capture: Captures) -> (String, String) {
let name = capture
.name("name")
.expect("Name not found")
.as_str()
.to_string();
(
name.clone(),
capture
.name("data")
.expect(&format!("No value named {name} for string data."))
.as_str()
.to_string(),
)
}
fn parse_cell_array_capture(
capture: Captures,
mut elements: Vec<Captures>,
) -> (String, OctaveType) {
println!("{elements:?}");
let name = capture
.name("name")
.expect("Name not found")
.as_str()
.to_string();
let rows = usize::from_str(capture.name("rows").expect("No key named rows.").as_str())
.expect("Could not parse usize from string.");
let columns = usize::from_str(
capture
.name("columns")
.expect("No key named columns.")
.as_str(),
)
.expect("Could not parse usize from string.");
let mut cell_array = vec![vec![OctaveType::Empty; columns]; rows];
(name, OctaveType::CellArray(cell_array))
}
fn parse_matrix_capture(capture: Captures) -> (String, Vec<Vec<f64>>) {
let name = capture
.name("name")
.expect("Name not found")
.as_str()
.to_string();
let rows = usize::from_str(capture.name("rows").expect("No key named rows.").as_str())
.expect("Could not parse usize from string.");
let columns = usize::from_str(
capture
.name("columns")
.expect("No key named columns.")
.as_str(),
)
.expect("Could not parse usize from string.");
let mut matrix = vec![vec![0.0_f64; columns]; rows];
matrix = match capture.name("data") {
None => matrix,
Some(s) => {
if capture.get(2).unwrap().as_str().contains("diagonal") {
s.as_str()
.replacen('\n', " ", rows - 1)
.replace('\n', "")
.split(' ')
.map(|elem| match f64::from_str(elem) {
Ok(val) => val,
Err(_) => f64::NAN,
})
.enumerate()
.map(|(idx, element)| matrix[idx][idx] = element)
.for_each(drop);
} else {
let data = s
.as_str()
.replacen(' ', "", 1)
.replace('\n', "")
.split(' ')
.map(|elem| match f64::from_str(elem) {
Ok(val) => val,
Err(_) => f64::NAN,
})
.collect::<Vec<f64>>();
let mut counter: usize = 0;
for i in 0..rows {
for j in 0..columns {
matrix[i][j] = data[counter];
counter += 1;
}
}
}
matrix
}
};
(name, matrix)
}