From: Nicolas Luck Date: Fri, 21 Jul 2023 12:35:44 +0000 (+0200) Subject: Fix result parsing for complex string results X-Git-Tag: remove^2~54 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=9e85be11fecd00b3d0be8b828eb0dd5cfb57e60a;p=scryer-prolog.git Fix result parsing for complex string results --- diff --git a/Cargo.lock b/Cargo.lock index 29cf7f67..5ed25656 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -138,7 +147,7 @@ checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", "memchr", - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -1673,12 +1682,41 @@ version = "0.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6" +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1848,6 +1886,7 @@ dependencies = [ "proc-macro2 1.0.47", "quote 1.0.21", "ref_thread_local", + "regex", "ring", "ripemd160", "roxmltree", diff --git a/Cargo.toml b/Cargo.toml index a6f97b04..c64b7510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" tokio = { version = "1", features = ["full"] } futures = "0.3" +regex = "1.9.1" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index f88577ce..1dfdd2b2 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -83,4 +83,56 @@ mod tests { Err(String::from("error(existence_error(procedure,triple/3),triple/3).")) ); } + + #[test] + fn complex_results() { + let mut machine = Machine::new_lib(); + machine.load_module_string( + "facts", + r#" + :- discontiguous(subject_class/2). + :- discontiguous(constructor/2). + + subject_class("Todo", c). + constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + + subject_class("Recipe", xyz). + constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]'). + "#.to_string()); + + let result = machine.run_query(String::from("subject_class(\"Todo\", C), constructor(C, Actions).")); + assert_eq!( + result, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "C" => Value::from("c"), + "Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]"), + }), + ])) + ); + + let result = machine.run_query(String::from("subject_class(\"Recipe\", C), constructor(C, Actions).")); + assert_eq!( + result, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "C" => Value::from("xyz"), + "Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]"), + }), + ])) + ); + + let result = machine.run_query(String::from("subject_class(Class, _).")); + assert_eq!( + result, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "Class" => Value::from("Todo") + }), + QueryMatch::from(btreemap! { + "Class" => Value::from("Recipe") + }), + ])) + ); + } } diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index f34eadea..ba33089d 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -2,6 +2,8 @@ use crate::atom_table::*; use ordered_float::OrderedFloat; use rug::*; use std::collections::BTreeMap; +use regex::Regex; +use std::collections::HashMap; pub type QueryResult = Result; @@ -103,6 +105,25 @@ impl From> for QueryResolution { } } +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(); + // 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 { @@ -110,20 +131,17 @@ impl TryFrom for QueryResolutionLine { "true" => Ok(QueryResolutionLine::True), "false" => Ok(QueryResolutionLine::False), _ => Ok(QueryResolutionLine::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().ok_or(())?.to_string(); - let value = iter.next().ok_or(())?.to_string(); - + parse_prolog_response(&string) + .iter() + .map(|(k, v)| -> Result<(String, Value), ()> { + let key = k.to_string(); + let value = v.to_string(); Ok((key, Value::try_from(value)?)) }) .filter_map(Result::ok) - .collect::>(), - )), + .collect::>() + ) + ), } } } @@ -175,6 +193,8 @@ impl TryFrom for Value { } Ok(Value::Structure(atom!("<<>>"), values)) + } else if !trimmed.contains(",") && !trimmed.contains("'") && !trimmed.contains("\"") { + Ok(Value::String(trimmed.into())) } else { Err(()) }