WIP: refactor to generalize Machine::run_top_level()
This commit is contained in:
@@ -6,6 +6,6 @@ fn main() {
|
|||||||
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
let mut wam = machine::Machine::new();
|
let mut wam = machine::Machine::new(Default::default());
|
||||||
wam.run_top_level();
|
wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1));
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/lib_toplevel.pl
Normal file
62
src/lib_toplevel.pl
Normal file
@@ -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.
|
||||||
32
src/machine/config.rs
Normal file
32
src/machine/config.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/machine/lib_machine.rs
Normal file
71
src/machine/lib_machine.rs
Normal file
@@ -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::<Vec<QueryResultLine>>()
|
||||||
|
.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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -215,104 +215,7 @@ pub(crate) fn parse_and_write_parsed_term_to_heap(
|
|||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub fn with_test_streams() -> Self {
|
pub fn with_test_streams() -> Self {
|
||||||
use ref_thread_local::RefThreadLocal;
|
Machine::new(MachineConfig::in_memory())
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_load_file(&mut self, file: &str) -> Vec<u8> {
|
pub fn test_load_file(&mut self, file: &str) -> Vec<u8> {
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ pub mod code_walker;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod compile;
|
pub mod compile;
|
||||||
|
pub mod config;
|
||||||
pub mod copier;
|
pub mod copier;
|
||||||
pub mod dispatch;
|
pub mod dispatch;
|
||||||
pub mod gc;
|
pub mod gc;
|
||||||
pub mod heap;
|
pub mod heap;
|
||||||
|
pub mod lib_machine;
|
||||||
pub mod load_state;
|
pub mod load_state;
|
||||||
pub mod machine_errors;
|
pub mod machine_errors;
|
||||||
pub mod machine_indices;
|
pub mod machine_indices;
|
||||||
@@ -53,6 +55,7 @@ use std::path::PathBuf;
|
|||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
use self::config::MachineConfig;
|
||||||
use self::parsed_results::*;
|
use self::parsed_results::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -221,23 +224,18 @@ impl Machine {
|
|||||||
|
|
||||||
pub fn load_file(&mut self, path: &str, stream: Stream) {
|
pub fn load_file(&mut self, path: &str, stream: Stream) {
|
||||||
self.machine_st.registers[1] = stream_as_cell!(stream);
|
self.machine_st.registers[1] = stream_as_cell!(stream);
|
||||||
self.machine_st.registers[2] = atom_as_cell!(
|
self.machine_st.registers[2] = atom_as_cell!(self.machine_st.atom_tbl.build_with(path));
|
||||||
self.machine_st.atom_tbl.build_with(path)
|
|
||||||
);
|
|
||||||
|
|
||||||
self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2));
|
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();
|
let mut path_buf = current_dir();
|
||||||
|
|
||||||
path_buf.push("src/toplevel.pl");
|
path_buf.push("src/toplevel.pl");
|
||||||
|
|
||||||
let path = path_buf.to_str().unwrap();
|
let path = path_buf.to_str().unwrap();
|
||||||
let toplevel_stream = Stream::from_static_string(
|
let toplevel_stream = Stream::from_static_string(program, &mut self.machine_st.arena);
|
||||||
include_str!("../toplevel.pl"),
|
|
||||||
&mut self.machine_st.arena,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.load_file(path, toplevel_stream);
|
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![];
|
let mut arg_pstrs = vec![];
|
||||||
|
|
||||||
for arg in env::args() {
|
for arg in env::args() {
|
||||||
@@ -303,15 +301,12 @@ impl Machine {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.machine_st.registers[1] = heap_loc_as_cell!(
|
self.machine_st.registers[1] = heap_loc_as_cell!(iter_to_heap_list(
|
||||||
iter_to_heap_list(&mut self.machine_st.heap, arg_pstrs.into_iter())
|
&mut self.machine_st.heap,
|
||||||
);
|
arg_pstrs.into_iter()
|
||||||
|
));
|
||||||
|
|
||||||
self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1));
|
self.run_module_predicate(module_name, key);
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
pub fn set_user_input(&mut self, input: String) {
|
||||||
@@ -328,24 +323,6 @@ impl Machine {
|
|||||||
self.load_file(module_name, stream);
|
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::<Vec<QueryResultLine>>()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn configure_modules(&mut self) {
|
pub(crate) fn configure_modules(&mut self) {
|
||||||
fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir, arena: &mut Arena) {
|
fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir, arena: &mut Arena) {
|
||||||
for arity in 1..66 {
|
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;
|
use ref_thread_local::RefThreadLocal;
|
||||||
|
|
||||||
let args = MachineArgs::new();
|
let args = MachineArgs::new();
|
||||||
let mut machine_st = MachineState::new();
|
let mut machine_st = MachineState::new();
|
||||||
|
|
||||||
let user_input = Stream::stdin(&mut machine_st.arena, args.add_history);
|
let (user_input, user_output, user_error) = match config.streams {
|
||||||
let user_output = Stream::stdout(&mut machine_st.arena);
|
config::StreamConfig::Stdio => (
|
||||||
let user_error = Stream::stderr(&mut machine_st.arena);
|
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()
|
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut wam = Machine {
|
let mut wam = Machine {
|
||||||
machine_st,
|
machine_st,
|
||||||
@@ -555,7 +540,7 @@ impl Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wam.load_special_forms();
|
wam.load_special_forms();
|
||||||
wam.load_top_level();
|
wam.load_top_level(config.toplevel);
|
||||||
wam.configure_streams();
|
wam.configure_streams();
|
||||||
|
|
||||||
wam
|
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -429,28 +429,4 @@ print_exception_with_check(E) :-
|
|||||||
% number, a GNU-style error message
|
% number, a GNU-style error message
|
||||||
% is expected to be printed instead.
|
% is expected to be printed instead.
|
||||||
; print_exception(E)
|
; 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.
|
|
||||||
Reference in New Issue
Block a user