From f324c9591dc9b23fcb7350e781743db797ca0141 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 10:34:07 +0200 Subject: [PATCH] Refactor result parsing to idiomatic Rust and extract into parsed_results.rs --- src/machine/mod.rs | 142 ++------------------------------ src/machine/parsed_results.rs | 149 ++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 134 deletions(-) create mode 100644 src/machine/parsed_results.rs diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 75f08e6c..1aca907f 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -15,6 +15,7 @@ pub mod machine_indices; pub mod machine_state; pub mod machine_state_impl; pub mod mock_wam; +pub mod parsed_results; pub mod partial_string; pub mod preprocessor; pub mod stack; @@ -46,13 +47,14 @@ use lazy_static::lazy_static; use ordered_float::OrderedFloat; use std::cmp::Ordering; -use std::collections::BTreeMap; use std::env; use std::io::Read; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use tokio::runtime::Runtime; +use self::parsed_results::*; + lazy_static! { pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false); } @@ -181,32 +183,6 @@ pub(crate) fn get_structure_index(value: HeapCellValue) -> Option { None } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResult { - True, - False, - Matches(Vec), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResultLine { - True, - False, - Match(BTreeMap), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Value { - Integer(Integer), - Rational(Rational), - Float(OrderedFloat), - Atom(Atom), - String(String), - List(Vec), - Structure(Atom, Vec), - Var, -} - impl Machine { #[inline] pub fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView, &mut MachineState) { @@ -360,118 +336,16 @@ impl Machine { pub fn parse_output(&self) -> QueryResult { let output = self.get_user_output(); - let parsed_lines = output.split(";") + output.split(";") .map(|s| s.trim()) .map(|s| s.replace(".", "")) .filter(|s| !s.is_empty()) - .map(|s| { - match s.as_str() { - "true" => QueryResultLine::True, - "false" => QueryResultLine::False, - _ => QueryResultLine::Match( - s.split(",") - .map(|s| s.trim()) - .filter(|s| !s.is_empty()) - .map(|s| { - let mut iter = s.split(" = "); - - let key = iter.next().unwrap().to_string(); - let value = iter.next().unwrap().to_string(); - - (key, Machine::parse_value(value)) - }) - .collect::>() - ) - } - }) - .collect::>(); - - // If there is only one line, and it is true or false, return that. - if parsed_lines.len() == 1 { - match parsed_lines[0].clone() { - QueryResultLine::True => return QueryResult::True, - QueryResultLine::False => return QueryResult::False, - _ => {} - } - } - - // If there is at least one line with true and no matches, return true. - if parsed_lines.iter().any(|l| l == &QueryResultLine::True) - && !parsed_lines.iter().any(|l| { - if let &QueryResultLine::Match(_) = l { true } else { false } - }) { - return QueryResult::True; - } - - // If there is at least one match, return all matches. - if parsed_lines.iter().any(|l| { - if let &QueryResultLine::Match(_) = l { true } else { false } - }) { - let all_matches = parsed_lines.into_iter() - .filter(|l| { - if let &QueryResultLine::Match(_) = l { true } else { false } - }) - .collect::>(); - return QueryResult::Matches(all_matches); - } - - QueryResult::False + .map(QueryResultLine::try_from) + .filter_map(Result::ok) + .collect::>() + .into() } - pub fn parse_value(string: String) -> Value { - let trimmed = string.trim(); - - if trimmed.starts_with("'") && trimmed.ends_with("'") { - Value::String(trimmed[1..trimmed.len() - 1].into()) - } else - if trimmed.starts_with("\"") && trimmed.ends_with("\"") { - Value::String(trimmed[1..trimmed.len() - 1].into()) - } else if trimmed.starts_with("[") && trimmed.ends_with("]") { - let mut iter = trimmed[1..trimmed.len() - 1].split(","); - - let mut values = vec![]; - - while let Some(value) = iter.next() { - values.push(Machine::parse_value(value.to_string())); - } - - Value::List(values) - } else if trimmed.starts_with("{") && trimmed.ends_with("}") { - let mut iter = trimmed[1..trimmed.len() - 1].split(","); - - let mut values = vec![]; - - while let Some(value) = iter.next() { - let mut iter = value.split(":"); - - let key = iter.next().unwrap().to_string(); - let value = iter.next().unwrap().to_string(); - - values.push(Machine::parse_value(value)); - } - - Value::Structure(atom!("{}"), values) - } else if trimmed.starts_with("<<") && trimmed.ends_with(">>") { - let mut iter = trimmed[2..trimmed.len() - 2].split(","); - - let mut values = vec![]; - - while let Some(value) = iter.next() { - let mut iter = value.split(":"); - - let key = iter.next().unwrap().to_string(); - let value = iter.next().unwrap().to_string(); - - values.push(Machine::parse_value(value)); - } - - Value::Structure(atom!("<<>>"), values) - } else { - Value::String(string) - } - } - - pub(crate) fn configure_modules(&mut self) { fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir, arena: &mut Arena) { for arity in 1..66 { diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs new file mode 100644 index 00000000..4bc562db --- /dev/null +++ b/src/machine/parsed_results.rs @@ -0,0 +1,149 @@ +use ordered_float::OrderedFloat; +use rug::*; +use std::{collections::BTreeMap}; +use crate::atom_table::*; + +use super::Machine; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryResult { + True, + False, + Matches(Vec), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryResultLine { + True, + False, + Match(BTreeMap), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Value { + Integer(Integer), + Rational(Rational), + Float(OrderedFloat), + Atom(Atom), + String(String), + List(Vec), + Structure(Atom, Vec), + Var, +} + +impl From> for QueryResult { + fn from(query_result_lines: Vec) -> Self { + // If there is only one line, and it is true or false, return that. + if query_result_lines.len() == 1 { + match query_result_lines[0].clone() { + QueryResultLine::True => return QueryResult::True, + QueryResultLine::False => return QueryResult::False, + _ => {} + } + } + + // If there is at least one line with true and no matches, return true. + if query_result_lines.iter().any(|l| l == &QueryResultLine::True) + && !query_result_lines.iter().any(|l| { + if let &QueryResultLine::Match(_) = l { true } else { false } + }) { + return QueryResult::True; + } + + // If there is at least one match, return all matches. + if query_result_lines.iter().any(|l| { + if let &QueryResultLine::Match(_) = l { true } else { false } + }) { + let all_matches = query_result_lines.into_iter() + .filter(|l| { + if let &QueryResultLine::Match(_) = l { true } else { false } + }) + .collect::>(); + return QueryResult::Matches(all_matches); + } + + QueryResult::False + } +} + + +impl TryFrom for QueryResultLine { + type Error = (); + fn try_from(string: String) -> Result { + match string.as_str() { + "true" => Ok(QueryResultLine::True), + "false" => Ok(QueryResultLine::False), + _ => Ok(QueryResultLine::Match( + string.split(",") + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .map(|s| -> Result<(String, Value), ()>{ + let mut iter = s.split(" = "); + + let key = iter.next().unwrap().to_string(); + let value = iter.next().unwrap().to_string(); + + Ok((key, Value::try_from(value)?)) + }) + .filter_map(Result::ok) + .collect::>() + )) + } + } +} + +impl TryFrom for Value { + type Error = (); + fn try_from(string: String) -> Result { + let trimmed = string.trim(); + + if trimmed.starts_with("'") && trimmed.ends_with("'") { + Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) + } else + if trimmed.starts_with("\"") && trimmed.ends_with("\"") { + Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) + } else if trimmed.starts_with("[") && trimmed.ends_with("]") { + let mut iter = trimmed[1..trimmed.len() - 1].split(","); + + let mut values = vec![]; + + while let Some(s) = iter.next() { + values.push(Value::try_from(s.to_string())?); + } + + Ok(Value::List(values)) + } else if trimmed.starts_with("{") && trimmed.ends_with("}") { + let mut iter = trimmed[1..trimmed.len() - 1].split(","); + + let mut values = vec![]; + + while let Some(value) = iter.next() { + let mut iter = value.split(":"); + + let key = iter.next().unwrap().to_string(); + let value = iter.next().unwrap().to_string(); + + values.push(Value::try_from(value)?); + } + + Ok(Value::Structure(atom!("{}"), values)) + } else if trimmed.starts_with("<<") && trimmed.ends_with(">>") { + let mut iter = trimmed[2..trimmed.len() - 2].split(","); + + let mut values = vec![]; + + while let Some(value) = iter.next() { + let mut iter = value.split(":"); + + let key = iter.next().unwrap().to_string(); + let value = iter.next().unwrap().to_string(); + + values.push(Value::try_from(value)?); + } + + Ok(Value::Structure(atom!("<<>>"), values)) + } else { + Err(()) + } + } +} \ No newline at end of file -- 2.54.0