From 5f8cc3c64b6669791361403d674b07b10657e9cb Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Mon, 17 Jul 2023 21:34:26 +0200 Subject: [PATCH] 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 eae00fe2..a7519def 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 00000000..7987a2ff --- /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 00000000..6268c8d8 --- /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 00000000..7b749561 --- /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 2c16ba3a..77011b32 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 c45d7cb4..6a319597 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 93b1a60d..5a36aa77 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -429,28 +429,4 @@ print_exception_with_check(E) :- % 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 -- 2.54.0