From fab5ca9440ce4391b2ed83647ac45a70a456a3cf Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 3 Oct 2023 20:19:50 +0200 Subject: [PATCH] Fix nested List parsing --- src/machine/lib_machine.rs | 45 ++++++++++++++++-- src/machine/parsed_results.rs | 87 +++++++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 83e5b048..65b4591f 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -321,11 +321,13 @@ mod tests { result, Ok(QueryResolution::Matches(vec![ QueryMatch::from(btreemap! { - "X" => Value::List(Vec::from([ - Value::Float(OrderedFloat(1.0)), - Value::Float(OrderedFloat(2.0)), - Value::Float(OrderedFloat(3.0)), - ])) + "X" => Value::List( + Vec::from([ + Value::Float(OrderedFloat::from(1.0)), + Value::Float(OrderedFloat::from(2.0)), + Value::Float(OrderedFloat::from(3.0)) + ]) + ) }), ])) ); @@ -437,6 +439,39 @@ mod tests { machine.consult_module_string("facts", code.to_string()); } } + } + + #[test] + fn findall() { + let mut machine = Machine::new_lib(); + + machine.consult_module_string( + "facts", + String::from( + r#" + triple("a", "p1", "b"). + triple("a", "p2", "b"). + "#, + ), + ); + + let query = String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#); + let output = machine.run_query(query); + assert_eq!( + output, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "Predicate" => Value::from("Predicate"), + "Result" => Value::List( + Vec::from([ + Value::List([Value::from("p1"), Value::from("b")].into()), + Value::List([Value::from("p2"), Value::from("b")].into()), + ]) + ), + "Target" => Value::from("Target"), + }), + ])) + ); } } diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index f0436da4..38632855 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -2,7 +2,6 @@ use crate::atom_table::*; use ordered_float::OrderedFloat; use dashu::*; use std::collections::BTreeMap; -use regex::Regex; use std::collections::HashMap; pub type QueryResult = Result; @@ -117,14 +116,56 @@ impl From> for QueryResolution { } } +fn split_response_string(input: &str) -> Vec { + let mut level_bracket = 0; + let mut level_parenthesis = 0; + let mut in_double_quotes = false; + let mut in_single_quotes = false; + let mut start = 0; + let mut result = Vec::new(); + + for (i, c) in input.chars().enumerate() { + match c { + '[' => level_bracket += 1, + ']' => level_bracket -= 1, + '(' => level_parenthesis += 1, + ')' => level_parenthesis -= 1, + '"' => in_double_quotes = !in_double_quotes, + '\'' => in_single_quotes = !in_single_quotes, + ',' if level_bracket == 0 && level_parenthesis == 0 && !in_double_quotes && !in_single_quotes => { + result.push(input[start..i].trim().to_string()); + start = i + 1; + } + _ => {} + } + } + + result.push(input[start..].trim().to_string()); + result +} + +fn split_key_value_pairs(input: &str) -> Vec<(String, String)> { + let items = split_response_string(input); + let mut result = Vec::new(); + + for item in items { + let parts: Vec<&str> = item.splitn(2, '=').collect(); + if parts.len() == 2 { + let key = parts[0].trim().to_string(); + let value = parts[1].trim().to_string(); + result.push((key, value)); + } + } + + result +} + fn parse_prolog_response(input: &str) -> HashMap { let mut map: HashMap = HashMap::new(); // Use regex to match strings including commas inside them - let re = Regex::new(r"(\w+)\s=\s((?:[^,\[]|\[[^\]]*\])*)").unwrap(); - - for cap in re.captures_iter(input) { - let key = cap[1].to_string(); - let value = cap[2].trim_end_matches(',').trim().to_string(); + for result in split_key_value_pairs(input) { + let key = result.0; + let value = result.1; // cut off at given characters/strings: let value = value.split("\n").next().unwrap().to_string(); let value = value.split(" ").next().unwrap().to_string(); @@ -158,6 +199,27 @@ impl TryFrom for QueryResolutionLine { } } +fn split_nested_list(input: &str) -> Vec { + let mut level = 0; + let mut start = 0; + let mut result = Vec::new(); + + for (i, c) in input.chars().enumerate() { + match c { + '[' => level += 1, + ']' => level -= 1, + ',' if level == 0 => { + result.push(input[start..i].trim().to_string()); + start = i + 1; + } + _ => {} + } + } + + result.push(input[start..].trim().to_string()); + result +} + impl TryFrom for Value { type Error = (); fn try_from(string: String) -> Result { @@ -172,13 +234,12 @@ impl TryFrom for Value { } 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())?); - } + let split = split_nested_list(&trimmed[1..trimmed.len() - 1]); + + let values = split + .into_iter() + .map(Value::try_from) + .collect::, _>>()?; Ok(Value::List(values)) } else if trimmed.starts_with("{") && trimmed.ends_with("}") { -- 2.54.0