From 95b31146b58f2bae0e15b057321e4c194174cd92 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 11 Jul 2023 14:22:27 +0200 Subject: [PATCH 01/66] Add Machine::set_user_input(&mut self, input: String) and get_user_output() -> String. Make read_term_from_user_input() handle Stream::Byte. --- src/machine/machine_state.rs | 4 ++++ src/machine/mod.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 6aa519167..50431a8aa 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -495,6 +495,10 @@ impl MachineState { } } + if let Stream::Byte(_) = stream { + return self.read_term(stream, indices) + } + unreachable!("Stream must be a Stream::Readline(_)") } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 4cd1fce83..f7beecd3b 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -47,6 +47,7 @@ use ordered_float::OrderedFloat; use std::cmp::Ordering; use std::env; +use std::io::Read; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use tokio::runtime::Runtime; @@ -306,6 +307,15 @@ impl Machine { self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1)); } + pub fn set_user_input(&mut self, input: String) { + self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena); + } + + pub fn get_user_output(&mut self) -> String { + let output_bytes: Vec<_> = self.user_output.bytes().map(|b| b.unwrap()).collect(); + String::from_utf8(output_bytes).unwrap() + } + 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 { From 112d398175f244d1fbf5190c1f128b55beadf71c Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 11 Jul 2023 14:24:30 +0200 Subject: [PATCH 02/66] Add Machine::run_input_once() which reads one goal from user input and runs it --- src/machine/mod.rs | 4 ++++ src/toplevel.pl | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index f7beecd3b..9a1c978aa 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -307,6 +307,10 @@ impl Machine { self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1)); } + pub fn run_input_once(&mut self) { + self.run_module_predicate(atom!("$toplevel"), (atom!("run_input_once"), 0)); + } + pub fn set_user_input(&mut self, input: String) { self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena); } diff --git a/src/toplevel.pl b/src/toplevel.pl index 8caea7ba8..56b8df947 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -430,3 +430,6 @@ % is expected to be printed instead. ; print_exception(E) ). + +run_input_once :- + catch(read_and_match, E, print_exception(E)). From 2f45f0cfed9bcc3f89e8d5aceeeb359f60f55d56 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 11 Jul 2023 14:51:58 +0200 Subject: [PATCH 03/66] Add convenience methods Machine::load_module_string() and Machine::run_query() --- src/machine/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 9a1c978aa..2d5cff2eb 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -320,6 +320,17 @@ impl Machine { String::from_utf8(output_bytes).unwrap() } + pub fn load_module_string(&mut self, module_name: &str, program: String) { + let stream = Stream::from_owned_string(program, &mut self.machine_st.arena); + self.load_file(module_name, stream); + } + + pub fn run_query(&mut self, query: String) -> String{ + self.set_user_input(query); + self.run_input_once(); + self.get_user_output() + } + 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 { From 703efdb22db03edea9e7e793219a9cabda64ed1b Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 00:03:47 +0200 Subject: [PATCH 04/66] Make run_input_once/0 match and print all results --- src/toplevel.pl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 56b8df947..93b1a60d3 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -432,4 +432,25 @@ ). run_input_once :- - catch(read_and_match, E, print_exception(E)). + bb_put('$report_all', true), + catch(read_and_match_all_results, E, print_exception(E)). + +read_and_match_all_results :- + '$read_query_term'(_, Term, _, _, VarList), + bb_put('$answer_count', 0), + submit_query_and_print_all_results(Term, VarList). + +submit_query_and_print_all_results(Term, VarList) :- + '$get_b_value'(B), + bb_put('$report_all', true), + bb_put('$report_n_more', 0), + call(user:Term), + write_eqs_and_read_input(B, VarList), + !. +submit_query_and_print_all_results(_, _) :- + ( bb_get('$answer_count', 0) -> + write(' ') + ; true + ), + write('false.'), + nl. From 568abef5b8af9e961d3c7a95a896e8071de83962 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 01:24:39 +0200 Subject: [PATCH 05/66] Parsed QueryResult --- src/machine/mod.rs | 150 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 3 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 2d5cff2eb..b3e5b2e34 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -46,6 +46,7 @@ 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; @@ -180,6 +181,32 @@ 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) { @@ -315,7 +342,7 @@ impl Machine { self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena); } - pub fn get_user_output(&mut self) -> String { + pub fn get_user_output(&self) -> String { let output_bytes: Vec<_> = self.user_output.bytes().map(|b| b.unwrap()).collect(); String::from_utf8(output_bytes).unwrap() } @@ -325,12 +352,129 @@ impl Machine { self.load_file(module_name, stream); } - pub fn run_query(&mut self, query: String) -> String{ + pub fn run_query(&mut self, query: String) -> QueryResult{ self.set_user_input(query); self.run_input_once(); - self.get_user_output() + self.parse_output() + } + + pub fn parse_output(&self) -> QueryResult { + let output = self.get_user_output(); + println!("parse_output output: {:?}", output); + let parsed_lines = 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::>(); + + println!("parsed_lines: {:?}", parsed_lines); + + // 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 + } + + 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 { From 3347f830c7ac852eceedbd7224273d562223f51e Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 01:26:54 +0200 Subject: [PATCH 06/66] Remove debug println!s --- src/machine/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index b3e5b2e34..75f08e6c8 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -360,7 +360,6 @@ impl Machine { pub fn parse_output(&self) -> QueryResult { let output = self.get_user_output(); - println!("parse_output output: {:?}", output); let parsed_lines = output.split(";") .map(|s| s.trim()) .map(|s| s.replace(".", "")) @@ -387,8 +386,6 @@ impl Machine { }) .collect::>(); - println!("parsed_lines: {:?}", parsed_lines); - // If there is only one line, and it is true or false, return that. if parsed_lines.len() == 1 { match parsed_lines[0].clone() { From f324c9591dc9b23fcb7350e781743db797ca0141 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 10:34:07 +0200 Subject: [PATCH 07/66] 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 75f08e6c8..1aca907f6 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 000000000..4bc562db8 --- /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 From f65675836c5afe2daf3f08c56ac24f52ad29e329 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 11:38:53 +0200 Subject: [PATCH 08/66] Fix build warnings --- src/machine/mock_wam.rs | 2 -- src/machine/parsed_results.rs | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index f18d95758..2c16ba3af 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -316,8 +316,6 @@ impl Machine { } pub fn test_load_file(&mut self, file: &str) -> Vec { - use std::io::Read; - let stream = Stream::from_owned_string( std::fs::read_to_string(AsRef::::as_ref(file)).unwrap(), &mut self.machine_st.arena, diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 4bc562db8..3b47ad6b4 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -1,9 +1,8 @@ use ordered_float::OrderedFloat; use rug::*; -use std::{collections::BTreeMap}; +use std::collections::BTreeMap; use crate::atom_table::*; -use super::Machine; #[derive(Debug, Clone, PartialEq, Eq)] pub enum QueryResult { @@ -120,7 +119,7 @@ impl TryFrom for Value { while let Some(value) = iter.next() { let mut iter = value.split(":"); - let key = iter.next().unwrap().to_string(); + let _key = iter.next().unwrap().to_string(); let value = iter.next().unwrap().to_string(); values.push(Value::try_from(value)?); @@ -135,7 +134,7 @@ impl TryFrom for Value { while let Some(value) = iter.next() { let mut iter = value.split(":"); - let key = iter.next().unwrap().to_string(); + let _key = iter.next().unwrap().to_string(); let value = iter.next().unwrap().to_string(); values.push(Value::try_from(value)?); From c0dd94c8a3c4bf37961575324a7bd21792845088 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 12 Jul 2023 11:57:53 +0200 Subject: [PATCH 09/66] Add test for programatic queries --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/lib.rs | 2 ++ src/machine/mod.rs | 37 ++++++++++++++++++++++++ src/machine/parsed_results.rs | 54 +++++++++++++++++++++++++++++------ 5 files changed, 92 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05e549112..29cf7f677 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,6 +1004,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "markup5ever" version = "0.8.1" @@ -1833,6 +1839,7 @@ dependencies = [ "lazy_static", "lexical", "libc", + "maplit", "modular-bitfield", "native-tls", "ordered-float", diff --git a/Cargo.toml b/Cargo.toml index 6e42de23b..a6f97b046 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ futures = "0.3" assert_cmd = "1.0.3" predicates-core = "1.0.2" serial_test = "0.5.1" +maplit = "1.0.2" [patch.crates-io] modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } diff --git a/src/lib.rs b/src/lib.rs index 2846fd0e6..d643296fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #[macro_use] extern crate static_assertions; +#[cfg(test)] +#[macro_use] extern crate maplit; #[macro_use] pub mod macros; diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 1aca907f6..c45d7cb49 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -936,3 +936,40 @@ impl Machine { } } } + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn programatic_query() { + let mut machine = Machine::with_test_streams(); + + machine.load_module_string("facts", String::from(r#" + triple("a", "p1", "b"). + triple("a", "p2", "b"). + "#)); + + let query = String::from(r#"triple("a",P,"b")."#); + let output = machine.run_query(query); + assert_eq!(output, QueryResult::Matches(vec![ + QueryMatch::from(btreemap!{ + "P" => Value::from("p1"), + }), + QueryMatch::from(btreemap!{ + "P" => Value::from("p2"), + }), + ])); + + assert_eq!( + machine.run_query(String::from(r#"triple("a","p1","b")."#)), + QueryResult::True + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("x","y","z")."#)), + QueryResult::False + ); + } +} diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 3b47ad6b4..984c74395 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -8,7 +8,12 @@ use crate::atom_table::*; pub enum QueryResult { True, False, - Matches(Vec), + Matches(Vec), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QueryMatch { + pub bindings: BTreeMap } #[derive(Debug, Clone, PartialEq, Eq)] @@ -30,6 +35,24 @@ pub enum Value { Var, } +impl From> for QueryMatch { + fn from(bindings: BTreeMap<&str, Value>) -> 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 QueryResult { fn from(query_result_lines: Vec) -> Self { // If there is only one line, and it is true or false, return that. @@ -50,14 +73,21 @@ impl From> for QueryResult { } // 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::>(); + let all_matches = query_result_lines.into_iter() + .filter(|l| { + if let &QueryResultLine::Match(_) = l { true } else { false } + }) + .map(|l| { + match l { + QueryResultLine::Match(m) => { + QueryMatch::from(m) + }, + _ => unreachable!() + } + }) + .collect::>(); + + if !all_matches.is_empty() { return QueryResult::Matches(all_matches); } @@ -145,4 +175,10 @@ impl TryFrom for Value { Err(()) } } +} + +impl From<&str> for Value { + fn from(str: &str) -> Self { + Value::String(str.to_string()) + } } \ No newline at end of file From 5f8cc3c64b6669791361403d674b07b10657e9cb Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:34:26 +0200 Subject: [PATCH 10/66] WIP: refactor to generalize Machine::run_top_level() --- src/bin/scryer-prolog.rs | 4 +- src/lib_toplevel.pl | 62 ++++++++++++++++++++++ src/machine/config.rs | 32 +++++++++++ src/machine/lib_machine.rs | 71 +++++++++++++++++++++++++ src/machine/mock_wam.rs | 99 +--------------------------------- src/machine/mod.rs | 106 ++++++++++--------------------------- src/toplevel.pl | 26 +-------- 7 files changed, 196 insertions(+), 204 deletions(-) create mode 100644 src/lib_toplevel.pl create mode 100644 src/machine/config.rs create mode 100644 src/machine/lib_machine.rs diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index eae00fe23..a7519defb 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -6,6 +6,6 @@ fn main() { scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed); }).unwrap(); - let mut wam = machine::Machine::new(); - wam.run_top_level(); + let mut wam = machine::Machine::new(Default::default()); + wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)); } diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl new file mode 100644 index 000000000..7987a2ff8 --- /dev/null +++ b/src/lib_toplevel.pl @@ -0,0 +1,62 @@ +:- module('$toplevel', [argv/1, + copy_term/3]). + +:- use_module(library(atts), [call_residue_vars/2]). +:- use_module(library(charsio)). +:- use_module(library(error)). +:- use_module(library(files)). +:- use_module(library(iso_ext)). +:- use_module(library(lambda)). +:- use_module(library(lists)). +:- use_module(library(si)). + +:- use_module(library('$project_atts')). +:- use_module(library('$atts')). + +:- dynamic(disabled_init_file/0). + +:- dynamic(argv/1). + + +arg_type(g). +arg_type(t). +arg_type(g(_)). +arg_type(t(_)). + + + + +print_exception(E) :- + ( E == error('$interrupt_thrown', repl) -> nl % print the + % exception on a + % newline to evade + % "^C". + ; true + ), + loader:write_error(E), + nl. + + +run_input_once :- + bb_put('$report_all', true), + catch(read_and_match_all_results, E, print_exception(E)). + +read_and_match_all_results :- + '$read_query_term'(_, Term, _, _, VarList), + bb_put('$answer_count', 0), + submit_query_and_print_all_results(Term, VarList). + +submit_query_and_print_all_results(Term, VarList) :- + '$get_b_value'(B), + bb_put('$report_all', true), + bb_put('$report_n_more', 0), + call(user:Term), + write_eqs_and_read_input(B, VarList), + !. +submit_query_and_print_all_results(_, _) :- + ( bb_get('$answer_count', 0) -> + write(' ') + ; true + ), + write('false.'), + nl. diff --git a/src/machine/config.rs b/src/machine/config.rs new file mode 100644 index 000000000..6268c8d81 --- /dev/null +++ b/src/machine/config.rs @@ -0,0 +1,32 @@ +pub struct MachineConfig { + pub streams: StreamConfig, + pub toplevel: &'static str, +} + +pub enum StreamConfig { + Stdio, + Memory, +} + +impl Default for MachineConfig { + fn default() -> Self { + MachineConfig { + streams: StreamConfig::Stdio, + toplevel: include_str!("../toplevel.pl"), + } + } +} + +impl MachineConfig { + pub fn in_memory() -> Self { + MachineConfig { + streams: StreamConfig::Memory, + ..Default::default() + } + } + + pub fn with_toplevel(mut self, toplevel: &'static str) -> Self { + self.toplevel = toplevel; + self + } +} diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs new file mode 100644 index 000000000..7b7495610 --- /dev/null +++ b/src/machine/lib_machine.rs @@ -0,0 +1,71 @@ +use super::{Machine, MachineConfig, QueryResult, QueryResultLine, Atom}; + +impl Machine { + pub fn new_lib() -> Self { + Machine::new(MachineConfig::in_memory().with_toplevel(include_str!("../lib_toplevel.pl"))) + } + + pub fn run_query(&mut self, query: String) -> QueryResult { + self.set_user_input(query); + self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); + self.parse_output() + } + + pub fn parse_output(&self) -> QueryResult { + let output = self.get_user_output(); + output + .split(";") + .map(|s| s.trim()) + .map(|s| s.replace(".", "")) + .filter(|s| !s.is_empty()) + .map(QueryResultLine::try_from) + .filter_map(Result::ok) + .collect::>() + .into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::machine::{QueryMatch, Value}; + + #[test] + fn programatic_query() { + let mut machine = Machine::with_test_streams(); + + machine.load_module_string( + "facts", + String::from( + r#" + triple("a", "p1", "b"). + triple("a", "p2", "b"). + "#, + ), + ); + + let query = String::from(r#"triple("a",P,"b")."#); + let output = machine.run_query(query); + assert_eq!( + output, + QueryResult::Matches(vec![ + QueryMatch::from(btreemap! { + "P" => Value::from("p1"), + }), + QueryMatch::from(btreemap! { + "P" => Value::from("p2"), + }), + ]) + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("a","p1","b")."#)), + QueryResult::True + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("x","y","z")."#)), + QueryResult::False + ); + } +} diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 2c16ba3af..77011b327 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -215,104 +215,7 @@ pub(crate) fn parse_and_write_parsed_term_to_heap( impl Machine { pub fn with_test_streams() -> Self { - use ref_thread_local::RefThreadLocal; - - let mut machine_st = MachineState::new(); - - let user_input = Stream::Null(StreamOptions::default()); - let user_output = Stream::from_owned_string("".to_owned(), &mut machine_st.arena); - let user_error = Stream::stderr(&mut machine_st.arena); - - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - let mut wam = Machine { - machine_st, - indices: IndexStore::new(), - code: Code::new(), - user_input, - user_output, - user_error, - load_contexts: vec![], - runtime - }; - - let mut lib_path = current_dir(); - - lib_path.pop(); - lib_path.push("lib"); - - wam.add_impls_to_indices(); - - bootstrapping_compile( - Stream::from_static_string( - LIBRARIES.borrow()["ops_and_meta_predicates"], - &mut wam.machine_st.arena, - ), - &mut wam, - ListingSource::from_file_and_path( - atom!("ops_and_meta_predicates.pl"), - lib_path.clone(), - ), - ) - .unwrap(); - - bootstrapping_compile( - Stream::from_static_string( - LIBRARIES.borrow()["builtins"], - &mut wam.machine_st.arena, - ), - &mut wam, - ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()), - ) - .unwrap(); - - if let Some(ref mut builtins) = wam.indices.modules.get_mut(&atom!("builtins")) { - load_module( - &mut wam.machine_st, - &mut wam.indices.code_dir, - &mut wam.indices.op_dir, - &mut wam.indices.meta_predicates, - &CompilationTarget::User, - builtins, - ); - - import_builtin_impls(&wam.indices.code_dir, builtins); - } else { - unreachable!() - } - - lib_path.pop(); // remove the "lib" at the end - - bootstrapping_compile( - Stream::from_static_string(include_str!("../loader.pl"), &mut wam.machine_st.arena), - &mut wam, - ListingSource::from_file_and_path(atom!("loader.pl"), lib_path.clone()), - ) - .unwrap(); - - wam.configure_modules(); - - if let Some(loader) = wam.indices.modules.get(&atom!("loader")) { - load_module( - &mut wam.machine_st, - &mut wam.indices.code_dir, - &mut wam.indices.op_dir, - &mut wam.indices.meta_predicates, - &CompilationTarget::User, - loader, - ); - } else { - unreachable!() - } - - wam.load_special_forms(); - wam.load_top_level(); - wam.configure_streams(); - - wam + Machine::new(MachineConfig::in_memory()) } pub fn test_load_file(&mut self, file: &str) -> Vec { diff --git a/src/machine/mod.rs b/src/machine/mod.rs index c45d7cb49..6a3195973 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -5,10 +5,12 @@ pub mod code_walker; #[macro_use] pub mod loader; pub mod compile; +pub mod config; pub mod copier; pub mod dispatch; pub mod gc; pub mod heap; +pub mod lib_machine; pub mod load_state; pub mod machine_errors; pub mod machine_indices; @@ -53,6 +55,7 @@ use std::path::PathBuf; use std::sync::atomic::AtomicBool; use tokio::runtime::Runtime; +use self::config::MachineConfig; use self::parsed_results::*; lazy_static! { @@ -221,23 +224,18 @@ impl Machine { pub fn load_file(&mut self, path: &str, stream: Stream) { self.machine_st.registers[1] = stream_as_cell!(stream); - self.machine_st.registers[2] = atom_as_cell!( - self.machine_st.atom_tbl.build_with(path) - ); + self.machine_st.registers[2] = atom_as_cell!(self.machine_st.atom_tbl.build_with(path)); self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2)); } - fn load_top_level(&mut self) { + fn load_top_level(&mut self, program: &'static str) { let mut path_buf = current_dir(); path_buf.push("src/toplevel.pl"); let path = path_buf.to_str().unwrap(); - let toplevel_stream = Stream::from_static_string( - include_str!("../toplevel.pl"), - &mut self.machine_st.arena, - ); + let toplevel_stream = Stream::from_static_string(program, &mut self.machine_st.arena); self.load_file(path, toplevel_stream); @@ -292,7 +290,7 @@ impl Machine { } } - pub fn run_top_level(&mut self) { + pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) { let mut arg_pstrs = vec![]; for arg in env::args() { @@ -303,15 +301,12 @@ impl Machine { )); } - self.machine_st.registers[1] = heap_loc_as_cell!( - iter_to_heap_list(&mut self.machine_st.heap, arg_pstrs.into_iter()) - ); + self.machine_st.registers[1] = heap_loc_as_cell!(iter_to_heap_list( + &mut self.machine_st.heap, + arg_pstrs.into_iter() + )); - self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1)); - } - - pub fn run_input_once(&mut self) { - self.run_module_predicate(atom!("$toplevel"), (atom!("run_input_once"), 0)); + self.run_module_predicate(module_name, key); } pub fn set_user_input(&mut self, input: String) { @@ -328,24 +323,6 @@ impl Machine { self.load_file(module_name, stream); } - pub fn run_query(&mut self, query: String) -> QueryResult{ - self.set_user_input(query); - self.run_input_once(); - self.parse_output() - } - - pub fn parse_output(&self) -> QueryResult { - let output = self.get_user_output(); - output.split(";") - .map(|s| s.trim()) - .map(|s| s.replace(".", "")) - .filter(|s| !s.is_empty()) - .map(QueryResultLine::try_from) - .filter_map(Result::ok) - .collect::>() - .into() - } - 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 { @@ -461,18 +438,26 @@ impl Machine { } } - pub fn new() -> Self { + pub fn new(config: MachineConfig) -> Self { use ref_thread_local::RefThreadLocal; let args = MachineArgs::new(); let mut machine_st = MachineState::new(); - let user_input = Stream::stdin(&mut machine_st.arena, args.add_history); - let user_output = Stream::stdout(&mut machine_st.arena); - let user_error = Stream::stderr(&mut machine_st.arena); + let (user_input, user_output, user_error) = match config.streams { + config::StreamConfig::Stdio => ( + Stream::stdin(&mut machine_st.arena, args.add_history), + Stream::stdout(&mut machine_st.arena), + Stream::stderr(&mut machine_st.arena), + ), + config::StreamConfig::Memory => ( + Stream::Null(StreamOptions::default()), + Stream::from_owned_string("".to_owned(), &mut machine_st.arena), + Stream::stderr(&mut machine_st.arena), + ), + }; - let runtime = tokio::runtime::Runtime::new() - .unwrap(); + let runtime = tokio::runtime::Runtime::new().unwrap(); let mut wam = Machine { machine_st, @@ -555,7 +540,7 @@ impl Machine { } wam.load_special_forms(); - wam.load_top_level(); + wam.load_top_level(config.toplevel); wam.configure_streams(); wam @@ -936,40 +921,3 @@ impl Machine { } } } - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn programatic_query() { - let mut machine = Machine::with_test_streams(); - - machine.load_module_string("facts", String::from(r#" - triple("a", "p1", "b"). - triple("a", "p2", "b"). - "#)); - - let query = String::from(r#"triple("a",P,"b")."#); - let output = machine.run_query(query); - assert_eq!(output, QueryResult::Matches(vec![ - QueryMatch::from(btreemap!{ - "P" => Value::from("p1"), - }), - QueryMatch::from(btreemap!{ - "P" => Value::from("p2"), - }), - ])); - - assert_eq!( - machine.run_query(String::from(r#"triple("a","p1","b")."#)), - QueryResult::True - ); - - assert_eq!( - machine.run_query(String::from(r#"triple("x","y","z")."#)), - QueryResult::False - ); - } -} diff --git a/src/toplevel.pl b/src/toplevel.pl index 93b1a60d3..5a36aa77d 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -429,28 +429,4 @@ % number, a GNU-style error message % is expected to be printed instead. ; print_exception(E) - ). - -run_input_once :- - bb_put('$report_all', true), - catch(read_and_match_all_results, E, print_exception(E)). - -read_and_match_all_results :- - '$read_query_term'(_, Term, _, _, VarList), - bb_put('$answer_count', 0), - submit_query_and_print_all_results(Term, VarList). - -submit_query_and_print_all_results(Term, VarList) :- - '$get_b_value'(B), - bb_put('$report_all', true), - bb_put('$report_n_more', 0), - call(user:Term), - write_eqs_and_read_input(B, VarList), - !. -submit_query_and_print_all_results(_, _) :- - ( bb_get('$answer_count', 0) -> - write(' ') - ; true - ), - write('false.'), - nl. + ). \ No newline at end of file From e7f1e32ee300d510b78a2cbc51e156f6072d8470 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:35:00 +0200 Subject: [PATCH 11/66] fmt machine/parsed_results.rs --- src/machine/parsed_results.rs | 61 +++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 984c74395..7938106a3 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -1,8 +1,7 @@ +use crate::atom_table::*; use ordered_float::OrderedFloat; use rug::*; use std::collections::BTreeMap; -use crate::atom_table::*; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum QueryResult { @@ -13,7 +12,7 @@ pub enum QueryResult { #[derive(Debug, Clone, PartialEq, Eq)] pub struct QueryMatch { - pub bindings: BTreeMap + pub bindings: BTreeMap, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -38,18 +37,17 @@ pub enum Value { impl From> for QueryMatch { fn from(bindings: BTreeMap<&str, Value>) -> Self { QueryMatch { - bindings: bindings.into_iter() + bindings: bindings + .into_iter() .map(|(k, v)| (k.to_string(), v)) - .collect::>() + .collect::>(), } } } impl From> for QueryMatch { fn from(bindings: BTreeMap) -> Self { - QueryMatch { - bindings - } + QueryMatch { bindings } } } @@ -65,26 +63,34 @@ impl From> for QueryResult { } // If there is at least one line with true and no matches, return true. - if query_result_lines.iter().any(|l| l == &QueryResultLine::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 } - }) { + if let &QueryResultLine::Match(_) = l { + true + } else { + false + } + }) + { return QueryResult::True; } // If there is at least one match, return all matches. - let all_matches = query_result_lines.into_iter() + let all_matches = query_result_lines + .into_iter() .filter(|l| { - if let &QueryResultLine::Match(_) = l { true } else { false } - }) - .map(|l| { - match l { - QueryResultLine::Match(m) => { - QueryMatch::from(m) - }, - _ => unreachable!() + if let &QueryResultLine::Match(_) = l { + true + } else { + false } }) + .map(|l| match l { + QueryResultLine::Match(m) => QueryMatch::from(m), + _ => unreachable!(), + }) .collect::>(); if !all_matches.is_empty() { @@ -95,7 +101,6 @@ impl From> for QueryResult { } } - impl TryFrom for QueryResultLine { type Error = (); fn try_from(string: String) -> Result { @@ -103,10 +108,11 @@ impl TryFrom for QueryResultLine { "true" => Ok(QueryResultLine::True), "false" => Ok(QueryResultLine::False), _ => Ok(QueryResultLine::Match( - string.split(",") + string + .split(",") .map(|s| s.trim()) .filter(|s| !s.is_empty()) - .map(|s| -> Result<(String, Value), ()>{ + .map(|s| -> Result<(String, Value), ()> { let mut iter = s.split(" = "); let key = iter.next().unwrap().to_string(); @@ -115,8 +121,8 @@ impl TryFrom for QueryResultLine { Ok((key, Value::try_from(value)?)) }) .filter_map(Result::ok) - .collect::>() - )) + .collect::>(), + )), } } } @@ -128,8 +134,7 @@ impl TryFrom for Value { if trimmed.starts_with("'") && trimmed.ends_with("'") { Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) - } else - if trimmed.starts_with("\"") && trimmed.ends_with("\"") { + } 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(","); @@ -181,4 +186,4 @@ impl From<&str> for Value { fn from(str: &str) -> Self { Value::String(str.to_string()) } -} \ No newline at end of file +} From 39473908777c5167e8df1d29714a637c48cfa228 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:47:43 +0200 Subject: [PATCH 12/66] Use lib constructor in lib tests --- src/machine/lib_machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 7b7495610..708c524f9 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -32,7 +32,7 @@ mod tests { #[test] fn programatic_query() { - let mut machine = Machine::with_test_streams(); + let mut machine = Machine::new_lib(); machine.load_module_string( "facts", From bb95ed3ad04f1f98000c8fc41c9afe0478980221 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:48:05 +0200 Subject: [PATCH 13/66] Add back all needed predicates to lib_toplevel.pl --- src/lib_toplevel.pl | 158 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 7987a2ff8..3bdb2bb6a 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -23,8 +23,166 @@ arg_type(g(_)). arg_type(t(_)). +trailing_period_is_ambiguous(Value) :- + atom(Value), + atom_chars(Value, ValueChars), + list_last_item(ValueChars, Char), + ValueChars \== ['.'], + graphic_token_char(Char). +graphic_token_char(C) :- + memberchk(C, ['#', '$', '&', '*', '+', '-', '.', ('/'), ':', + '<', '=', '>', '?', '@', '^', '~', ('\\')]). +needs_bracketing(Value, Op) :- + catch((functor(Value, F, _), + current_op(EqPrec, EqSpec, Op), + current_op(FPrec, _, F)), + _, + false), + ( EqPrec < FPrec -> + true + ; FPrec > 0, F == Value, graphic_token_char(F) -> + true + ; F \== '.', '$quoted_token'(F) -> + true + ; EqPrec == FPrec, + memberchk(EqSpec, [fx,xfx,yfx]) + ). + +write_last_goal(G, VarList, MaxDepth) :- + ( G = (Var = Value) -> + ( var(Value) -> + select((Var = _), VarList, NewVarList) + ; VarList = NewVarList + ), + write(Var), + write(' = '), + ( needs_bracketing(Value, (=)) -> + write('('), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write(')') + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + ( trailing_period_is_ambiguous(Value) -> + write(' ') + ; true + ) + ) + ; G == [] -> + write('true') + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ). + +write_eq((G1, G2), VarList, MaxDepth) :- + !, + write_goal(G1, VarList, MaxDepth), + write(', '), + write_eq(G2, VarList, MaxDepth). +write_eq(G, VarList, MaxDepth) :- + write_last_goal(G, VarList, MaxDepth). + +term_variables_under_max_depth(Term, MaxDepth, Vars) :- + '$term_variables_under_max_depth'(Term, MaxDepth, Vars). + +write_eqs_and_read_input(B, VarList) :- + gather_query_vars(VarList, OrigVars), + % one layer of depth added for (=/2) functor + '$term_variables_under_max_depth'(OrigVars, 22, Vars0), + '$term_attributed_variables'(VarList, AttrVars), + '$project_atts':project_attributes(Vars0, AttrVars), + copy_term(AttrVars, AttrVars, AttrGoals), + term_variables(AttrGoals, AttrGoalVars), + append([Vars0, AttrGoalVars, AttrVars], Vars), + charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), + '$get_b_value'(B0), + gather_equations(NewVarList, OrigVars, Equations), + append(Equations, AttrGoals, Goals), + % one layer of depth added for (=/2) functor + maplist(\Term^Vs^term_variables_under_max_depth(Term, 22, Vs), Equations, EquationVars), + % maplist(term_variables_under_max_depth(22), Equations, EquationVars), + append([AttrGoalVars | EquationVars], Vars1), + term_variables(Vars1, Vars2), % deduplicate vars of Vars1 but preserve their order. + charsio:extend_var_list(Vars2, VarList, NewVarList0, fabricated), + bb_get('$answer_count', Count), + ( Count =:= 0 -> + write(' ') + ; true + ), + Count1 is Count + 1, + bb_put('$answer_count', Count1), + ( B0 == B -> + ( Goals == [] -> + write('true.'), nl + ; loader:thread_goals(Goals, ThreadedGoals, (',')), + write_eq(ThreadedGoals, NewVarList0, 20), + write('.'), + nl + ) + ; loader:thread_goals(Goals, ThreadedGoals, (',')), + write_eq(ThreadedGoals, NewVarList0, 20), + read_input(ThreadedGoals, NewVarList0) + ). + +read_input(ThreadedGoals, NewVarList) :- + ( bb_get('$report_all', true) -> + C = n + ; bb_get('$report_n_more', N), N > 1 -> + N1 is N - 1, + bb_put('$report_n_more', N1), + C = n + ; get_single_char(C) + ), + ( C = w -> + nl, + write(' '), + write_eq(ThreadedGoals, NewVarList, 0), + read_input(ThreadedGoals, NewVarList) + ; C = p -> + nl, + write(' '), + write_eq(ThreadedGoals, NewVarList, 20), + read_input(ThreadedGoals, NewVarList) + ; member(C, [';', ' ', n]) -> + nl, write('; '), false + ; C = h -> + help_message, + read_input(ThreadedGoals, NewVarList) + ; member(C, ['\n', .]) -> + nl, write('; ... .'), nl + ; C = a -> + bb_put('$report_all', true), + nl, write('; '), false + ; C = f -> + bb_get('$answer_count', Count), + More is 5 - Count mod 5, + bb_put('$report_n_more', More), + nl, write('; '), false + ; read_input(ThreadedGoals, NewVarList) + ). + +gather_query_vars([_ = Var | Vars], QueryVars) :- + ( var(Var) -> + QueryVars = [Var | QueryVars0], + gather_query_vars(Vars, QueryVars0) + ; + gather_query_vars(Vars, QueryVars) + ). +gather_query_vars([], []). + +gather_equations([], _, []). +gather_equations([Var = Value | Pairs], OrigVarList, Goals) :- + ( var(Value) -> + ( eq_member(Value, OrigVarList), + select_all(Pairs, Var, Value, [_ | VarEqs], NewPairs) -> + append([Var = Value | VarEqs], Goals0, Goals), + gather_equations(NewPairs, OrigVarList, Goals0) + ; + gather_equations(Pairs, OrigVarList, Goals) + ) + ; + Goals = [Var = Value | Goals0], + gather_equations(Pairs, OrigVarList, Goals0) + ). print_exception(E) :- ( E == error('$interrupt_thrown', repl) -> nl % print the From 644559b7f793429c487693489a7e398b91cc29a3 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:52:17 +0200 Subject: [PATCH 14/66] Add back newline at end of toplevel.pl --- src/toplevel.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 5a36aa77d..f80adc1e9 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -429,4 +429,5 @@ % number, a GNU-style error message % is expected to be printed instead. ; print_exception(E) - ). \ No newline at end of file + ). + \ No newline at end of file From 836f6c1d5b0944debdd0a1ce22da50e75c0046d8 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 20 Jul 2023 21:34:43 +0200 Subject: [PATCH 15/66] Don't panic when parsing results fails --- src/machine/parsed_results.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 7938106a3..329042a22 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -114,9 +114,8 @@ impl TryFrom for QueryResultLine { .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(); + let key = iter.next().ok_or(())?.to_string(); + let value = iter.next().ok_or(())?.to_string(); Ok((key, Value::try_from(value)?)) }) @@ -148,31 +147,29 @@ impl TryFrom for Value { 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)?); + let items: Vec<_> = value.split(":").collect(); + if items.len() == 2 { + let _key = items[0].to_string(); + let value = items[1].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)?); + let items: Vec<_> = value.split(":").collect(); + if items.len() == 2 { + let _key = items[0].to_string(); + let value = items[1].to_string(); + values.push(Value::try_from(value)?); + } } Ok(Value::Structure(atom!("<<>>"), values)) From cae32d6a0023df0633f5227c9f69e5d4717f8b4a Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 20 Jul 2023 22:23:51 +0200 Subject: [PATCH 16/66] Error handling --- src/machine/lib_machine.rs | 47 +++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 708c524f9..674a459e4 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -5,23 +5,27 @@ impl Machine { Machine::new(MachineConfig::in_memory().with_toplevel(include_str!("../lib_toplevel.pl"))) } - pub fn run_query(&mut self, query: String) -> QueryResult { + pub fn run_query(&mut self, query: String) -> Result { self.set_user_input(query); self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); self.parse_output() } - pub fn parse_output(&self) -> QueryResult { - let output = self.get_user_output(); - output - .split(";") - .map(|s| s.trim()) - .map(|s| s.replace(".", "")) - .filter(|s| !s.is_empty()) - .map(QueryResultLine::try_from) - .filter_map(Result::ok) - .collect::>() - .into() + pub fn parse_output(&self) -> Result { + let output = self.get_user_output().trim().to_string(); + if output.starts_with("error(") { + Err(output) + } else { + Ok(output + .split(";") + .map(|s| s.trim()) + .map(|s| s.replace(".", "")) + .filter(|s| !s.is_empty()) + .map(QueryResultLine::try_from) + .filter_map(Result::ok) + .collect::>() + .into()) + } } } @@ -48,24 +52,35 @@ mod tests { let output = machine.run_query(query); assert_eq!( output, - QueryResult::Matches(vec![ + Ok(QueryResult::Matches(vec![ QueryMatch::from(btreemap! { "P" => Value::from("p1"), }), QueryMatch::from(btreemap! { "P" => Value::from("p2"), }), - ]) + ])) ); assert_eq!( machine.run_query(String::from(r#"triple("a","p1","b")."#)), - QueryResult::True + Ok(QueryResult::True) ); assert_eq!( machine.run_query(String::from(r#"triple("x","y","z")."#)), - QueryResult::False + Ok(QueryResult::False) + ); + } + + #[test] + fn failing_query() { + let mut machine = Machine::new_lib(); + let query = String::from(r#"triple("a",P,"b")."#); + let output = machine.run_query(query); + assert_eq!( + output, + Err(String::from("error(existence_error(procedure,triple/3),triple/3).")) ); } } From 7c93450aa7f88475b2382407acfb20958c04fdf7 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 20 Jul 2023 22:31:20 +0200 Subject: [PATCH 17/66] type QueryResult = Result --- src/machine/lib_machine.rs | 16 ++++++++-------- src/machine/parsed_results.rs | 36 ++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 674a459e4..f88577cea 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,17 +1,17 @@ -use super::{Machine, MachineConfig, QueryResult, QueryResultLine, Atom}; +use super::{Machine, MachineConfig, QueryResult, QueryResolution, QueryResolutionLine, Atom}; impl Machine { pub fn new_lib() -> Self { Machine::new(MachineConfig::in_memory().with_toplevel(include_str!("../lib_toplevel.pl"))) } - pub fn run_query(&mut self, query: String) -> Result { + pub fn run_query(&mut self, query: String) -> QueryResult { self.set_user_input(query); self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); self.parse_output() } - pub fn parse_output(&self) -> Result { + pub fn parse_output(&self) -> QueryResult { let output = self.get_user_output().trim().to_string(); if output.starts_with("error(") { Err(output) @@ -21,9 +21,9 @@ impl Machine { .map(|s| s.trim()) .map(|s| s.replace(".", "")) .filter(|s| !s.is_empty()) - .map(QueryResultLine::try_from) + .map(QueryResolutionLine::try_from) .filter_map(Result::ok) - .collect::>() + .collect::>() .into()) } } @@ -52,7 +52,7 @@ mod tests { let output = machine.run_query(query); assert_eq!( output, - Ok(QueryResult::Matches(vec![ + Ok(QueryResolution::Matches(vec![ QueryMatch::from(btreemap! { "P" => Value::from("p1"), }), @@ -64,12 +64,12 @@ mod tests { assert_eq!( machine.run_query(String::from(r#"triple("a","p1","b")."#)), - Ok(QueryResult::True) + Ok(QueryResolution::True) ); assert_eq!( machine.run_query(String::from(r#"triple("x","y","z")."#)), - Ok(QueryResult::False) + Ok(QueryResolution::False) ); } diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 329042a22..f34eadeab 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -3,8 +3,10 @@ use ordered_float::OrderedFloat; use rug::*; use std::collections::BTreeMap; +pub type QueryResult = Result; + #[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResult { +pub enum QueryResolution { True, False, Matches(Vec), @@ -16,7 +18,7 @@ pub struct QueryMatch { } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum QueryResultLine { +pub enum QueryResolutionLine { True, False, Match(BTreeMap), @@ -51,13 +53,13 @@ impl From> for QueryMatch { } } -impl From> for QueryResult { - fn from(query_result_lines: Vec) -> Self { +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() { - QueryResultLine::True => return QueryResult::True, - QueryResultLine::False => return QueryResult::False, + QueryResolutionLine::True => return QueryResolution::True, + QueryResolutionLine::False => return QueryResolution::False, _ => {} } } @@ -65,49 +67,49 @@ impl From> for QueryResult { // If there is at least one line with true and no matches, return true. if query_result_lines .iter() - .any(|l| l == &QueryResultLine::True) + .any(|l| l == &QueryResolutionLine::True) && !query_result_lines.iter().any(|l| { - if let &QueryResultLine::Match(_) = l { + if let &QueryResolutionLine::Match(_) = l { true } else { false } }) { - return QueryResult::True; + return QueryResolution::True; } // If there is at least one match, return all matches. let all_matches = query_result_lines .into_iter() .filter(|l| { - if let &QueryResultLine::Match(_) = l { + if let &QueryResolutionLine::Match(_) = l { true } else { false } }) .map(|l| match l { - QueryResultLine::Match(m) => QueryMatch::from(m), + QueryResolutionLine::Match(m) => QueryMatch::from(m), _ => unreachable!(), }) .collect::>(); if !all_matches.is_empty() { - return QueryResult::Matches(all_matches); + return QueryResolution::Matches(all_matches); } - QueryResult::False + QueryResolution::False } } -impl TryFrom for QueryResultLine { +impl TryFrom for QueryResolutionLine { type Error = (); fn try_from(string: String) -> Result { match string.as_str() { - "true" => Ok(QueryResultLine::True), - "false" => Ok(QueryResultLine::False), - _ => Ok(QueryResultLine::Match( + "true" => Ok(QueryResolutionLine::True), + "false" => Ok(QueryResolutionLine::False), + _ => Ok(QueryResolutionLine::Match( string .split(",") .map(|s| s.trim()) From 0b833bd2f3cf0e87d4240b3d6a3953d8050b90f5 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 21 Jul 2023 00:23:35 +0200 Subject: [PATCH 18/66] Add missing write_goal/3 to lib_toplevel.pl --- src/lib_toplevel.pl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 3bdb2bb6a..70ece2732 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -50,6 +50,26 @@ memberchk(EqSpec, [fx,xfx,yfx]) ). +write_goal(G, VarList, MaxDepth) :- + ( G = (Var = Value) -> + ( var(Value) -> + select((Var = _), VarList, NewVarList) + ; VarList = NewVarList + ), + write(Var), + write(' = '), + ( needs_bracketing(Value, (=)) -> + write('('), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write(')') + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]) + ) + ; G == [] -> + write('true') + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ). + + write_last_goal(G, VarList, MaxDepth) :- ( G = (Var = Value) -> ( var(Value) -> From 9e85be11fecd00b3d0be8b828eb0dd5cfb57e60a Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 21 Jul 2023 14:35:44 +0200 Subject: [PATCH 19/66] Fix result parsing for complex string results --- Cargo.lock | 41 ++++++++++++++++++++++++++- Cargo.toml | 1 + src/machine/lib_machine.rs | 52 +++++++++++++++++++++++++++++++++++ src/machine/parsed_results.rs | 42 ++++++++++++++++++++-------- 4 files changed, 124 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29cf7f677..5ed25656c 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 a6f97b046..c64b75102 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 f88577cea..1dfdd2b28 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 f34eadeab..ba33089d1 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(()) } From d8a947546084843ca7700cdd5c885eb78868ac8a Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Sat, 22 Jul 2023 00:30:51 +0200 Subject: [PATCH 20/66] Add missing list_last_item to lib_toplevel.pl and increase MaxDepth of write_eq to avoid truncation of results --- src/lib_toplevel.pl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 70ece2732..a7c8df133 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -101,6 +101,10 @@ write_eq(G, VarList, MaxDepth) :- write_last_goal(G, VarList, MaxDepth). +list_last_item([C], C) :- !. +list_last_item([_|Cs], D) :- + list_last_item(Cs, D). + term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). @@ -134,12 +138,12 @@ ( Goals == [] -> write('true.'), nl ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 20), + write_eq(ThreadedGoals, NewVarList0, 200000), write('.'), nl ) ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 20), + write_eq(ThreadedGoals, NewVarList0, 200000), read_input(ThreadedGoals, NewVarList0) ). From 9fd6e18d595c2516eac60626974baea81c23cbfb Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Sat, 22 Jul 2023 00:32:24 +0200 Subject: [PATCH 21/66] Dedupe machine results --- src/machine/lib_machine.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 1dfdd2b28..050c883b1 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use super::{Machine, MachineConfig, QueryResult, QueryResolution, QueryResolutionLine, Atom}; impl Machine { @@ -16,6 +18,14 @@ impl Machine { if output.starts_with("error(") { Err(output) } else { + // Remove duplicate lines + let output = output + .lines() + .collect::>() + .iter() + .cloned() + .collect::>() + .join("\n"); Ok(output .split(";") .map(|s| s.trim()) From 2f99bb025c304b5a99301891d018c078d36c1229 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 27 Jul 2023 11:41:52 +0200 Subject: [PATCH 22/66] Add consult that works with streams / strings in library use-case --- src/loader.pl | 5 ++- src/machine/lib_machine.rs | 81 +++++++++++++++++++++++++++++++++++++- src/machine/mod.rs | 5 --- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/loader.pl b/src/loader.pl index fe3d6fe25..8365d7615 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -562,7 +562,10 @@ ) ). - +consult_stream(Stream, PathFileName) :- + '$push_load_state_payload'(Evacuable), + file_load(Stream, PathFileName, Subevacuable), + '$use_module'(Evacuable, Subevacuable, _). check_predicate_property(meta_predicate, Module, Name, Arity, MetaPredicateTerm) :- '$meta_predicate_property'(Module, Name, Arity, MetaPredicateTerm). diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 050c883b1..1eec92b82 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,12 +1,29 @@ use std::collections::HashSet; -use super::{Machine, MachineConfig, QueryResult, QueryResolution, QueryResolutionLine, Atom}; +use super::{ + Machine, MachineConfig, QueryResult, QueryResolutionLine, + Atom, AtomCell, HeapCellValue, HeapCellValueTag, + streams::Stream +}; impl Machine { pub fn new_lib() -> Self { Machine::new(MachineConfig::in_memory().with_toplevel(include_str!("../lib_toplevel.pl"))) } + pub fn load_module_string(&mut self, module_name: &str, program: String) { + let stream = Stream::from_owned_string(program, &mut self.machine_st.arena); + self.load_file(module_name, stream); + } + + pub fn consult_module_string(&mut self, module_name: &str, program: String) { + let stream = Stream::from_owned_string(program, &mut self.machine_st.arena); + self.machine_st.registers[1] = stream_as_cell!(stream); + self.machine_st.registers[2] = atom_as_cell!(self.machine_st.atom_tbl.build_with(module_name)); + + self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); + } + pub fn run_query(&mut self, query: String) -> QueryResult { self.set_user_input(query); self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); @@ -42,7 +59,7 @@ impl Machine { #[cfg(test)] mod tests { use super::*; - use crate::machine::{QueryMatch, Value}; + use crate::machine::{QueryMatch, Value, QueryResolution}; #[test] fn programatic_query() { @@ -145,4 +162,64 @@ mod tests { ])) ); } + + + #[test] + fn consult() { + 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#"triple("a",P,"b")."#); + let output = machine.run_query(query); + assert_eq!( + output, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "P" => Value::from("p1"), + }), + QueryMatch::from(btreemap! { + "P" => Value::from("p2"), + }), + ])) + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("a","p1","b")."#)), + Ok(QueryResolution::True) + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("x","y","z")."#)), + Ok(QueryResolution::False) + ); + + machine.consult_module_string( + "facts", + String::from( + r#" + triple("a", "new", "b"). + "#, + ), + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("a","p1","b")."#)), + Ok(QueryResolution::False) + ); + + assert_eq!( + machine.run_query(String::from(r#"triple("a","new","b")."#)), + Ok(QueryResolution::True) + ); + + } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 6a3195973..c77272c90 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -318,11 +318,6 @@ impl Machine { String::from_utf8(output_bytes).unwrap() } - pub fn load_module_string(&mut self, module_name: &str, program: String) { - let stream = Stream::from_owned_string(program, &mut self.machine_st.arena); - self.load_file(module_name, stream); - } - 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 { From 0d28404aad4acd5a416a5182fb6363771ccff3da Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 27 Jul 2023 11:50:38 +0200 Subject: [PATCH 23/66] Add special case when parsing --- src/machine/parsed_results.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index ba33089d1..ecb58c1cb 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -66,6 +66,18 @@ impl From> for QueryResolution { } } + // If there is only one line, and it is an empty match, return true. + if query_result_lines.len() == 1 { + match query_result_lines[0].clone() { + QueryResolutionLine::Match(m) => { + if m.is_empty() { + return QueryResolution::True; + } + } + _ => {} + } + } + // If there is at least one line with true and no matches, return true. if query_result_lines .iter() From 30dac8ea4186f915cd5c8eca06eb703bda31f027 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 27 Jul 2023 12:06:03 +0200 Subject: [PATCH 24/66] HashSet -> BTreeSet: Make parsing or results and thus tests deterministic. Add comments. --- src/machine/lib_machine.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 1eec92b82..b75f9b6d7 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::BTreeSet; use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, @@ -36,19 +36,24 @@ impl Machine { Err(output) } else { // Remove duplicate lines - let output = output - .lines() - .collect::>() + Ok(output + // 1. Split into disjunct matches + .split(";") + .map(|s| s.trim()) + // 2. Dedupe through Set + .collect::>() .iter() .cloned() + // 3. Back to Vec .collect::>() - .join("\n"); - Ok(output - .split(";") + .iter() + // 4. Trim and remove empty lines .map(|s| s.trim()) .map(|s| s.replace(".", "")) .filter(|s| !s.is_empty()) + // 5. Parse into QueryResolutionLine .map(QueryResolutionLine::try_from) + // 6. Remove lines that couldn't be parsed, so we still keep the ones they did .filter_map(Result::ok) .collect::>() .into()) @@ -154,10 +159,10 @@ mod tests { result, Ok(QueryResolution::Matches(vec![ QueryMatch::from(btreemap! { - "Class" => Value::from("Todo") + "Class" => Value::from("Recipe") }), QueryMatch::from(btreemap! { - "Class" => Value::from("Recipe") + "Class" => Value::from("Todo") }), ])) ); From c2658dc6da430f8f35446fe4d25f787fd2b6d9dd Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 27 Jul 2023 14:46:00 +0200 Subject: [PATCH 25/66] Try triska's toplevel and add some debugging println!s. --- src/lib_toplevel.pl | 27 +++++++++++++++++++++++++++ src/machine/lib_machine.rs | 7 +++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index a7c8df133..4eccbafe3 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -242,3 +242,30 @@ ), write('false.'), nl. + + + +toplevel :- + read_term(Goal, [variable_names(VNs)]), + Goal, + write('bindings(['), + write_bindings(VNs), + write(']).'), + nl, + false. + +write_bindings([]). +write_bindings([VN|VNs]) :- + write_bindings_(VNs, VN). + +write_bindings_([], VN) :- + write_binding(VN). +write_bindings_([VN|VNs], Prev) :- + write_binding(Prev), + write(','), + write_bindings_(VNs, VN). + +write_binding(Var=Val) :- + write(Var), + write(=), + write_term(Val, [quoted(true),double_quotes(true)]). \ No newline at end of file diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index b75f9b6d7..194434c33 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -25,13 +25,16 @@ impl Machine { } pub fn run_query(&mut self, query: String) -> QueryResult { - self.set_user_input(query); - self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); + let input = format!("{}", query); + println!("Running query: {}", input); + self.set_user_input(input); + self.run_top_level(atom!("$toplevel"), (atom!("toplevel"), 0)); self.parse_output() } pub fn parse_output(&self) -> QueryResult { let output = self.get_user_output().trim().to_string(); + println!("Output: {}", output); if output.starts_with("error(") { Err(output) } else { From 21c36880f18b1687887fc56a7fd21cfd15e078d2 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 2 Aug 2023 14:51:41 +0200 Subject: [PATCH 26/66] Fix build --- src/bin/scryer-prolog.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index a7519defb..f1f0b705d 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -1,6 +1,7 @@ fn main() { use std::sync::atomic::Ordering; use scryer_prolog::*; + use scryer_prolog::atom_table::Atom; ctrlc::set_handler(move || { scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed); From df048a4f42627d891abe4ada24b17b091dc9335a Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 2 Aug 2023 14:52:49 +0200 Subject: [PATCH 27/66] Switch back to run_input_once and use duplicated write_eqs/2 without any input handling --- src/lib_toplevel.pl | 44 ++++++++++++++++++++++++++++++++++++-- src/machine/lib_machine.rs | 2 +- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 4eccbafe3..091d15cda 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -231,9 +231,9 @@ submit_query_and_print_all_results(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', true), - bb_put('$report_n_more', 0), + bb_put('$report_n_more', 100), call(user:Term), - write_eqs_and_read_input(B, VarList), + write_eqs(B, VarList), !. submit_query_and_print_all_results(_, _) :- ( bb_get('$answer_count', 0) -> @@ -243,6 +243,46 @@ write('false.'), nl. +write_eqs(B, VarList) :- + gather_query_vars(VarList, OrigVars), + % one layer of depth added for (=/2) functor + '$term_variables_under_max_depth'(OrigVars, 22, Vars0), + '$term_attributed_variables'(VarList, AttrVars), + '$project_atts':project_attributes(Vars0, AttrVars), + copy_term(AttrVars, AttrVars, AttrGoals), + term_variables(AttrGoals, AttrGoalVars), + append([Vars0, AttrGoalVars, AttrVars], Vars), + charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), + '$get_b_value'(B0), + gather_equations(NewVarList, OrigVars, Equations), + append(Equations, AttrGoals, Goals), + % one layer of depth added for (=/2) functor + maplist(\Term^Vs^term_variables_under_max_depth(Term, 22, Vs), Equations, EquationVars), + % maplist(term_variables_under_max_depth(22), Equations, EquationVars), + append([AttrGoalVars | EquationVars], Vars1), + term_variables(Vars1, Vars2), % deduplicate vars of Vars1 but preserve their order. + charsio:extend_var_list(Vars2, VarList, NewVarList0, fabricated), + bb_get('$answer_count', Count), + ( Count =:= 0 -> + write(' ') + ; true + ), + Count1 is Count + 1, + bb_put('$answer_count', Count1), + ( B0 == B -> + ( Goals == [] -> + write('true.'), nl + ; loader:thread_goals(Goals, ThreadedGoals, (',')), + write_eq(ThreadedGoals, NewVarList0, 200000), + write('.'), + nl + ) + ; loader:thread_goals(Goals, ThreadedGoals, (',')), + write_eq(ThreadedGoals, NewVarList0, 200000), + write(';'), nl, false + ). + + toplevel :- diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 194434c33..bec03a768 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -28,7 +28,7 @@ impl Machine { let input = format!("{}", query); println!("Running query: {}", input); self.set_user_input(input); - self.run_top_level(atom!("$toplevel"), (atom!("toplevel"), 0)); + self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); self.parse_output() } From 3ff02da3147a0e1dfd7c1a42fdb55d0a780d3ed9 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 2 Aug 2023 16:25:53 +0200 Subject: [PATCH 28/66] Deactivate some debugging outputs --- src/machine/lib_machine.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index bec03a768..831ca2009 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -26,7 +26,7 @@ impl Machine { pub fn run_query(&mut self, query: String) -> QueryResult { let input = format!("{}", query); - println!("Running query: {}", input); + //println!("Running query: {}", input); self.set_user_input(input); self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); self.parse_output() @@ -34,7 +34,7 @@ impl Machine { pub fn parse_output(&self) -> QueryResult { let output = self.get_user_output().trim().to_string(); - println!("Output: {}", output); + //println!("Output: {}", output); if output.starts_with("error(") { Err(output) } else { From 65eb93793cccfd6c6d9240eb7b6a726b31a0330f Mon Sep 17 00:00:00 2001 From: Joshua Parkin Date: Thu, 3 Aug 2023 16:22:03 +0100 Subject: [PATCH 29/66] dont spawn a runtime in machine; inherit from outside with runtime::handle::Current --- src/bin/scryer-prolog.rs | 11 +++++++++-- src/machine/lib_machine.rs | 16 ++++++++-------- src/machine/mod.rs | 5 ----- src/machine/system_calls.rs | 11 +++++++---- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index f1f0b705d..4dcc05c26 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -7,6 +7,13 @@ fn main() { scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed); }).unwrap(); - let mut wam = machine::Machine::new(Default::default()); - wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)); + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + runtime.block_on(async move { + let mut wam = machine::Machine::new(Default::default()); + wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)); + }); } diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 831ca2009..074731e9b 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -69,8 +69,8 @@ mod tests { use super::*; use crate::machine::{QueryMatch, Value, QueryResolution}; - #[test] - fn programatic_query() { + #[tokio::test] + async fn programatic_query() { let mut machine = Machine::new_lib(); machine.load_module_string( @@ -108,8 +108,8 @@ mod tests { ); } - #[test] - fn failing_query() { + #[tokio::test] + async fn failing_query() { let mut machine = Machine::new_lib(); let query = String::from(r#"triple("a",P,"b")."#); let output = machine.run_query(query); @@ -119,8 +119,8 @@ mod tests { ); } - #[test] - fn complex_results() { + #[tokio::test] + async fn complex_results() { let mut machine = Machine::new_lib(); machine.load_module_string( "facts", @@ -172,8 +172,8 @@ mod tests { } - #[test] - fn consult() { + #[tokio::test] + async fn consult() { let mut machine = Machine::new_lib(); machine.consult_module_string( diff --git a/src/machine/mod.rs b/src/machine/mod.rs index c77272c90..03ac64925 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -53,7 +53,6 @@ use std::env; use std::io::Read; use std::path::PathBuf; use std::sync::atomic::AtomicBool; -use tokio::runtime::Runtime; use self::config::MachineConfig; use self::parsed_results::*; @@ -71,7 +70,6 @@ pub struct Machine { pub(super) user_output: Stream, pub(super) user_error: Stream, pub(super) load_contexts: Vec, - pub(super) runtime: Runtime, } #[derive(Debug)] @@ -452,8 +450,6 @@ impl Machine { ), }; - let runtime = tokio::runtime::Runtime::new().unwrap(); - let mut wam = Machine { machine_st, indices: IndexStore::new(), @@ -462,7 +458,6 @@ impl Machine { user_output, user_error, load_contexts: vec![], - runtime, }; let mut lib_path = current_dir(); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index b154e266a..1160c1cdb 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3864,7 +3864,8 @@ impl Machine { let address_string = address_sink.as_str(); //to_string(); let address: Uri = address_string.parse().unwrap(); - let stream = self.runtime.block_on(async { + let runtime = tokio::runtime::Handle::current(); + let stream = runtime.block_on(async { let https = HttpsConnector::new(); let client = Client::builder() .build::<_, hyper::Body>(https); @@ -3945,7 +3946,8 @@ impl Machine { let (tx, rx) = channel(1); let tx = Arc::new(Mutex::new(tx)); - let _guard = self.runtime.enter(); + let runtime = tokio::runtime::Handle::current(); + let _guard = runtime.enter(); let server = match Server::try_bind(&addr) { Ok(server) => server, Err(_) => { @@ -3953,7 +3955,7 @@ impl Machine { } }; - self.runtime.spawn(async move { + runtime.spawn(async move { let make_svc = make_service_fn(move |_conn| { let tx = tx.clone(); async move { Ok::<_, Infallible>(service_fn(move |req| http::serve_req(req, tx.clone()))) } @@ -4016,7 +4018,8 @@ impl Machine { let query_cell = string_as_cstr_cell!(query_atom); let hyper_req = request.request; - let buf = self.runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()}); + let runtime = tokio::runtime::Handle::current(); + let buf = runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()}); let reader = buf.reader(); let mut stream = Stream::from_http_stream( From ac61055d437ab1df79146b1aa98559d38dc006a0 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 3 Aug 2023 17:41:20 +0200 Subject: [PATCH 30/66] Remove some unused code from lib_toplevel.pl --- src/lib_toplevel.pl | 78 +-------------------------------------------- 1 file changed, 1 insertion(+), 77 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 091d15cda..eedaa828d 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -4,7 +4,7 @@ :- use_module(library(atts), [call_residue_vars/2]). :- use_module(library(charsio)). :- use_module(library(error)). -:- use_module(library(files)). + :- use_module(library(iso_ext)). :- use_module(library(lambda)). :- use_module(library(lists)). @@ -108,82 +108,6 @@ term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). -write_eqs_and_read_input(B, VarList) :- - gather_query_vars(VarList, OrigVars), - % one layer of depth added for (=/2) functor - '$term_variables_under_max_depth'(OrigVars, 22, Vars0), - '$term_attributed_variables'(VarList, AttrVars), - '$project_atts':project_attributes(Vars0, AttrVars), - copy_term(AttrVars, AttrVars, AttrGoals), - term_variables(AttrGoals, AttrGoalVars), - append([Vars0, AttrGoalVars, AttrVars], Vars), - charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), - '$get_b_value'(B0), - gather_equations(NewVarList, OrigVars, Equations), - append(Equations, AttrGoals, Goals), - % one layer of depth added for (=/2) functor - maplist(\Term^Vs^term_variables_under_max_depth(Term, 22, Vs), Equations, EquationVars), - % maplist(term_variables_under_max_depth(22), Equations, EquationVars), - append([AttrGoalVars | EquationVars], Vars1), - term_variables(Vars1, Vars2), % deduplicate vars of Vars1 but preserve their order. - charsio:extend_var_list(Vars2, VarList, NewVarList0, fabricated), - bb_get('$answer_count', Count), - ( Count =:= 0 -> - write(' ') - ; true - ), - Count1 is Count + 1, - bb_put('$answer_count', Count1), - ( B0 == B -> - ( Goals == [] -> - write('true.'), nl - ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 200000), - write('.'), - nl - ) - ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 200000), - read_input(ThreadedGoals, NewVarList0) - ). - -read_input(ThreadedGoals, NewVarList) :- - ( bb_get('$report_all', true) -> - C = n - ; bb_get('$report_n_more', N), N > 1 -> - N1 is N - 1, - bb_put('$report_n_more', N1), - C = n - ; get_single_char(C) - ), - ( C = w -> - nl, - write(' '), - write_eq(ThreadedGoals, NewVarList, 0), - read_input(ThreadedGoals, NewVarList) - ; C = p -> - nl, - write(' '), - write_eq(ThreadedGoals, NewVarList, 20), - read_input(ThreadedGoals, NewVarList) - ; member(C, [';', ' ', n]) -> - nl, write('; '), false - ; C = h -> - help_message, - read_input(ThreadedGoals, NewVarList) - ; member(C, ['\n', .]) -> - nl, write('; ... .'), nl - ; C = a -> - bb_put('$report_all', true), - nl, write('; '), false - ; C = f -> - bb_get('$answer_count', Count), - More is 5 - Count mod 5, - bb_put('$report_n_more', More), - nl, write('; '), false - ; read_input(ThreadedGoals, NewVarList) - ). - gather_query_vars([_ = Var | Vars], QueryVars) :- ( var(Var) -> QueryVars = [Var | QueryVars0], From 3cf3c0ea99dcdf795f48800efcd4bc7819f8528e Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 3 Aug 2023 19:51:12 +0200 Subject: [PATCH 31/66] Integration stress test showing Machine blocking on query --- src/machine/lib_integration_test_commands.txt | 1018 +++++++++++++++++ src/machine/lib_machine.rs | 48 + 2 files changed, 1066 insertions(+) create mode 100644 src/machine/lib_integration_test_commands.txt diff --git a/src/machine/lib_integration_test_commands.txt b/src/machine/lib_integration_test_commands.txt new file mode 100644 index 000000000..faf40938c --- /dev/null +++ b/src/machine/lib_integration_test_commands.txt @@ -0,0 +1,1018 @@ +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +:- discontiguous(languageName/2). +:- discontiguous(expressionAddress/2). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +=====query +subject_class(X, _). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +:- discontiguous(languageName/2). +:- discontiguous(expressionAddress/2). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class(X, _). +=====query +subject_class("Todo", C), constructor(C, Actions). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:construct%20test", "todo://state", "todo://ready"). +link("literal://string:construct%20test", "todo://state", "todo://ready", 1691084130581, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:construct%20test", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:construct%20test", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), property(C, Property). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "state"). +=====query +subject_class("Todo", C), property_resolve(C, "title"). +=====query +subject_class("Todo", C), property_setter(C, Property, Setter). +=====query +subject_class("Todo", C), property_resolve_language(C, "state", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), collection(C, Collection). +=====query +subject_class("Todo", C), collection_adder(C, Collection, Adder). +=====query +subject_class("Todo", C), collection_remover(C, Collection, Remover). +=====query +subject_class("Todo", C), collection_setter(C, Collection, Setter). +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), constructor(C, Actions). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:construct%20test", "todo://state", "todo://ready"). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +link("literal://string:construct%20test", "todo://state", "todo://ready", 1691084130581, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://string:get%20proxy%20test", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://string:get%20proxy%20test", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), property(C, Property). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "state"). +=====query +subject_class("Todo", C), property_resolve(C, "title"). +=====query +subject_class("Todo", C), property_setter(C, Property, Setter). +=====query +subject_class("Todo", C), property_resolve_language(C, "state", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), collection(C, Collection). +=====query +subject_class("Todo", C), collection_adder(C, Collection, Adder). +=====query +subject_class("Todo", C), collection_remover(C, Collection, Remover). +=====query +subject_class("Todo", C), collection_setter(C, Collection, Setter). +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), property(C, Property). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "state"). +=====query +subject_class("Todo", C), property_resolve(C, "title"). +=====query +subject_class("Todo", C), property_setter(C, Property, Setter). +=====query +subject_class("Todo", C), property_resolve_language(C, "state", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), collection(C, Collection). +=====query +subject_class("Todo", C), collection_adder(C, Collection, Adder). +=====query +subject_class("Todo", C), collection_remover(C, Collection, Remover). +=====query +subject_class("Todo", C), collection_setter(C, Collection, Setter). +=====query +subject_class("Todo", C), property_getter(C, "literal://string:get%20proxy%20test", "state", Value). +=====query +subject_class("Todo", C), property_getter(C, "literal://string:get%20proxy%20test", "title", Value). +=====query +subject_class("Todo", C), constructor(C, Actions). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:construct%20test", "todo://state", "todo://ready"). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://ready"). +link("literal://string:construct%20test", "todo://state", "todo://ready", 1691084130581, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://ready", 1691084131145, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://string:get%20proxy%20test", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://string:get%20proxy%20test", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), property(C, Property). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "state"). +=====query +subject_class("Todo", C), property_resolve(C, "title"). +=====query +subject_class("Todo", C), property_setter(C, Property, Setter). +=====query +subject_class("Todo", C), property_resolve_language(C, "state", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), collection(C, Collection). +=====query +subject_class("Todo", C), collection_adder(C, Collection, Adder). +=====query +subject_class("Todo", C), collection_remover(C, Collection, Remover). +=====query +subject_class("Todo", C), collection_setter(C, Collection, Setter). +=====query +subject_class("Todo", C), property_getter(C, "literal://string:construct%20test", "state", Value). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://done"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://done", 1691084131359, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:get%20proxy%20test", "literal"). +languageAddress("literal://string:construct%20test", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:get%20proxy%20test", "literal"). +languageName("literal://string:construct%20test", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), property_getter(C, "literal://string:construct%20test", "state", Value). +=====query +subject_class("Todo", C), property_getter(C, "literal://string:construct%20test", "title", Value). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://done"). +triple("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://done", 1691084131359, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", 1691084131416, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:get%20proxy%20test", "literal"). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:get%20proxy%20test", "literal"). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), property_getter(C, "literal://string:construct%20test", "title", Value). +=====query +subject_class("Todo", C), collection_getter(C, "literal://string:construct%20test", "comments", Value). +=====query +subject_class("Todo", C), collection_getter(C, "literal://string:construct%20test", "comments", Value). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://done"). +triple("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +triple("literal://string:construct%20test", "todo://comment", "literal://string:comment%201"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://done", 1691084131359, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", 1691084131416, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://comment", "literal://string:comment%201", 1691084131473, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:get%20proxy%20test", "literal"). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +languageAddress("literal://string:comment%201", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:get%20proxy%20test", "literal"). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +languageName("literal://string:comment%201", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +expressionAddress("literal://string:comment%201", "string:comment%201"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), collection_getter(C, "literal://string:construct%20test", "comments", Value). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://done"). +triple("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://done", 1691084131359, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", 1691084131416, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:get%20proxy%20test", "literal"). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:get%20proxy%20test", "literal"). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), collection_getter(C, "literal://string:construct%20test", "comments", Value). +=====consult +:- discontiguous(triple/3). +:- discontiguous(link/5). +triple("literal://string:get%20proxy%20test", "todo://state", "todo://ready"). +triple("literal://string:construct%20test", "todo://state", "todo://done"). +triple("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +triple("literal://string:construct%20test", "todo://comment", "literal://string:new%20comment%201"). +link("literal://string:get%20proxy%20test", "todo://state", "todo://ready", 1691084130761, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://state", "todo://done", 1691084131359, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://has_title", "literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", 1691084131416, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +link("literal://string:construct%20test", "todo://comment", "literal://string:new%20comment%201", 1691084131557, "did:key:z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa"). +:- discontiguous(reachable/2). +reachable(A,B) :- triple(A,_,B). +reachable(A,B) :- triple(A,_,X), reachable(X,B). +:- discontiguous(hiddenExpression/1). +:- discontiguous(languageAddress/2). +languageAddress("literal://string:get%20proxy%20test", "literal"). +languageAddress("literal://string:construct%20test", "literal"). +languageAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +languageAddress("literal://string:new%20comment%201", "literal"). +:- discontiguous(languageName/2). +languageName("literal://string:get%20proxy%20test", "literal"). +languageName("literal://string:construct%20test", "literal"). +languageName("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "literal"). +languageName("literal://string:new%20comment%201", "literal"). +:- discontiguous(expressionAddress/2). +expressionAddress("literal://string:get%20proxy%20test", "string:get%20proxy%20test"). +expressionAddress("literal://string:construct%20test", "string:construct%20test"). +expressionAddress("literal://json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D", "json:%7B%22author%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22timestamp%22%3A%222023-08-03T17%3A35%3A31.405Z%22%2C%22data%22%3A%22test%20title%22%2C%22proof%22%3A%7B%22key%22%3A%22did%3Akey%3Az6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%23z6MkfMZgoaZc5CRUSMxrugjiJLufgkAmRZ72gctjRY4LfxRa%22%2C%22signature%22%3A%2253a6d498d7c19fed24000efe3339526b564d779f0906b71304c96b1016a09f0288b1f2c5718505f4e23933c95b7a7e68466330a824e01a80c6275d922030860c%22%2C%22valid%22%3Atrue%2C%22invalid%22%3Afalse%7D%7D"). +expressionAddress("literal://string:new%20comment%201", "string:new%20comment%201"). +:- discontiguous(register_sdna_flow/2). +:- discontiguous(flowable/2). +:- discontiguous(flow_state/3). +:- discontiguous(start_action/2). +:- discontiguous(action/4). +:- discontiguous(subject_class/2). +:- discontiguous(constructor/2). +:- discontiguous(destructor/2). +:- discontiguous(instance/2). +:- discontiguous(property/2). +:- discontiguous(property_getter/4). +:- discontiguous(property_setter/3). +:- discontiguous(property_resolve/2). +:- discontiguous(property_resolve_language/3). +:- discontiguous(property_named_option/4). +:- discontiguous(collection/2). +:- discontiguous(collection_getter/4). +:- discontiguous(collection_setter/3). +:- discontiguous(collection_remover/3). +:- discontiguous(collection_adder/3). +:- discontiguous(p3_class_icon/2). +:- discontiguous(p3_class_color/2). +:- discontiguous(p3_instance_color/3). +subject_class("Todo", c). +constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). +instance(c, Base) :- triple(Base, "todo://state", _). + +destructor(c, '[{action: "removeLink", source: "this", predicate: "todo://state", target: "todo://ready"}]'). + +property(c, "state"). +property_getter(c, Base, "state", Value) :- triple(Base, "todo://state", Value). +property_setter(c, "state", '[{action: "setSingleTarget", source: "this", predicate: "todo://state", target: "value"}]'). + +property(c, "title"). +property_resolve(c, "title"). +property_resolve_language(c, "title", "literal"). +property_getter(c, Base, "title", Value) :- triple(Base, "todo://has_title", Value). +property_setter(c, "title", '[{action: "setSingleTarget", source: "this", predicate: "todo://has_title", target: "value"}]'). + +property(c, "isLiked"). +property_getter(c, Base, "isLiked", Value) :- triple(Base, "flux://has_reaction", "flux://thumbsup"), Value = true. + +collection(c, "comments"). +collection_getter(c, Base, "comments", List) :- findall(C, triple(Base, "todo://comment", C), List). +collection_adder(c, "commentss", '[{action: "addLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_remover(c, "commentss", '[{action: "removeLink", source: "this", predicate: "todo://comment", target: "value"}]'). +collection_setter(c, "commentss", '[{action: "collectionSetter", source: "this", predicate: "todo://comment", target: "value"}]'). + +collection(c, "entries"). +collection_getter(c, Base, "entries", List) :- findall(C, triple(Base, "flux://entry_type", C), List). +collection_adder(c, "entriess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "entriess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "entriess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "messages"). +collection_getter(c, Base, "messages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), instance(OtherClass, Target), subject_class("Message", OtherClass)), List). +collection_adder(c, "messagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "messagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "messagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +collection(c, "likedMessages"). +collection_getter(c, Base, "likedMessages", List) :- setof(Target, (triple(Base, "flux://entry_type", Target), triple(Target, "flux://has_reaction", "flux://thumbsup")), List). +collection_adder(c, "likedMessagess", '[{action: "addLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_remover(c, "likedMessagess", '[{action: "removeLink", source: "this", predicate: "flux://entry_type", target: "value"}]'). +collection_setter(c, "likedMessagess", '[{action: "collectionSetter", source: "this", predicate: "flux://entry_type", target: "value"}]'). + +=====query +subject_class("Todo", C), collection_getter(C, "literal://string:construct%20test", "comments", Value). +=====query +subject_class("Todo", C), instance(C, X). +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:construct%20test"). +=====query +subject_class("Todo", C), instance(C, "literal://string:get%20proxy%20test"). +=====query +subject_class("Todo", C), property(C, Property). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "isLiked"). +=====query +subject_class("Todo", C), property_resolve(C, "state"). +=====query +subject_class("Todo", C), property_resolve(C, "title"). +=====query +subject_class("Todo", C), property_setter(C, Property, Setter). +=====query +subject_class("Todo", C), property_resolve_language(C, "state", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), property_resolve_language(C, "title", Language). +=====query +subject_class("Todo", C), collection(C, Collection). +=====query +subject_class("Todo", C), collection_adder(C, Collection, Adder). +=====query +subject_class("Todo", C), collection_remover(C, Collection, Remover). +=====query +subject_class("Todo", C), collection_setter(C, Collection, Setter). +=====query +subject_class("Todo", C), property_getter(C, "literal://string:get%20proxy%20test", "state", Value). +=====query +subject_class(Class, C), property(C, "state"), property(C, "title"), collection(C, "comments"), property_setter(C, "state", _), property_setter(C, "title", _), collection_adder(C, "commentss", _), collection_setter(C, "commentss", _). diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 074731e9b..8641d13de 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -230,4 +230,52 @@ mod tests { ); } + + #[tokio::test] + async fn stress_integration_test() { + let mut machine = Machine::new_lib(); + + // File with test commands, i.e. program code to consult and queries to run + let code = include_str!("./lib_integration_test_commands.txt"); + + // Split the code into blocks + let blocks = code.split("====="); + + let mut i = 0; + // Iterate over the blocks + for block in blocks { + // Trim the block to remove any leading or trailing whitespace + let block = block.trim(); + + // Skip empty blocks + if block.is_empty() { + continue; + } + + // Check if the block is a query + if block.starts_with("query") { + // Extract the query from the block + let query = &block[5..]; + + i += 1; + println!("query #{}: {}", i, query); + // Parse and execute the query + let result = machine.run_query(query.to_string()); + + assert!(result.is_ok()); + + // Print the result + println!("{:?}", result); + } else if block.starts_with("consult") { + // Extract the code from the block + let code = &block[7..]; + + println!("load code: {}", code); + + // Load the code into the machine + machine.consult_module_string("facts", code.to_string()); + } + } + + } } From 579816a04fc0d06f441d731b2491de2ebaa381e1 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 7 Aug 2023 18:29:05 +0200 Subject: [PATCH 32/66] Use new `double_quotes` write-option --- src/lib_toplevel.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index eedaa828d..1fb287c0b 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -60,9 +60,9 @@ write(' = '), ( needs_bracketing(Value, (=)) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]) + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]) ) ; G == [] -> write('true') @@ -80,9 +80,9 @@ write(' = '), ( needs_bracketing(Value, (=)) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), ( trailing_period_is_ambiguous(Value) -> write(' ') ; true From 4e8f7f0a1ba2935982749f900be176c5b3338fca Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 7 Aug 2023 18:38:34 +0200 Subject: [PATCH 33/66] Remove unused toplevel predicate --- src/lib_toplevel.pl | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl index 1fb287c0b..4b13f9a4b 100644 --- a/src/lib_toplevel.pl +++ b/src/lib_toplevel.pl @@ -205,31 +205,3 @@ write_eq(ThreadedGoals, NewVarList0, 200000), write(';'), nl, false ). - - - - -toplevel :- - read_term(Goal, [variable_names(VNs)]), - Goal, - write('bindings(['), - write_bindings(VNs), - write(']).'), - nl, - false. - -write_bindings([]). -write_bindings([VN|VNs]) :- - write_bindings_(VNs, VN). - -write_bindings_([], VN) :- - write_binding(VN). -write_bindings_([VN|VNs], Prev) :- - write_binding(Prev), - write(','), - write_bindings_(VNs, VN). - -write_binding(Var=Val) :- - write(Var), - write(=), - write_term(Val, [quoted(true),double_quotes(true)]). \ No newline at end of file From cf63b588bcef071302dbe0c491def652f509acd5 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 7 Aug 2023 18:41:08 +0200 Subject: [PATCH 34/66] Ignore stress test because it fails on windows --- src/machine/lib_machine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 8641d13de..a90e7adc8 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -231,6 +231,7 @@ mod tests { } + #[ignore = "fails on windows"] #[tokio::test] async fn stress_integration_test() { let mut machine = Machine::new_lib(); From 72ceceb7aeef86f4aaea14f02729a5b9e7716a9b Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Tue, 8 Aug 2023 16:20:49 +0530 Subject: [PATCH 35/66] Updated hyper to 1.0.0-rc.4 --- Cargo.lock | 71 +++++++++++++++++++++++++++++++++---- Cargo.toml | 5 +-- src/http.rs | 44 +++++++++++------------ src/machine/system_calls.rs | 19 +++++----- 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0c62cfc1..4385dbcfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,13 +954,12 @@ dependencies = [ [[package]] name = "hyper" -version = "1.0.0-rc.3" +version = "1.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +checksum = "d280a71f348bcc670fc55b02b63c53a04ac0bf2daff2980795aeaf53edae10e6" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", @@ -987,6 +986,24 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.0.0" +source = "git+https://github.com/hyperium/hyper-util.git#f898015fc9eca9f459ddac521db278d904099e89" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper 1.0.0-rc.4", + "once_cell", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1127,7 +1144,6 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libffi" version = "3.2.0" -source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "libc", "libffi-sys", @@ -1136,7 +1152,6 @@ dependencies = [ [[package]] name = "libffi-sys" version = "2.3.0" -source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "cc", ] @@ -1626,6 +1641,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2011,7 +2046,8 @@ dependencies = [ "git-version", "hostname", "http-body-util", - "hyper 1.0.0-rc.3", + "hyper 1.0.0-rc.4", + "hyper-util", "indexmap", "lazy_static", "lexical", @@ -2497,6 +2533,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2510,6 +2568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-core", ] diff --git a/Cargo.toml b/Cargo.toml index 40b66623c..50dab7615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,8 @@ smallvec = "1.8.0" sodiumoxide = "0.2.6" static_assertions = "1.1.0" ryu = "1.0.9" -hyper = { version = "1.0.0-rc.3", features = ["full"] } +hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper-util = { git = "https://github.com/hyperium/hyper-util.git" } tokio = { version = "1.28.2", features = ["full"] } futures = "0.3" regex = "1.9.1" @@ -69,7 +70,7 @@ http-body-util = "0.1.0-rc.2" bytes = "1" reqwest = { version = "0.11.18", features = ["blocking"] } dashu = { git = "https://github.com/coasys/dashu.git" } -libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space" } +libffi = { path = "../libffi-rs/libffi-rs" } rand = "0.8.5" [dev-dependencies] diff --git a/src/http.rs b/src/http.rs index 71d1682cf..8f548235e 100644 --- a/src/http.rs +++ b/src/http.rs @@ -27,28 +27,28 @@ impl Service> for HttpService { type Error = hyper::Error; type Future = Pin> + Send>>; - fn call(&mut self, req: Request) -> Self::Future { - // new connection! - // we send the Request info to Prolog - let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); - let http_request = HttpRequest { request: req, response: Arc::clone(&response) }; - self.tx.send(http_request).unwrap(); + fn call(self: &HttpService, req: Request) -> Self::Future { + // new connection! + // we send the Request info to Prolog + let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); + let http_request = HttpRequest { request: req, response: Arc::clone(&response) }; + self.tx.send(http_request).unwrap(); - // we wait for the Response info from Prolog - { - let (ready, _response, cvar) = &*response; - let mut ready = ready.lock().unwrap(); - while !*ready { - ready = cvar.wait(ready).unwrap(); - } - } - { - let (_, response, _) = &*response; - let response = response.lock().unwrap().take(); - let res = response.expect("Data race error in HTTP Server"); - Box::pin(async move { - Ok(res) - }) - } + // we wait for the Response info from Prolog + { + let (ready, _response, cvar) = &*response; + let mut ready = ready.lock().unwrap(); + while !*ready { + ready = cvar.wait(ready).unwrap(); + } + } + { + let (_, response, _) = &*response; + let response = response.lock().unwrap().take(); + let res = response.expect("Data race error in HTTP Server"); + Box::pin(async move { + Ok(res) + }) + } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 88d1e2cad..f025306c4 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -88,6 +88,7 @@ use hyper::{HeaderMap, Method}; use http_body_util::BodyExt; use bytes::Buf; use reqwest::Url; +use hyper_util::rt::TokioIo; pub(crate) fn get_key() -> KeyEvent { let key; @@ -4349,14 +4350,16 @@ impl Machine { let (stream, _) = listener.accept().await.unwrap(); tokio::task::spawn(async move { - if let Err(err) = http1::Builder::new() - .serve_connection(stream, HttpService { - tx - }) - .await - { - eprintln!("Error serving connection: {:?}", err); - } + let io = TokioIo::new(stream); + + if let Err(err) = http1::Builder::new() + .serve_connection(io, HttpService { + tx + }) + .await + { + eprintln!("Error serving connection: {:?}", err); + } }); } }); From ce1c8aac4ccc0a49ce4816c0870634a703b1fc1f Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Tue, 8 Aug 2023 16:22:08 +0530 Subject: [PATCH 36/66] Fixed libffi dep --- Cargo.lock | 2 ++ Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4385dbcfc..a53f32cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1144,6 +1144,7 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libffi" version = "3.2.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "libc", "libffi-sys", @@ -1152,6 +1153,7 @@ dependencies = [ [[package]] name = "libffi-sys" version = "2.3.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index 50dab7615..1ef29ebe7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ http-body-util = "0.1.0-rc.2" bytes = "1" reqwest = { version = "0.11.18", features = ["blocking"] } dashu = { git = "https://github.com/coasys/dashu.git" } -libffi = { path = "../libffi-rs/libffi-rs" } +libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space" } rand = "0.8.5" [dev-dependencies] From 7e97f16f4126d1a30e813a110b45341d6bfa11d8 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 22 Aug 2023 22:46:00 +0200 Subject: [PATCH 37/66] Fix list result parsing --- src/machine/lib_machine.rs | 20 ++++++++++++++++++++ src/machine/parsed_results.rs | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index a90e7adc8..9c5245883 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -171,6 +171,26 @@ mod tests { ); } + #[tokio::test] + async fn list_results() { + let mut machine = Machine::new_lib(); + machine.load_module_string( + "facts", + r#" + list([1,2,3]). + "#.to_string()); + + let result = machine.run_query(String::from("list(X).")); + assert_eq!( + result, + Ok(QueryResolution::Matches(vec![ + QueryMatch::from(btreemap! { + "X" => Value::List(Vec::from([Value::from("1"), Value::from("2"), Value::from("3")])) + }), + ])) + ); + } + #[tokio::test] async fn consult() { diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index aaec386d9..ec9b92b8e 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -120,7 +120,7 @@ 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(); + let re = Regex::new(r"(\w+)\s=\s((?:[^,\[]|\[[^\]]*\])*)").unwrap(); for cap in re.captures_iter(input) { let key = cap[1].to_string(); From bfb3164a0d766035de382e653de01b1e56f1feed Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 23 Aug 2023 14:36:07 +0200 Subject: [PATCH 38/66] Enable multiple Machines per process by having ATOM_TABLE_BUF_BASE always be thread_local, not only in cfg(test) mode. --- src/atom_table.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/atom_table.rs b/src/atom_table.rs index 48f50ec94..11693484d 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -40,17 +40,11 @@ impl From for Atom { const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; -#[cfg(test)] thread_local! { static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut()); } -#[cfg(not(test))] -static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr = - std::sync::atomic::AtomicPtr::new(ptr::null_mut()); - fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> { -#[cfg(test)] { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { let mut borrow = atom_table_buf_base.borrow_mut(); @@ -62,30 +56,12 @@ fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), * } })?; }; - #[cfg(not(test))] - { - ATOM_TABLE_BUF_BASE - .compare_exchange( - old_ptr.cast_mut(), - new_ptr.cast_mut(), - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, - ) - .map_err(|ptr| ptr.cast_const()) - }?; Ok(()) } pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { - #[cfg(test)] - { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) } -#[cfg(not(test))] - { - ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed) - } -} #[test] #[should_panic(expected = "Overwriting atom table base pointer")] From c25888288a1e30e9852efbe5ca3e084c484f969e Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 24 Aug 2023 18:10:47 +0200 Subject: [PATCH 39/66] Fix parsing of floats --- src/machine/lib_machine.rs | 2 +- src/machine/parsed_results.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 9c5245883..a3438783e 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -52,7 +52,7 @@ impl Machine { .iter() // 4. Trim and remove empty lines .map(|s| s.trim()) - .map(|s| s.replace(".", "")) + .map(|s| if s.ends_with(".") { s[..s.len()-1].to_string() } else { s.to_string() }) .filter(|s| !s.is_empty()) // 5. Parse into QueryResolutionLine .map(QueryResolutionLine::try_from) diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index ec9b92b8e..01ba6d543 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -163,7 +163,13 @@ impl TryFrom for Value { fn try_from(string: String) -> Result { let trimmed = string.trim(); - if trimmed.starts_with("'") && trimmed.ends_with("'") { + if let Ok(float_value) = string.parse::() { + println!("float value: {} -> {}", string, float_value); + Ok(Value::Float(OrderedFloat(float_value))) + } else if let Ok(int_value) = string.parse::() { + println!("int value: {} -> {}", string, int_value); + Ok(Value::Integer(int_value.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("\"") { Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) From afbadd9ea2c6f6f94d38dbf2132f1ac09ea32c88 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 24 Aug 2023 18:13:55 +0200 Subject: [PATCH 40/66] Revert "Enable multiple Machines per process by having ATOM_TABLE_BUF_BASE always be thread_local, not only in cfg(test) mode." This reverts commit bfb3164a0d766035de382e653de01b1e56f1feed. --- src/atom_table.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/atom_table.rs b/src/atom_table.rs index 11693484d..48f50ec94 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -40,11 +40,17 @@ impl From for Atom { const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; +#[cfg(test)] thread_local! { static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut()); } +#[cfg(not(test))] +static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr = + std::sync::atomic::AtomicPtr::new(ptr::null_mut()); + fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> { +#[cfg(test)] { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { let mut borrow = atom_table_buf_base.borrow_mut(); @@ -56,12 +62,30 @@ fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), * } })?; }; + #[cfg(not(test))] + { + ATOM_TABLE_BUF_BASE + .compare_exchange( + old_ptr.cast_mut(), + new_ptr.cast_mut(), + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) + .map_err(|ptr| ptr.cast_const()) + }?; Ok(()) } pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { + #[cfg(test)] + { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) } +#[cfg(not(test))] + { + ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed) + } +} #[test] #[should_panic(expected = "Overwriting atom table base pointer")] From d13173942d35fb6261118a4e117b8f3dee964e3f Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 24 Aug 2023 19:12:09 +0200 Subject: [PATCH 41/66] Cargo feature "multi_thread" for thread-local ATOM_TABLE_BUF_BASE --- Cargo.toml | 1 + src/atom_table.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ef29ebe7..f594f3f4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ build = "build/main.rs" rust-version = "1.63" [features] +multi_thread = [] [build-dependencies] indexmap = "1.0.2" diff --git a/src/atom_table.rs b/src/atom_table.rs index 48f50ec94..bb7b56cfa 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -40,17 +40,17 @@ impl From for Atom { const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; -#[cfg(test)] +#[cfg(any(test, feature = "multi_thread"))] thread_local! { static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut()); } -#[cfg(not(test))] +#[cfg(all(not(test), not(feature = "multi_thread")))] static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr = std::sync::atomic::AtomicPtr::new(ptr::null_mut()); fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> { -#[cfg(test)] + #[cfg(any(test, feature = "multi_thread"))] { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { let mut borrow = atom_table_buf_base.borrow_mut(); @@ -62,7 +62,7 @@ fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), * } })?; }; - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "multi_thread")))] { ATOM_TABLE_BUF_BASE .compare_exchange( @@ -77,11 +77,11 @@ fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), * } pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { - #[cfg(test)] + #[cfg(any(test, feature = "multi_thread"))] { - ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) -} -#[cfg(not(test))] + ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) + } + #[cfg(not(any(test, feature = "multi_thread")))] { ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed) } From 77394ba914d0b02e52dd6197e399ea9aedd6c408 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 24 Aug 2023 19:15:41 +0200 Subject: [PATCH 42/66] Remove debug println!s --- src/machine/parsed_results.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index 01ba6d543..f0436da47 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -164,10 +164,8 @@ impl TryFrom for Value { let trimmed = string.trim(); if let Ok(float_value) = string.parse::() { - println!("float value: {} -> {}", string, float_value); Ok(Value::Float(OrderedFloat(float_value))) } else if let Ok(int_value) = string.parse::() { - println!("int value: {} -> {}", string, int_value); Ok(Value::Integer(int_value.into())) } else if trimmed.starts_with("'") && trimmed.ends_with("'") { Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) From 44c274b9e9fbf2d6aefac84d7b14b72195076109 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 30 Aug 2023 17:35:36 +0200 Subject: [PATCH 43/66] WIP: Pure Rust impl. of run_query --- src/machine/lib_machine.rs | 94 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index a3438783e..561462abb 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,10 +1,48 @@ use std::collections::BTreeSet; +use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; +use crate::machine::mock_wam::{CompositeOpDir, Term}; +use crate::parser::parser::{Parser, Tokens}; + use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, Atom, AtomCell, HeapCellValue, HeapCellValueTag, streams::Stream }; +use ref_thread_local::__Deref; +fn print_term(term: &Term) { + match term { + Term::Clause(clause, atom, terms) => { + println!("clause: {:?}", clause); + println!("atom: {:?}", atom.as_str()); + println!("terms: {:?}", terms); + + for term in terms { + print_term(term); + } + }, + Term::Cons(cons, term1, term2) => { + println!("constant: {:?}", cons); + println!("term1: {:?}", term1); + println!("term2: {:?}", term2); + }, + Term::Literal(cell, literal) => { + println!("Cell: {:?}", cell); + println!("Literal: {:?}", literal); + }, + Term::Var(var_reg, var_ptr) => { + println!("Var: {:?}, {:?}", var_reg.get(), var_ptr.deref()); + }, + Term::CompleteString(cell, atom) => { + println!("Cell: {:?}", cell); + println!("Atom: {:?}", atom.as_str()); + }, + _ => { + println!("Parsed query: {:?}", term); + } + } + +} impl Machine { pub fn new_lib() -> Self { @@ -25,11 +63,59 @@ impl Machine { } pub fn run_query(&mut self, query: String) -> QueryResult { - let input = format!("{}", query); + //let input = format!("{}", query); //println!("Running query: {}", input); - self.set_user_input(input); - self.run_top_level(atom!("$toplevel"), (atom!("run_input_once"), 0)); - self.parse_output() + + // Create a new Stream from the user input + let stream = Stream::from_owned_string(query.clone(), &mut self.machine_st.arena); + // Read the term from the user input + let _result = self.machine_st.read_term_from_user_input(stream, &mut self.indices); + + + // Parse the query so we can analyze and then call the term + let mut parser = Parser::new( + Stream::from_owned_string(query, &mut self.machine_st.arena), + &mut self.machine_st + ); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + let term = parser.read_term(&op_dir, Tokens::Default).expect("Failed to parse query"); + + // ... trying to understand what's going on here + print_term(&term); + + // Extract the atom from the term which we need to find the code index + let term_atom = if let Term::Clause(_, atom, _) = term { + atom + } else { + panic!("Expected a clause"); + }; + println!("term_atom: {:?}", term_atom.as_str()); + //println!("code_dir: {:?}", self.indices.code_dir); + let code_index = self.indices.code_dir.get(&(term_atom, term.arity())); + println!("code_index: {:?}", code_index); + + // Ok, we have a code_index, so we can set the program counter to the code index: + + self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; + self.machine_st.p = code_index.expect("couldn't get code index").local().unwrap(); + + println!("running dispatch loop"); + self.dispatch_loop(); + println!("done"); + + // If we don't set this register, we get an error in write_term. + // It seems to be the register that holds max_depth + self.machine_st.registers[7] = 50.into(); + + let op_dir = &self.indices.op_dir; + let printer = self.machine_st.write_term(op_dir) + .expect("Couldn't get printer from write_term") + .expect("Couldn't get printer from write_term"); + + println!("Varnames: {:?}", printer.var_names); + println!("Printer: {:?}", printer); + + Err("not implementend".to_string()) } pub fn parse_output(&self) -> QueryResult { From 2b018be392c8455e53c6705ff0623a550a0ec663 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 30 Aug 2023 20:00:56 +0200 Subject: [PATCH 44/66] Add back needed dep. hyper-util and upgrade hyper to rc4 --- Cargo.lock | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 3 ++- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76d60f0a3..fd3211c1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,13 +948,12 @@ dependencies = [ [[package]] name = "hyper" -version = "1.0.0-rc.3" +version = "1.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +checksum = "d280a71f348bcc670fc55b02b63c53a04ac0bf2daff2980795aeaf53edae10e6" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", @@ -981,6 +980,24 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.0.0" +source = "git+https://github.com/hyperium/hyper-util.git#f898015fc9eca9f459ddac521db278d904099e89" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper 1.0.0-rc.4", + "once_cell", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1590,6 +1607,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1978,7 +2015,8 @@ dependencies = [ "git-version", "hostname", "http-body-util", - "hyper 1.0.0-rc.3", + "hyper 1.0.0-rc.4", + "hyper-util", "indexmap", "lazy_static", "lexical", @@ -2478,6 +2516,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2491,6 +2551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-core", ] diff --git a/Cargo.toml b/Cargo.toml index 47a412f09..7c257008d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,8 @@ crossterm = { version = "0.20.0", optional = true } ctrlc = { version = "3.2.2", optional = true } rustyline = { version = "12.0.0", optional = true } native-tls = { version = "0.2.4", optional = true } -hyper = { version = "=1.0.0-rc.3", features = ["full"], optional = true } +hyper = { version = "=1.0.0-rc.4", features = ["full"], optional = true } +hyper-util = { git = "https://github.com/hyperium/hyper-util.git" } reqwest = { version = "0.11.18", features = ["blocking"], optional = true } tokio = { version = "1.28.2", features = ["full"] } From 8a0685a3e9292f6a4b47857313a927555e3a6423 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 30 Aug 2023 20:01:56 +0200 Subject: [PATCH 45/66] Debug print value of register 6 which leads to failing heap dereference --- src/machine/machine_state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index da6f66413..942dcfd5f 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -746,6 +746,7 @@ impl MachineState { let term_to_be_printed = self.store(self.deref(self.registers[2])); let stub_gen = || functor_stub(atom!("write_term"), 2); + println!("Register 6: {:?}", self.registers[6]); let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { let mut var_names: IndexMap = IndexMap::new(); From ef56193c4479d4b414c1b0c2788f41d85289373c Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 13 Sep 2023 17:17:02 +0200 Subject: [PATCH 46/66] Use write_term_to_heap as suggest, clean up, and include error in comment --- src/machine/lib_machine.rs | 43 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 561462abb..eac576680 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; use crate::machine::mock_wam::{CompositeOpDir, Term}; use crate::parser::parser::{Parser, Tokens}; +use crate::read::write_term_to_heap; use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, @@ -66,12 +67,6 @@ impl Machine { //let input = format!("{}", query); //println!("Running query: {}", input); - // Create a new Stream from the user input - let stream = Stream::from_owned_string(query.clone(), &mut self.machine_st.arena); - // Read the term from the user input - let _result = self.machine_st.read_term_from_user_input(stream, &mut self.indices); - - // Parse the query so we can analyze and then call the term let mut parser = Parser::new( Stream::from_owned_string(query, &mut self.machine_st.arena), @@ -80,35 +75,31 @@ impl Machine { let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); let term = parser.read_term(&op_dir, Tokens::Default).expect("Failed to parse query"); - // ... trying to understand what's going on here - print_term(&term); + // Write parsed term to heap + let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap, &mut self.machine_st.atom_tbl).expect("couldn't write term to heap"); - // Extract the atom from the term which we need to find the code index - let term_atom = if let Term::Clause(_, atom, _) = term { - atom - } else { - panic!("Expected a clause"); - }; - println!("term_atom: {:?}", term_atom.as_str()); - //println!("code_dir: {:?}", self.indices.code_dir); - let code_index = self.indices.code_dir.get(&(term_atom, term.arity())); - println!("code_index: {:?}", code_index); + // Set up registers + self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; + self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; + self.machine_st.p = self.indices.code_dir.get(&(atom!("$call"), 1)).expect("couldn't get code index").local().unwrap(); - // Ok, we have a code_index, so we can set the program counter to the code index: + // If we don't set this register, we get an error in write_term. + // It seems to be the register that holds max_depth + self.machine_st.registers[7] = 50.into(); + // Not setting this will cause: + // thread 'machine::lib_machine::tests::programatic_query' panicked at 'index out of bounds: the len is 10 but the index is 295', src/machine/machine_state_impl.rs:1479:56 + self.machine_st.registers[6] = 9.into(); - self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; - self.machine_st.p = code_index.expect("couldn't get code index").local().unwrap(); println!("running dispatch loop"); self.dispatch_loop(); println!("done"); - // If we don't set this register, we get an error in write_term. - // It seems to be the register that holds max_depth - self.machine_st.registers[7] = 50.into(); - let op_dir = &self.indices.op_dir; - let printer = self.machine_st.write_term(op_dir) + let write_term_result = self.machine_st.write_term(op_dir); + println!("write_term_result: {:?}", write_term_result); + // => Err([HeapCellValue { tag: Atom, name: "error", arity: 2, m: false, f: false }, HeapCellValue { tag: Str, value: 13, m: false, f: false }, HeapCellValue { tag: Str, value: 16, m: false, f: false }, HeapCellValue { tag: Atom, name: "type_error", arity: 2, m: false, f: false }, HeapCellValue { tag: Atom, name: "list", arity: 0, m: false, f: false }, HeapCellValue { tag: Cons, ptr: 9, m: false, f: false }, HeapCellValue { tag: Atom, name: "/", arity: 2, m: false, f: false }, HeapCellValue { tag: Atom, name: "write_term", arity: 0, m: false, f: false }, HeapCellValue { tag: Fixnum, value: 2, m: false, f: false }]) + let printer = write_term_result .expect("Couldn't get printer from write_term") .expect("Couldn't get printer from write_term"); From 5fa68e253c04a6b1694bee1ead9a11620cb10285 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Wed, 13 Sep 2023 19:51:57 +0200 Subject: [PATCH 47/66] lib_machine tests should not be tokio/async --- src/machine/lib_machine.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index c2b8966a4..28f82264e 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -147,8 +147,8 @@ mod tests { use super::*; use crate::machine::{QueryMatch, Value, QueryResolution}; - #[tokio::test] - async fn programatic_query() { + #[test] + fn programatic_query() { let mut machine = Machine::new_lib(); machine.load_module_string( @@ -186,8 +186,8 @@ mod tests { ); } - #[tokio::test] - async fn failing_query() { + #[test] + fn failing_query() { let mut machine = Machine::new_lib(); let query = String::from(r#"triple("a",P,"b")."#); let output = machine.run_query(query); @@ -197,8 +197,8 @@ mod tests { ); } - #[tokio::test] - async fn complex_results() { + #[test] + fn complex_results() { let mut machine = Machine::new_lib(); machine.load_module_string( "facts", @@ -249,8 +249,8 @@ mod tests { ); } - #[tokio::test] - async fn list_results() { + #[test] + fn list_results() { let mut machine = Machine::new_lib(); machine.load_module_string( "facts", @@ -270,8 +270,8 @@ mod tests { } - #[tokio::test] - async fn consult() { + #[test] + fn consult() { let mut machine = Machine::new_lib(); machine.consult_module_string( @@ -330,8 +330,8 @@ mod tests { } #[ignore = "fails on windows"] - #[tokio::test] - async fn stress_integration_test() { + #[test] + fn stress_integration_test() { let mut machine = Machine::new_lib(); // File with test commands, i.e. program code to consult and queries to run From 6bdd7f3a3f2730d6d13d4ba56e97da6711c9dd77 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 14 Sep 2023 22:00:50 +0200 Subject: [PATCH 48/66] mthom's revised run_query with manually created printer and some cleanup --- src/machine/lib_machine.rs | 57 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 28f82264e..9f669c392 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,8 +1,10 @@ use std::collections::BTreeSet; +use std::sync::Arc; use crate::atom_table; +use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter}; use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; -use crate::machine::mock_wam::{CompositeOpDir, Term}; +use crate::machine::mock_wam::{CompositeOpDir, Term, Fixnum}; use crate::parser::parser::{Parser, Tokens}; use crate::read::write_term_to_heap; @@ -65,9 +67,6 @@ impl Machine { } pub fn run_query(&mut self, query: String) -> QueryResult { - //let input = format!("{}", query); - //println!("Running query: {}", input); - // Parse the query so we can analyze and then call the term let mut parser = Parser::new( Stream::from_owned_string(query, &mut self.machine_st.arena), @@ -79,33 +78,39 @@ impl Machine { // Write parsed term to heap let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap, &mut self.machine_st.atom_tbl).expect("couldn't write term to heap"); - // Set up registers + // Write term to heap self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; + // Call the term self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; - self.machine_st.p = self.indices.code_dir.get(&(atom!("$call"), 1)).expect("couldn't get code index").local().unwrap(); - - // If we don't set this register, we get an error in write_term. - // It seems to be the register that holds max_depth - self.machine_st.registers[7] = 50.into(); - // Not setting this will cause: - // thread 'machine::lib_machine::tests::programatic_query' panicked at 'index out of bounds: the len is 10 but the index is 295', src/machine/machine_state_impl.rs:1479:56 - self.machine_st.registers[6] = 9.into(); - - - println!("running dispatch loop"); + self.machine_st.p = self.indices.code_dir.get(&(atom!("call"), 1)).expect("couldn't get code index").local().unwrap(); self.dispatch_loop(); - println!("done"); - - let op_dir = &self.indices.op_dir; - let write_term_result = self.machine_st.write_term(op_dir); - println!("write_term_result: {:?}", write_term_result); - // => Err([HeapCellValue { tag: Atom, name: "error", arity: 2, m: false, f: false }, HeapCellValue { tag: Str, value: 13, m: false, f: false }, HeapCellValue { tag: Str, value: 16, m: false, f: false }, HeapCellValue { tag: Atom, name: "type_error", arity: 2, m: false, f: false }, HeapCellValue { tag: Atom, name: "list", arity: 0, m: false, f: false }, HeapCellValue { tag: Cons, ptr: 9, m: false, f: false }, HeapCellValue { tag: Atom, name: "/", arity: 2, m: false, f: false }, HeapCellValue { tag: Atom, name: "write_term", arity: 0, m: false, f: false }, HeapCellValue { tag: Fixnum, value: 2, m: false, f: false }]) - let printer = write_term_result - .expect("Couldn't get printer from write_term") - .expect("Couldn't get printer from write_term"); + + + // NOTE: mimic writeq settings here (see arguments of '$write_term'/8 at builtins.pl:693) + self.machine_st.registers[8] = atom_as_cell!(atom!("false")); + self.machine_st.registers[7] = fixnum_as_cell!(Fixnum::build_with(10)); + self.machine_st.registers[6] = empty_list_as_cell!(); + self.machine_st.registers[5] = atom_as_cell!(atom!("true")); + self.machine_st.registers[4] = atom_as_cell!(atom!("true")); + self.machine_st.registers[3] = atom_as_cell!(atom!("false")); + + let term_to_be_printed = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + + let mut printer = HCPrinter::new( + &mut self.machine_st.heap, + Arc::clone(&self.machine_st.atom_tbl), + &mut self.machine_st.stack, + &self.indices.op_dir, + PrinterOutputter::new(), + term_to_be_printed, + ); println!("Varnames: {:?}", printer.var_names); - println!("Printer: {:?}", printer); + printer.max_depth = 1000; + let output = printer.print(); + println!("Print: {:?}", output); + println!("Result: {:?}", output.result()); + Err("not implementend".to_string()) } From 48283c4dbc7ff1ba080dfdfaac17b7320d7b47cd Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 15 Sep 2023 10:04:11 +0200 Subject: [PATCH 49/66] mthom's changes to run_query with backtracking --- src/machine/lib_machine.rs | 90 ++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 9f669c392..d7a6036df 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -7,6 +7,9 @@ use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; use crate::machine::mock_wam::{CompositeOpDir, Term, Fixnum}; use crate::parser::parser::{Parser, Tokens}; use crate::read::write_term_to_heap; +use crate::machine::machine_indices::VarKey; +use crate::parser::ast::{Var, VarPtr}; +use indexmap::IndexMap; use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, @@ -80,37 +83,68 @@ impl Machine { // Write term to heap self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; - // Call the term + self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.p = self.indices.code_dir.get(&(atom!("call"), 1)).expect("couldn't get code index").local().unwrap(); - self.dispatch_loop(); - - - // NOTE: mimic writeq settings here (see arguments of '$write_term'/8 at builtins.pl:693) - self.machine_st.registers[8] = atom_as_cell!(atom!("false")); - self.machine_st.registers[7] = fixnum_as_cell!(Fixnum::build_with(10)); - self.machine_st.registers[6] = empty_list_as_cell!(); - self.machine_st.registers[5] = atom_as_cell!(atom!("true")); - self.machine_st.registers[4] = atom_as_cell!(atom!("true")); - self.machine_st.registers[3] = atom_as_cell!(atom!("false")); - - let term_to_be_printed = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - - let mut printer = HCPrinter::new( - &mut self.machine_st.heap, - Arc::clone(&self.machine_st.atom_tbl), - &mut self.machine_st.stack, - &self.indices.op_dir, - PrinterOutputter::new(), - term_to_be_printed, - ); - - println!("Varnames: {:?}", printer.var_names); - printer.max_depth = 1000; - let output = printer.print(); - println!("Print: {:?}", output); - println!("Result: {:?}", output.result()); + let var_names: IndexMap<_, _> = term_write_result.var_dict.iter() + .map(|(var_key, cell)| match var_key { + // NOTE: not the intention behind Var::InSitu here but + // we can hijack it to store anonymous variables + // without creating problems. + VarKey::AnonVar(h) => (*cell, VarPtr::from(Var::InSitu(*h))), + VarKey::VarPtr(var_ptr) => (*cell, var_ptr.clone()), + }) + .collect(); + + // Call the term + loop { + self.dispatch_loop(); + + if self.machine_st.fail { + // NOTE: only print results on success + self.machine_st.fail = false; + break; + } + + for (var_key, term_to_be_printed) in &term_write_result.var_dict { + let mut printer = HCPrinter::new( + &mut self.machine_st.heap, + Arc::clone(&self.machine_st.atom_tbl), + &mut self.machine_st.stack, + &self.indices.op_dir, + PrinterOutputter::new(), + *term_to_be_printed, + ); + + // NOTE: I've converted the former register settings + // for '$write_term'/8 to printer settings. Registers + // are not read by the printer. + printer.ignore_ops = false; + printer.numbervars = true; + printer.quoted = true; + printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth + printer.double_quotes = false; + printer.var_names = var_names.clone(); + + println!("Varnames: {:?}", printer.var_names); + + let output = printer.print(); + + println!("Print: {:?}", output); + println!("Result: {} = {}", var_key.to_string(), output.result()); + } + + if self.machine_st.b > 0 { + // NOTE: there are outstanding choicepoints, backtrack + // through them for further solutions. + self.machine_st.backtrack(); + } else { + // NOTE: out of choicepoints to backtrack through, no + // more solutions to gather. + break; + } + } Err("not implementend".to_string()) } From f02728aab3c6a359e5c0d30a8d40f7f5b8ae6ed8 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 15 Sep 2023 10:44:59 +0200 Subject: [PATCH 50/66] Construct QueryResult from printer output --- src/machine/lib_machine.rs | 50 +++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index d7a6036df..1a6fb6861 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,10 +1,10 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeSet, BTreeMap}; use std::sync::Arc; use crate::atom_table; use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter}; use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; -use crate::machine::mock_wam::{CompositeOpDir, Term, Fixnum}; +use crate::machine::mock_wam::{CompositeOpDir, Term}; use crate::parser::parser::{Parser, Tokens}; use crate::read::write_term_to_heap; use crate::machine::machine_indices::VarKey; @@ -13,7 +13,7 @@ use indexmap::IndexMap; use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, - Atom, AtomCell, HeapCellValue, HeapCellValueTag, + Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryMatch, QueryResolution, streams::Stream }; use ref_thread_local::__Deref; @@ -70,6 +70,7 @@ impl Machine { } pub fn run_query(&mut self, query: String) -> QueryResult { + println!("Query: {}", query); // Parse the query so we can analyze and then call the term let mut parser = Parser::new( Stream::from_owned_string(query, &mut self.machine_st.arena), @@ -97,6 +98,7 @@ impl Machine { }) .collect(); + let mut matches: Vec = Vec::new(); // Call the term loop { self.dispatch_loop(); @@ -104,10 +106,17 @@ impl Machine { if self.machine_st.fail { // NOTE: only print results on success self.machine_st.fail = false; + println!("false"); + matches.push(QueryResolutionLine::False); break; } + let mut bindings: BTreeMap = BTreeMap::new(); + for (var_key, term_to_be_printed) in &term_write_result.var_dict { + if var_key.to_string().starts_with("_") { + continue; + } let mut printer = HCPrinter::new( &mut self.machine_st.heap, Arc::clone(&self.machine_st.atom_tbl), @@ -117,36 +126,37 @@ impl Machine { *term_to_be_printed, ); - // NOTE: I've converted the former register settings - // for '$write_term'/8 to printer settings. Registers - // are not read by the printer. printer.ignore_ops = false; printer.numbervars = true; printer.quoted = true; printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth - printer.double_quotes = false; + printer.double_quotes = true; printer.var_names = var_names.clone(); - println!("Varnames: {:?}", printer.var_names); - - let output = printer.print(); - - println!("Print: {:?}", output); - println!("Result: {} = {}", var_key.to_string(), output.result()); + let outputter = printer.print(); + + let output: String = outputter.result(); + println!("Result: {} = {}", var_key.to_string(), output); + + bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs")); } + matches.push(QueryResolutionLine::Match(bindings)); + if self.machine_st.b > 0 { + println!("b: {}", self.machine_st.b); // NOTE: there are outstanding choicepoints, backtrack // through them for further solutions. self.machine_st.backtrack(); } else { + println!("breaking"); // NOTE: out of choicepoints to backtrack through, no // more solutions to gather. break; } } - Err("not implementend".to_string()) + Ok(QueryResolution::from(matches)) } pub fn parse_output(&self) -> QueryResult { @@ -183,6 +193,8 @@ impl Machine { #[cfg(test)] mod tests { + use ordered_float::OrderedFloat; + use super::*; use crate::machine::{QueryMatch, Value, QueryResolution}; @@ -279,10 +291,10 @@ mod tests { result, Ok(QueryResolution::Matches(vec![ QueryMatch::from(btreemap! { - "Class" => Value::from("Recipe") + "Class" => Value::from("Todo") }), QueryMatch::from(btreemap! { - "Class" => Value::from("Todo") + "Class" => Value::from("Recipe") }), ])) ); @@ -302,7 +314,11 @@ mod tests { result, Ok(QueryResolution::Matches(vec![ QueryMatch::from(btreemap! { - "X" => Value::List(Vec::from([Value::from("1"), Value::from("2"), Value::from("3")])) + "X" => Value::List(Vec::from([ + Value::Float(OrderedFloat(1.0)), + Value::Float(OrderedFloat(2.0)), + Value::Float(OrderedFloat(3.0)), + ])) }), ])) ); From 7c83a1fb8ec186c63eb5f1ca256f3194c3918bea Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 15 Sep 2023 10:48:41 +0200 Subject: [PATCH 51/66] Cleanup code that's not needed anymore --- src/machine/lib_machine.rs | 71 ++------------------------------------ 1 file changed, 3 insertions(+), 68 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 1a6fb6861..b5a49729c 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -1,10 +1,10 @@ -use std::collections::{BTreeSet, BTreeMap}; +use std::collections::BTreeMap; use std::sync::Arc; use crate::atom_table; use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter}; use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; -use crate::machine::mock_wam::{CompositeOpDir, Term}; +use crate::machine::mock_wam::CompositeOpDir; use crate::parser::parser::{Parser, Tokens}; use crate::read::write_term_to_heap; use crate::machine::machine_indices::VarKey; @@ -13,43 +13,9 @@ use indexmap::IndexMap; use super::{ Machine, MachineConfig, QueryResult, QueryResolutionLine, - Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryMatch, QueryResolution, + Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryResolution, streams::Stream }; -use ref_thread_local::__Deref; -fn print_term(term: &Term) { - match term { - Term::Clause(clause, atom, terms) => { - println!("clause: {:?}", clause); - println!("atom: {:?}", atom.as_str()); - println!("terms: {:?}", terms); - - for term in terms { - print_term(term); - } - }, - Term::Cons(cons, term1, term2) => { - println!("constant: {:?}", cons); - println!("term1: {:?}", term1); - println!("term2: {:?}", term2); - }, - Term::Literal(cell, literal) => { - println!("Cell: {:?}", cell); - println!("Literal: {:?}", literal); - }, - Term::Var(var_reg, var_ptr) => { - println!("Var: {:?}, {:?}", var_reg.get(), var_ptr.deref()); - }, - Term::CompleteString(cell, atom) => { - println!("Cell: {:?}", cell); - println!("Atom: {:?}", atom.as_str()); - }, - _ => { - println!("Parsed query: {:?}", term); - } - } - -} impl Machine { pub fn new_lib() -> Self { @@ -158,37 +124,6 @@ impl Machine { Ok(QueryResolution::from(matches)) } - - pub fn parse_output(&self) -> QueryResult { - let output = self.get_user_output().trim().to_string(); - //println!("Output: {}", output); - if output.starts_with("error(") { - Err(output) - } else { - // Remove duplicate lines - Ok(output - // 1. Split into disjunct matches - .split(";") - .map(|s| s.trim()) - // 2. Dedupe through Set - .collect::>() - .iter() - .cloned() - // 3. Back to Vec - .collect::>() - .iter() - // 4. Trim and remove empty lines - .map(|s| s.trim()) - .map(|s| if s.ends_with(".") { s[..s.len()-1].to_string() } else { s.to_string() }) - .filter(|s| !s.is_empty()) - // 5. Parse into QueryResolutionLine - .map(QueryResolutionLine::try_from) - // 6. Remove lines that couldn't be parsed, so we still keep the ones they did - .filter_map(Result::ok) - .collect::>() - .into()) - } - } } #[cfg(test)] From c86304b18e57b0296cd43be2653b02edbc6db1c5 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 15 Sep 2023 11:01:16 +0200 Subject: [PATCH 52/66] Remove lib_toplevel.pl --- src/lib_toplevel.pl | 207 ------------------------------------- src/machine/lib_machine.rs | 2 +- 2 files changed, 1 insertion(+), 208 deletions(-) delete mode 100644 src/lib_toplevel.pl diff --git a/src/lib_toplevel.pl b/src/lib_toplevel.pl deleted file mode 100644 index 4b13f9a4b..000000000 --- a/src/lib_toplevel.pl +++ /dev/null @@ -1,207 +0,0 @@ -:- module('$toplevel', [argv/1, - copy_term/3]). - -:- use_module(library(atts), [call_residue_vars/2]). -:- use_module(library(charsio)). -:- use_module(library(error)). - -:- use_module(library(iso_ext)). -:- use_module(library(lambda)). -:- use_module(library(lists)). -:- use_module(library(si)). - -:- use_module(library('$project_atts')). -:- use_module(library('$atts')). - -:- dynamic(disabled_init_file/0). - -:- dynamic(argv/1). - - -arg_type(g). -arg_type(t). -arg_type(g(_)). -arg_type(t(_)). - -trailing_period_is_ambiguous(Value) :- - atom(Value), - atom_chars(Value, ValueChars), - list_last_item(ValueChars, Char), - ValueChars \== ['.'], - graphic_token_char(Char). - -graphic_token_char(C) :- - memberchk(C, ['#', '$', '&', '*', '+', '-', '.', ('/'), ':', - '<', '=', '>', '?', '@', '^', '~', ('\\')]). - -needs_bracketing(Value, Op) :- - catch((functor(Value, F, _), - current_op(EqPrec, EqSpec, Op), - current_op(FPrec, _, F)), - _, - false), - ( EqPrec < FPrec -> - true - ; FPrec > 0, F == Value, graphic_token_char(F) -> - true - ; F \== '.', '$quoted_token'(F) -> - true - ; EqPrec == FPrec, - memberchk(EqSpec, [fx,xfx,yfx]) - ). - -write_goal(G, VarList, MaxDepth) :- - ( G = (Var = Value) -> - ( var(Value) -> - select((Var = _), VarList, NewVarList) - ; VarList = NewVarList - ), - write(Var), - write(' = '), - ( needs_bracketing(Value, (=)) -> - write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), - write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]) - ) - ; G == [] -> - write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) - ). - - -write_last_goal(G, VarList, MaxDepth) :- - ( G = (Var = Value) -> - ( var(Value) -> - select((Var = _), VarList, NewVarList) - ; VarList = NewVarList - ), - write(Var), - write(' = '), - ( needs_bracketing(Value, (=)) -> - write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), - write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), - ( trailing_period_is_ambiguous(Value) -> - write(' ') - ; true - ) - ) - ; G == [] -> - write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) - ). - -write_eq((G1, G2), VarList, MaxDepth) :- - !, - write_goal(G1, VarList, MaxDepth), - write(', '), - write_eq(G2, VarList, MaxDepth). -write_eq(G, VarList, MaxDepth) :- - write_last_goal(G, VarList, MaxDepth). - -list_last_item([C], C) :- !. -list_last_item([_|Cs], D) :- - list_last_item(Cs, D). - -term_variables_under_max_depth(Term, MaxDepth, Vars) :- - '$term_variables_under_max_depth'(Term, MaxDepth, Vars). - -gather_query_vars([_ = Var | Vars], QueryVars) :- - ( var(Var) -> - QueryVars = [Var | QueryVars0], - gather_query_vars(Vars, QueryVars0) - ; - gather_query_vars(Vars, QueryVars) - ). -gather_query_vars([], []). - -gather_equations([], _, []). -gather_equations([Var = Value | Pairs], OrigVarList, Goals) :- - ( var(Value) -> - ( eq_member(Value, OrigVarList), - select_all(Pairs, Var, Value, [_ | VarEqs], NewPairs) -> - append([Var = Value | VarEqs], Goals0, Goals), - gather_equations(NewPairs, OrigVarList, Goals0) - ; - gather_equations(Pairs, OrigVarList, Goals) - ) - ; - Goals = [Var = Value | Goals0], - gather_equations(Pairs, OrigVarList, Goals0) - ). - -print_exception(E) :- - ( E == error('$interrupt_thrown', repl) -> nl % print the - % exception on a - % newline to evade - % "^C". - ; true - ), - loader:write_error(E), - nl. - - -run_input_once :- - bb_put('$report_all', true), - catch(read_and_match_all_results, E, print_exception(E)). - -read_and_match_all_results :- - '$read_query_term'(_, Term, _, _, VarList), - bb_put('$answer_count', 0), - submit_query_and_print_all_results(Term, VarList). - -submit_query_and_print_all_results(Term, VarList) :- - '$get_b_value'(B), - bb_put('$report_all', true), - bb_put('$report_n_more', 100), - call(user:Term), - write_eqs(B, VarList), - !. -submit_query_and_print_all_results(_, _) :- - ( bb_get('$answer_count', 0) -> - write(' ') - ; true - ), - write('false.'), - nl. - -write_eqs(B, VarList) :- - gather_query_vars(VarList, OrigVars), - % one layer of depth added for (=/2) functor - '$term_variables_under_max_depth'(OrigVars, 22, Vars0), - '$term_attributed_variables'(VarList, AttrVars), - '$project_atts':project_attributes(Vars0, AttrVars), - copy_term(AttrVars, AttrVars, AttrGoals), - term_variables(AttrGoals, AttrGoalVars), - append([Vars0, AttrGoalVars, AttrVars], Vars), - charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), - '$get_b_value'(B0), - gather_equations(NewVarList, OrigVars, Equations), - append(Equations, AttrGoals, Goals), - % one layer of depth added for (=/2) functor - maplist(\Term^Vs^term_variables_under_max_depth(Term, 22, Vs), Equations, EquationVars), - % maplist(term_variables_under_max_depth(22), Equations, EquationVars), - append([AttrGoalVars | EquationVars], Vars1), - term_variables(Vars1, Vars2), % deduplicate vars of Vars1 but preserve their order. - charsio:extend_var_list(Vars2, VarList, NewVarList0, fabricated), - bb_get('$answer_count', Count), - ( Count =:= 0 -> - write(' ') - ; true - ), - Count1 is Count + 1, - bb_put('$answer_count', Count1), - ( B0 == B -> - ( Goals == [] -> - write('true.'), nl - ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 200000), - write('.'), - nl - ) - ; loader:thread_goals(Goals, ThreadedGoals, (',')), - write_eq(ThreadedGoals, NewVarList0, 200000), - write(';'), nl, false - ). diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index b5a49729c..47a840f52 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -19,7 +19,7 @@ use super::{ impl Machine { pub fn new_lib() -> Self { - Machine::new(MachineConfig::in_memory().with_toplevel(include_str!("../lib_toplevel.pl"))) + Machine::new(MachineConfig::in_memory()) } pub fn load_module_string(&mut self, module_name: &str, program: String) { From d2f52914125bc9c0c24c1f500705a6c752dbe791 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 18 Sep 2023 13:01:27 +0200 Subject: [PATCH 53/66] mthom's changes fixing the panic --- src/machine/lib_machine.rs | 56 +++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 47a840f52..d1b6445e5 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -35,6 +35,28 @@ impl Machine { self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); } + fn allocate_stub_choice_point(&mut self) { + // NOTE: create a choice point to terminate the dispatch_loop + // if an exception is thrown. since the and/or stack is presumed empty, + + let stub_b = self.machine_st.stack.allocate_or_frame(0); + let or_frame = self.machine_st.stack.index_or_frame_mut(0); + + or_frame.prelude.num_cells = 0; + or_frame.prelude.e = 0; + or_frame.prelude.cp = 0; + or_frame.prelude.b = 0; + or_frame.prelude.bp = BREAK_FROM_DISPATCH_LOOP_LOC; + or_frame.prelude.boip = 0; + or_frame.prelude.biip = 0; + or_frame.prelude.tr = 0; + or_frame.prelude.h = 0; + or_frame.prelude.b0 = 0; + or_frame.prelude.attr_var_queue_len = 0; + + self.machine_st.b = stub_b; + } + pub fn run_query(&mut self, query: String) -> QueryResult { println!("Query: {}", query); // Parse the query so we can analyze and then call the term @@ -64,6 +86,10 @@ impl Machine { }) .collect(); + self.allocate_stub_choice_point(); + + let stub_b = self.machine_st.b; + let mut matches: Vec = Vec::new(); // Call the term loop { @@ -75,10 +101,18 @@ impl Machine { println!("false"); matches.push(QueryResolutionLine::False); break; + } else if self.machine_st.ball.stub.len() != 0 { + // NOTE: this means an exception was thrown, at which + // point we backtracked to the stub choice point. + // this should halt the search for solutions as it + // does in the Scryer top-level. the exception term is + // contained in self.machine_st.ball. + println!("exception thrown"); + break; } let mut bindings: BTreeMap = BTreeMap::new(); - + for (var_key, term_to_be_printed) in &term_write_result.var_dict { if var_key.to_string().starts_with("_") { continue; @@ -100,20 +134,23 @@ impl Machine { printer.var_names = var_names.clone(); let outputter = printer.print(); - + let output: String = outputter.result(); println!("Result: {} = {}", var_key.to_string(), output); - + bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs")); } matches.push(QueryResolutionLine::Match(bindings)); - if self.machine_st.b > 0 { + // NOTE: there are outstanding choicepoints, backtrack + // through them for further solutions. if + // self.machine_st.b == stub_b we've backtracked to the stub + // choice point, so we should break. + self.machine_st.backtrack(); + + if self.machine_st.b > stub_b { println!("b: {}", self.machine_st.b); - // NOTE: there are outstanding choicepoints, backtrack - // through them for further solutions. - self.machine_st.backtrack(); } else { println!("breaking"); // NOTE: out of choicepoints to backtrack through, no @@ -122,6 +159,11 @@ impl Machine { } } + // NOTE: deallocate stub choice point + if self.machine_st.b == stub_b { + self.trust_me(); + } + Ok(QueryResolution::from(matches)) } } From 65f64e428e04d4d02ab61b294b3fa75bde319ef1 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 18 Sep 2023 13:25:27 +0200 Subject: [PATCH 54/66] Construct and return exception string --- src/machine/lib_machine.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index d1b6445e5..6c4c262f8 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -107,8 +107,27 @@ impl Machine { // this should halt the search for solutions as it // does in the Scryer top-level. the exception term is // contained in self.machine_st.ball. - println!("exception thrown"); - break; + let error_string = self.machine_st.ball.stub + .iter() + .filter(|h| match h.get_tag() { + HeapCellValueTag::Atom => true, + HeapCellValueTag::Fixnum => true, + _ => false, + }) + .map(|h| match h.get_tag() { + HeapCellValueTag::Atom => { + let (name, _) = cell_as_atom_cell!(h).get_name_and_arity(); + name.as_str().to_string() + } + HeapCellValueTag::Fixnum => { + h.get_value().clone().to_string() + }, + _ => unreachable!(), + }) + .collect::>() + .join(" "); + + return Err(error_string); } let mut bindings: BTreeMap = BTreeMap::new(); @@ -221,7 +240,7 @@ mod tests { let output = machine.run_query(query); assert_eq!( output, - Err(String::from("error(existence_error(procedure,triple/3),triple/3).")) + Err(String::from("error existence_error procedure / triple 3 / triple 3")) ); } From 5e556257333c29dc0b7be2801d094e77cd234168 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 18 Sep 2023 19:12:50 +0200 Subject: [PATCH 55/66] Remove some debug println!s --- src/machine/lib_machine.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 6c4c262f8..18d6c7e63 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -168,10 +168,7 @@ impl Machine { // choice point, so we should break. self.machine_st.backtrack(); - if self.machine_st.b > stub_b { - println!("b: {}", self.machine_st.b); - } else { - println!("breaking"); + if self.machine_st.b <= stub_b { // NOTE: out of choicepoints to backtrack through, no // more solutions to gather. break; From 4968fa00240602565cc99bb81df2cac38a110d32 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 18 Sep 2023 19:31:56 +0200 Subject: [PATCH 56/66] Remove debugging println! --- src/machine/machine_state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index d3fc84d2c..0b9e06dcd 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -759,7 +759,6 @@ impl MachineState { let term_to_be_printed = self.store(self.deref(self.registers[2])); let stub_gen = || functor_stub(atom!("write_term"), 2); - println!("Register 6: {:?}", self.registers[6]); let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { let mut var_names: IndexMap = IndexMap::new(); From 7b128a9f00215bf4362c12303fba3748bb19dbed Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 3 Oct 2023 20:12:11 +0200 Subject: [PATCH 57/66] Handle stub_b = b as false --- src/machine/lib_machine.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 18d6c7e63..83e5b048d 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -95,13 +95,11 @@ impl Machine { loop { self.dispatch_loop(); - if self.machine_st.fail { - // NOTE: only print results on success - self.machine_st.fail = false; - println!("false"); - matches.push(QueryResolutionLine::False); - break; - } else if self.machine_st.ball.stub.len() != 0 { + //println!("b: {}", self.machine_st.b); + //println!("stub_b: {}", stub_b); + //println!("fail: {}", self.machine_st.fail); + + if self.machine_st.ball.stub.len() != 0 { // NOTE: this means an exception was thrown, at which // point we backtracked to the stub choice point. // this should halt the search for solutions as it @@ -128,7 +126,23 @@ impl Machine { .join(" "); return Err(error_string); - } + } + + if self.machine_st.fail { + // NOTE: only print results on success + self.machine_st.fail = false; + println!("fail!"); + matches.push(QueryResolutionLine::False); + break; + }; + + if self.machine_st.b == stub_b && term_write_result.var_dict.len() == 0 { + // NOTE: only print results on success + self.machine_st.fail = false; + println!("b == stub_b"); + matches.push(QueryResolutionLine::False); + break; + }; let mut bindings: BTreeMap = BTreeMap::new(); From fab5ca9440ce4391b2ed83647ac45a70a456a3cf Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 3 Oct 2023 20:19:50 +0200 Subject: [PATCH 58/66] 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 83e5b048d..65b4591fb 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 f0436da47..38632855b 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("}") { From 69cf2c36bc0f713aa8166ae2842285450e699881 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 5 Oct 2023 12:11:28 +0200 Subject: [PATCH 59/66] Deactivate tokio runtime respawning on interrupt --- src/machine/dispatch.rs | 26 ++++++++++++++++---------- src/machine/system_calls.rs | 9 +++++++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 23fe7c029..771fe91f4 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5269,16 +5269,22 @@ impl Machine { self.machine_st.throw_interrupt_exception(); self.machine_st.backtrack(); - #[cfg(not(target_arch = "wasm32"))] - let runtime = tokio::runtime::Runtime::new().unwrap(); - #[cfg(target_arch = "wasm32")] - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - let old_runtime = std::mem::replace(&mut self.runtime, runtime); - old_runtime.shutdown_background(); + // We have extracted controll over the Tokio runtime to the calling context for enabling library use case + // (see https://github.com/mthom/scryer-prolog/pull/1880) + // So we only have access to a runtime handle in here and can't shut it down. + // Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880 + // was not merged, I'm only deactivating it for now. + + //#[cfg(not(target_arch = "wasm32"))] + //let runtime = tokio::runtime::Runtime::new().unwrap(); + //#[cfg(target_arch = "wasm32")] + //let runtime = tokio::runtime::Builder::new_current_thread() + // .enable_all() + // .build() + // .unwrap(); + + //let old_runtime = tokio::runtime::Handle::current(); + //old_runtime.shutdown_background(); } } Err(_) => unreachable!(), diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 86fededf7..5c482636a 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4615,8 +4615,13 @@ impl Machine { if interruption { self.machine_st.throw_interrupt_exception(); self.machine_st.backtrack(); - let old_runtime = std::mem::replace(&mut self.runtime, tokio::runtime::Runtime::new().unwrap()); - old_runtime.shutdown_background(); + // We have extracted controll over the Tokio runtime to the calling context for enabling library use case + // (see https://github.com/mthom/scryer-prolog/pull/1880) + // So we only have access to a runtime handle in here and can't shut it down. + // Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880 + // was not merged, I'm only deactivating it for now. + //let old_runtime = std::mem::replace(&mut self.runtime, tokio::runtime::Runtime::new().unwrap()); + //old_runtime.shutdown_background(); break } } From bfa7d3cf415bdf454d2c8d0a59d8e0c462171081 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 16 Oct 2023 13:44:51 +0200 Subject: [PATCH 60/66] Remove unuse import --- src/machine/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 45688ff4b..61b1909bc 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -60,7 +60,6 @@ use std::sync::atomic::AtomicBool; use self::config::MachineConfig; use self::parsed_results::*; -use tokio::runtime::Runtime; use rand::rngs::StdRng; use rand::SeedableRng; From 025412aac0cb1af1efbb65d574b01d7f8f7fe83b Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 16 Oct 2023 14:55:38 +0200 Subject: [PATCH 61/66] Revert "Remove unuse import" This reverts commit bfa7d3cf415bdf454d2c8d0a59d8e0c462171081. --- src/machine/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 61b1909bc..45688ff4b 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -60,6 +60,7 @@ use std::sync::atomic::AtomicBool; use self::config::MachineConfig; use self::parsed_results::*; +use tokio::runtime::Runtime; use rand::rngs::StdRng; use rand::SeedableRng; From d7fa6c0ade713c8d3bf9c8ee7e2856625712f422 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 16 Oct 2023 14:56:17 +0200 Subject: [PATCH 62/66] Fix signalling of success/failure (by @mthom) --- src/machine/lib_machine.rs | 47 ++++++++++++++++++++++---------------- src/machine/mod.rs | 5 ++-- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 65b4591fb..78ff50c76 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::atom_table; use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter}; -use crate::machine::BREAK_FROM_DISPATCH_LOOP_LOC; +use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS}; use crate::machine::mock_wam::CompositeOpDir; use crate::parser::parser::{Parser, Tokens}; use crate::read::write_term_to_heap; @@ -12,7 +12,7 @@ use crate::parser::ast::{Var, VarPtr}; use indexmap::IndexMap; use super::{ - Machine, MachineConfig, QueryResult, QueryResolutionLine, + Machine, MachineConfig, QueryResult, QueryResolutionLine, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryResolution, streams::Stream }; @@ -73,7 +73,7 @@ impl Machine { // Write term to heap self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; - self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; + self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.p = self.indices.code_dir.get(&(atom!("call"), 1)).expect("couldn't get code index").local().unwrap(); let var_names: IndexMap<_, _> = term_write_result.var_dict.iter() @@ -122,27 +122,34 @@ impl Machine { }, _ => unreachable!(), }) - .collect::>() + .collect::>() .join(" "); - + return Err(error_string); - } - + } + + /* if self.machine_st.fail { // NOTE: only print results on success self.machine_st.fail = false; println!("fail!"); matches.push(QueryResolutionLine::False); break; - }; - - if self.machine_st.b == stub_b && term_write_result.var_dict.len() == 0 { - // NOTE: only print results on success - self.machine_st.fail = false; - println!("b == stub_b"); - matches.push(QueryResolutionLine::False); - break; - }; + }; + */ + + if term_write_result.var_dict.is_empty() { + if self.machine_st.p == LIB_QUERY_SUCCESS { + matches.push(QueryResolutionLine::True); + break; + } else if self.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC { + // NOTE: only print results on success + // self.machine_st.fail = false; + // println!("b == stub_b"); + matches.push(QueryResolutionLine::False); + break; + } + } let mut bindings: BTreeMap = BTreeMap::new(); @@ -323,8 +330,8 @@ mod tests { QueryMatch::from(btreemap! { "X" => Value::List( Vec::from([ - Value::Float(OrderedFloat::from(1.0)), - Value::Float(OrderedFloat::from(2.0)), + Value::Float(OrderedFloat::from(1.0)), + Value::Float(OrderedFloat::from(2.0)), Value::Float(OrderedFloat::from(3.0)) ]) ) @@ -464,8 +471,8 @@ mod tests { "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()), + 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/mod.rs b/src/machine/mod.rs index 45688ff4b..38afac5b4 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -60,7 +60,6 @@ use std::sync::atomic::AtomicBool; use self::config::MachineConfig; use self::parsed_results::*; -use tokio::runtime::Runtime; use rand::rngs::StdRng; use rand::SeedableRng; @@ -118,6 +117,7 @@ include!(concat!(env!("OUT_DIR"), "/libraries.rs")); pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0; pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1; pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2; +pub static LIB_QUERY_SUCCESS: usize = 3; pub struct MachinePreludeView<'a> { pub indices: &'a mut IndexStore, @@ -397,13 +397,14 @@ impl Machine { } pub(crate) fn add_impls_to_indices(&mut self) { - let impls_offset = self.code.len() + 3; + let impls_offset = self.code.len() + 4; self.code.extend( vec![ Instruction::BreakFromDispatchLoop, Instruction::InstallVerifyAttr, Instruction::VerifyAttrInterrupt, + Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS Instruction::ExecuteTermGreaterThan, Instruction::ExecuteTermLessThan, Instruction::ExecuteTermGreaterThanOrEqual, From 2776beb842906c666f7b9eef0023cae4a0086cff Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 16 Oct 2023 14:59:06 +0200 Subject: [PATCH 63/66] Use std::sync::RwLock instead of tokio::sync::RwLock (by @aarroyoc) --- src/arena.rs | 9 +++++---- src/atom_table.rs | 8 ++++---- src/rcu.rs | 11 +++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index 20763b8c5..77d82a360 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -10,7 +10,6 @@ use crate::read::*; use crate::parser::dashu::{Integer, Rational}; use ordered_float::OrderedFloat; -use tokio::sync::RwLock; use std::alloc; use std::cell::UnsafeCell; @@ -20,6 +19,7 @@ use std::mem; use std::net::TcpListener; use std::ops::{Deref, DerefMut}; use std::ptr; +use std::sync::RwLock; #[macro_export] macro_rules! arena_alloc { @@ -90,7 +90,8 @@ pub fn lookup_float( offset: F64Offset, ) -> RcuRef, UnsafeCell>> { let f64table = global_f64table() - .blocking_read() + .read() + .unwrap() .upgrade() .expect("We should only be looking up floats while there is a float table"); @@ -108,12 +109,12 @@ pub fn lookup_float( impl F64Table { #[inline] pub fn new() -> Arc { - let upgraded = global_f64table().blocking_read().upgrade(); + let upgraded = global_f64table().read().unwrap().upgrade(); // don't inline upgraded, otherwise temporary will be dropped too late in case of None if let Some(atom_table) = upgraded { atom_table } else { - let mut guard = global_f64table().blocking_write(); + let mut guard = global_f64table().write().unwrap(); // try to upgrade again in case we lost the race on the write lock if let Some(atom_table) = guard.upgrade() { atom_table diff --git a/src/atom_table.rs b/src/atom_table.rs index 80ba54114..db38f642e 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -12,12 +12,12 @@ use std::slice; use std::str; use std::sync::Arc; use std::sync::Mutex; +use std::sync::RwLock; use std::sync::Weak; use indexmap::IndexSet; use modular_bitfield::prelude::*; -use tokio::sync::RwLock; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Atom { @@ -74,7 +74,7 @@ fn global_atom_table() -> &'static RwLock> { #[inline(always)] fn arc_atom_table() -> Option> { - global_atom_table().blocking_read().upgrade() + global_atom_table().read().unwrap().upgrade() } impl RawBlockTraits for AtomTable { @@ -310,12 +310,12 @@ impl InnerAtomTable { impl AtomTable { #[inline] pub fn new() -> Arc { - let upgraded = global_atom_table().blocking_read().upgrade(); + let upgraded = global_atom_table().read().unwrap().upgrade(); // don't inline upgraded, otherwise temporary will be dropped too late in case of None if let Some(atom_table) = upgraded { atom_table } else { - let mut guard = global_atom_table().blocking_write(); + let mut guard = global_atom_table().write().unwrap(); // try to upgrade again in case we lost the race on the write lock if let Some(atom_table) = guard.upgrade() { atom_table diff --git a/src/rcu.rs b/src/rcu.rs index a554208e3..7631caceb 100644 --- a/src/rcu.rs +++ b/src/rcu.rs @@ -6,19 +6,17 @@ use std::{ ptr::NonNull, sync::{ atomic::{AtomicPtr, AtomicU8}, - Arc, Weak, + Arc, Weak, RwLock }, }; -use tokio::sync::RwLock; - // the epoch counters of all threads that have ever accessed an Rcu // threads that have finished will have a dangling Weak reference and can be cleand up // having this be shared between all Rcu's is a tradeof, // writes will be slower as more epoch counters need to be waited for // reads should be faster as a thread only needs to register itself once on the first read // -static EPOCH_COUNTERS: RwLock>> = RwLock::const_new(Vec::new()); +static EPOCH_COUNTERS: RwLock>> = RwLock::new(Vec::new()); thread_local! { // odd value means the current thread is about to access the active_epoch of an Rcu @@ -53,7 +51,8 @@ impl Rcu { let epoch_counter = Arc::new(AtomicU8::new(0)); // register the current threads epoch counter on init EPOCH_COUNTERS - .blocking_write() + .write() + .unwrap() .push(Arc::downgrade(&epoch_counter)); epoch_counter }); @@ -113,7 +112,7 @@ impl Rcu { // - the Rcu itself holds one strong count let arc = unsafe { ManuallyDrop::new(Arc::from_raw(arc_ptr)) }; - let epochs = EPOCH_COUNTERS.blocking_read().clone(); + let epochs = EPOCH_COUNTERS.read().unwrap().clone(); let mut epochs = epochs .into_iter() .flat_map(|elem| { From 0c9740fe52319aa4d593eccea0cf0bc690203e0d Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 17 Oct 2023 22:07:38 +0200 Subject: [PATCH 64/66] Fix wasm warnings --- src/lib.rs | 1 - src/machine/streams.rs | 4 +++- src/machine/system_calls.rs | 12 +++++++++--- src/read.rs | 9 ++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 87a0f8ade..3579fcf23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,6 @@ use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub fn eval_code(s: &str) -> String { - use web_sys::console; use machine::mock_wam::*; let mut wam = Machine::with_test_streams(); diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 1dfbfaa91..4f9550506 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -21,7 +21,9 @@ use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::hash::Hash; use std::io; -use std::io::{BufRead, Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; +use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; +#[cfg(feature = "http")] +use std::io::BufRead; use std::mem; use std::net::{Shutdown, TcpStream}; use std::ops::{Deref, DerefMut}; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 17a86e982..61df25e7f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -51,14 +51,20 @@ use std::env; use std::ffi::CString; use std::fs; use std::hash::{BuildHasher, BuildHasherDefault}; -use std::io::{ErrorKind, Read, BufRead, Write}; +use std::io::{ErrorKind, Read, Write}; +#[cfg(feature = "http")] +use std::io::BufRead; use std::iter::{once, FromIterator}; use std::mem; -use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs}; +use std::net::{TcpListener, TcpStream}; +#[cfg(feature = "http")] +use std::net::{SocketAddr, ToSocketAddrs}; use std::num::NonZeroU32; use std::ops::Sub; use std::process; +#[cfg(feature = "http")] use std::str::FromStr; +#[cfg(feature = "http")] use std::sync::{Mutex, Arc, Condvar}; use chrono::{offset::Local, DateTime}; @@ -100,7 +106,7 @@ use warp::hyper::{HeaderMap, Method}; use warp::{Buf, Filter}; #[cfg(feature = "http")] use reqwest::Url; -//use hyper_util::rt::TokioIo; +#[cfg(feature = "http")] use futures::future; #[cfg(feature = "repl")] diff --git a/src/read.rs b/src/read.rs index 1baf8720e..4955c6700 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,7 +24,9 @@ use rustyline::history::DefaultHistory; use rustyline::{Config, Editor}; use std::collections::VecDeque; -use std::io::{Cursor, Error, ErrorKind, Read}; +use std::io::{Cursor, Read}; +#[cfg(feature = "repl")] +use std::io::{Error, ErrorKind}; use std::sync::Arc; type SubtermDeque = VecDeque<(usize, usize)>; @@ -83,6 +85,7 @@ impl MachineState { } static mut PROMPT: bool = false; +#[cfg(feature = "repl")] const HISTORY_FILE: &'static str = ".scryer_history"; pub(crate) fn set_prompt(value: bool) { @@ -91,6 +94,7 @@ pub(crate) fn set_prompt(value: bool) { } } +#[cfg(feature = "repl")] #[inline] fn get_prompt() -> &'static str { unsafe { @@ -107,6 +111,7 @@ pub struct ReadlineStream { #[cfg(feature = "repl")] rl: Editor, pending_input: CharReader>, + #[allow(dead_code)] add_history: bool, } @@ -145,6 +150,7 @@ impl ReadlineStream { } } + #[allow(unused_variables)] pub fn set_atoms_for_completion(&mut self, atoms: &Arc) { #[cfg(feature = "repl")] { @@ -215,6 +221,7 @@ impl ReadlineStream { } } + #[allow(dead_code)] #[cfg(not(feature = "repl"))] fn save_history(&mut self) {} From 69a0725c300f58f5cf03c26d12f31eab153b5937 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 17 Oct 2023 22:09:34 +0200 Subject: [PATCH 65/66] Cleanup Cargo.toml --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f60f4bcd..6366610a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,8 +79,6 @@ crossterm = { version = "0.20.0", optional = true } ctrlc = { version = "3.2.2", optional = true } rustyline = { version = "12.0.0", optional = true } native-tls = { version = "0.2.4", optional = true } -#hyper = { version = "=1.0.0-rc.4", features = ["full"], optional = true } -#hyper-util = { git = "https://github.com/hyperium/hyper-util.git" } warp = { version = "=0.3.5", features = ["tls"], optional = true } reqwest = { version = "0.11.18", features = ["blocking"], optional = true } tokio = { version = "1.28.2", features = ["full"] } From 59264c0aa57de442fff16a82351ab4f5b8e6115d Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Thu, 2 Nov 2023 19:07:55 +0100 Subject: [PATCH 66/66] =?UTF-8?q?Check=20for=20target=5Farch=20=3D=20?= =?UTF-8?q?=E2=80=9Cwasm32=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit instead of target_os = “wasi” --- src/bin/scryer-prolog.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index f138024b4..aa507cfd2 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -9,13 +9,13 @@ fn main() -> std::process::ExitCode { }) .unwrap(); - #[cfg(target_os = "wasi")] + #[cfg(target_arch = "wasm32")] let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap(); - #[cfg(not(target_os = "wasi"))] + #[cfg(not(target_arch = "wasm32"))] let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()