From: bakaq Date: Sat, 7 Sep 2024 04:15:05 +0000 (-0300) Subject: Rename LeafAnswer X-Git-Tag: v0.10.0~92^2~26 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=29fc55cb28f877c0dbd67947ff893c2d7c9180a7;p=scryer-prolog.git Rename LeafAnswer --- diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index a4233696..52fb3e02 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -11,7 +11,7 @@ use indexmap::IndexMap; use super::{ streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, MachineConfig, - QueryResolutionLine, QueryResult, PrologTerm, + LeafAnswer, PrologTerm, }; pub struct QueryState<'a> { @@ -31,7 +31,7 @@ impl Drop for QueryState<'_> { } impl Iterator for QueryState<'_> { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { let var_names = &mut self.var_names; @@ -82,10 +82,10 @@ impl Iterator for QueryState<'_> { if machine.machine_st.p == LIB_QUERY_SUCCESS { if term_write_result.var_dict.is_empty() { self.machine.machine_st.backtrack(); - return Some(Ok(QueryResolutionLine::True)); + return Some(Ok(LeafAnswer::True)); } } else if machine.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC { - return Some(Ok(QueryResolutionLine::False)); + return Some(Ok(LeafAnswer::False)); } let mut bindings: BTreeMap = BTreeMap::new(); @@ -138,7 +138,7 @@ impl Iterator for QueryState<'_> { // choice point, so we should break. self.machine.machine_st.backtrack(); - Some(Ok(QueryResolutionLine::Match(bindings))) + Some(Ok(LeafAnswer::LeafAnswer { bindings: bindings, residual_goals: vec![] })) } } @@ -187,11 +187,7 @@ impl Machine { self.machine_st.block = stub_b; } - pub fn run_query(&mut self, query: String) -> QueryResult { - self.run_query_iter(query).collect() - } - - pub fn run_query_iter(&mut self, query: String) -> QueryState { + pub fn run_query(&mut self, query: String) -> QueryState { let mut parser = Parser::new( Stream::from_owned_string(query, &mut self.machine_st.arena), &mut self.machine_st, @@ -767,14 +763,14 @@ mod tests { iterator.next(); - assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False))); + assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False))); assert_eq!(iterator.next(), None); } { let mut iterator = machine.run_query_iter("false.".into()); - assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False))); + assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False))); assert_eq!(iterator.next(), None); } } @@ -786,8 +782,8 @@ mod tests { let mut iterator = machine.run_query_iter("true;false.".into()); - assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::True))); - assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False))); + assert_eq!(iterator.next(), Some(Ok(LeafAnswer::True))); + assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False))); assert_eq!(iterator.next(), None); } diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index b1641ef4..4ab1f91a 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -7,126 +7,19 @@ use indexmap::IndexMap; use ordered_float::OrderedFloat; use std::cmp::Ordering; use std::collections::BTreeMap; -use std::collections::HashMap; -use std::fmt::Display; -use std::fmt::Write; -use std::iter::FromIterator; use super::Machine; use super::{HeapCellValue, Number}; -pub type QueryResult = Result; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResolution { - True, - False, - Matches(Vec), -} - -fn write_prolog_value_as_json( - writer: &mut W, - value: &PrologTerm, -) -> Result<(), std::fmt::Error> { - match value { - PrologTerm::Integer(i) => write!(writer, "{}", i), - PrologTerm::Float(f) => write!(writer, "{}", f), - PrologTerm::Rational(r) => write!(writer, "{}", r), - PrologTerm::Atom(a) => writer.write_str(a.as_str()), - PrologTerm::String(s) => { - if let Err(_e) = serde_json::from_str::(s.as_str()) { - //treat as string literal - //escape double quotes - write!( - writer, - "\"{}\"", - s.replace('\"', "\\\"") - .replace('\n', "\\n") - .replace('\t', "\\t") - .replace('\r', "\\r") - ) - } else { - //return valid json string - writer.write_str(s) - } - } - PrologTerm::List(l) => { - writer.write_char('[')?; - if let Some((first, rest)) = l.split_first() { - write_prolog_value_as_json(writer, first)?; - - for other in rest { - writer.write_char(',')?; - write_prolog_value_as_json(writer, other)?; - } - } - writer.write_char(']') - } - PrologTerm::Structure(s, l) => { - write!(writer, "\"{}\":[", s.as_str())?; - - if let Some((first, rest)) = l.split_first() { - write_prolog_value_as_json(writer, first)?; - for other in rest { - writer.write_char(',')?; - write_prolog_value_as_json(writer, other)?; - } - } - writer.write_char(']') - } - _ => writer.write_str("null"), - } -} - -fn write_prolog_match_as_json( - writer: &mut W, - query_match: &QueryMatch, -) -> Result<(), std::fmt::Error> { - writer.write_char('{')?; - let mut iter = query_match.bindings.iter(); - - if let Some((k, v)) = iter.next() { - write!(writer, "\"{k}\":")?; - write_prolog_value_as_json(writer, v)?; - - for (k, v) in iter { - write!(writer, ",\"{k}\":")?; - write_prolog_value_as_json(writer, v)?; - } - } - writer.write_char('}') -} - -impl Display for QueryResolution { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - QueryResolution::True => f.write_str("true"), - QueryResolution::False => f.write_str("false"), - QueryResolution::Matches(matches) => { - f.write_char('[')?; - if let Some((first, rest)) = matches.split_first() { - write_prolog_match_as_json(f, first)?; - for other in rest { - f.write_char(',')?; - write_prolog_match_as_json(f, other)?; - } - } - f.write_char(']') - } - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct QueryMatch { - pub bindings: BTreeMap, -} - #[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResolutionLine { +pub enum LeafAnswer { True, False, - Match(BTreeMap), + Exception(PrologTerm), + LeafAnswer { + bindings: BTreeMap, + residual_goals: Vec, + }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -396,248 +289,3 @@ impl PrologTerm { term_stack.pop().unwrap() } } - -impl From> for QueryMatch { - fn from(bindings: BTreeMap<&str, PrologTerm>) -> Self { - QueryMatch { - bindings: bindings - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect::>(), - } - } -} - -impl From> for QueryMatch { - fn from(bindings: BTreeMap) -> Self { - QueryMatch { bindings } - } -} - -impl From> for QueryResolution { - 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() { - QueryResolutionLine::True => return QueryResolution::True, - QueryResolutionLine::False => return QueryResolution::False, - _ => {} - } - } - - // If there is only one line, and it is an empty match, return false. - if query_result_lines.len() == 1 { - if let QueryResolutionLine::Match(m) = query_result_lines[0].clone() { - if m.is_empty() { - return QueryResolution::False; - } - } - } - - // If there is at least one line with true and no matches, return true. - if query_result_lines - .iter() - .any(|l| l == &QueryResolutionLine::True) - && !query_result_lines - .iter() - .any(|l| matches!(l, QueryResolutionLine::Match(_))) - { - return QueryResolution::True; - } - - // If there is at least one match, return all matches. - let all_matches = query_result_lines - .into_iter() - .filter(|l| matches!(l, QueryResolutionLine::Match(_))) - .map(|l| match l { - QueryResolutionLine::Match(m) => QueryMatch::from(m), - _ => unreachable!(), - }) - .collect::>(); - - if !all_matches.is_empty() { - return QueryResolution::Matches(all_matches); - } - - QueryResolution::False - } -} - -impl FromIterator for QueryResolution { - fn from_iter>(iter: I) -> Self { - // TODO: Probably a good idea to implement From> based on this - // instead. - iter.into_iter().collect::>().into() - } -} - -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 - 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(); - let value = value.split('\t').next().unwrap().to_string(); - let value = value.split("error").next().unwrap().to_string(); - map.insert(key, value); - } - - map -} - -impl TryFrom for QueryResolutionLine { - type Error = (); - fn try_from(string: String) -> Result { - match string.as_str() { - "true" => Ok(QueryResolutionLine::True), - "false" => Ok(QueryResolutionLine::False), - _ => Ok(QueryResolutionLine::Match( - parse_prolog_response(&string) - .iter() - .map(|(k, v)| -> Result<(String, PrologTerm), ()> { - let key = k.to_string(); - let value = v.to_string(); - Ok((key, PrologTerm::try_from(value)?)) - }) - .filter_map(Result::ok) - .collect::>(), - )), - } - } -} - -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 PrologTerm { - type Error = (); - fn try_from(string: String) -> Result { - let trimmed = string.trim(); - - if let Ok(float_value) = string.parse::() { - Ok(PrologTerm::Float(OrderedFloat(float_value))) - } else if let Ok(int_value) = string.parse::() { - Ok(PrologTerm::Integer(int_value.into())) - } else if trimmed.starts_with('\'') && trimmed.ends_with('\'') - || trimmed.starts_with('"') && trimmed.ends_with('"') - { - Ok(PrologTerm::String(trimmed[1..trimmed.len() - 1].into())) - } else if trimmed.starts_with('[') && trimmed.ends_with(']') { - let split = split_nested_list(&trimmed[1..trimmed.len() - 1]); - - let values = split - .into_iter() - .map(PrologTerm::try_from) - .collect::, _>>()?; - - Ok(PrologTerm::List(values)) - } else if trimmed.starts_with('{') && trimmed.ends_with('}') { - let iter = trimmed[1..trimmed.len() - 1].split(','); - let mut values = vec![]; - - for value in iter { - let items: Vec<_> = value.split(':').collect(); - if items.len() == 2 { - let _key = items[0].to_string(); - let value = items[1].to_string(); - values.push(PrologTerm::try_from(value)?); - } - } - - Ok(PrologTerm::Structure("{}".into(), values)) - } else if trimmed.starts_with("<<") && trimmed.ends_with(">>") { - let iter = trimmed[2..trimmed.len() - 2].split(','); - let mut values = vec![]; - - for value in iter { - let items: Vec<_> = value.split(':').collect(); - if items.len() == 2 { - let _key = items[0].to_string(); - let value = items[1].to_string(); - values.push(PrologTerm::try_from(value)?); - } - } - - Ok(PrologTerm::Structure("<<>>".into(), values)) - } else if !trimmed.contains(',') && !trimmed.contains('\'') && !trimmed.contains('"') { - Ok(PrologTerm::String(trimmed.into())) - } else { - Err(()) - } - } -} - -impl From<&str> for PrologTerm { - fn from(str: &str) -> Self { - PrologTerm::String(str.to_string()) - } -}