use prolog_parser::ast::*; use prolog_parser::tabled_rc::*; use prolog_parser::{clause_name, temp_v}; use lazy_static::lazy_static; use crate::clause_types::*; use crate::forms::*; use crate::instructions::*; use crate::machine::heap::*; use crate::machine::loader::*; use crate::machine::term_stream::{LiveTermStream, LoadStatePayload, TermStream}; use crate::read::*; mod attributed_variables; pub(super) mod code_repo; pub mod code_walker; #[macro_use] pub(crate) mod loader; mod compile; mod copier; pub mod heap; mod load_state; pub mod machine_errors; pub mod machine_indices; pub(super) mod machine_state; pub mod partial_string; mod preprocessor; mod raw_block; mod stack; pub(crate) mod streams; mod term_stream; #[macro_use] mod arithmetic_ops; #[macro_use] mod machine_state_impl; mod system_calls; use crate::machine::code_repo::*; use crate::machine::compile::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::streams::*; use indexmap::IndexMap; //use std::convert::TryFrom; use prolog_parser::ast::ClauseName; use std::fs::File; use std::mem; use std::path::PathBuf; use std::sync::atomic::AtomicBool; #[derive(Debug)] pub struct MachinePolicies { call_policy: Box, cut_policy: Box, } lazy_static! { pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false); } impl MachinePolicies { #[inline] fn new() -> Self { MachinePolicies { call_policy: Box::new(DefaultCallPolicy {}), cut_policy: Box::new(DefaultCutPolicy {}), } } } impl Default for MachinePolicies { #[inline] fn default() -> Self { MachinePolicies::new() } } #[derive(Debug)] pub(super) struct LoadContext { pub(super) path: PathBuf, pub(super) stream: Stream, pub(super) module: ClauseName, } impl LoadContext { #[inline] fn new(path: &str, stream: Stream) -> Self { LoadContext { path: PathBuf::from(path), stream, module: clause_name!("user"), } } } #[derive(Debug)] pub struct Machine { pub(super) machine_st: MachineState, pub(super) inner_heap: Heap, pub(super) policies: MachinePolicies, pub(super) indices: IndexStore, pub(super) code_repo: CodeRepo, pub(super) user_input: Stream, pub(super) user_output: Stream, pub(super) load_contexts: Vec, } #[inline] fn current_dir() -> std::path::PathBuf { let mut path_buf = std::path::PathBuf::from(file!()); path_buf.pop(); path_buf } include!(concat!(env!("OUT_DIR"), "/libraries.rs")); impl Machine { /* fn compile_scryerrc(&mut self) { let mut path = match dirs_next::home_dir() { Some(path) => path, None => return, }; path.push(".scryerrc"); if path.is_file() { let file_src = match File::open(&path) { Ok(file_handle) => Stream::from_file_as_input( clause_name!(".scryerrc"), file_handle, ), Err(_) => return, }; let rc_src = ListingSource::from_file_and_path( clause_name!(".scryerrc"), path.to_path_buf(), ); compile_user_module(self, file_src, rc_src); } } */ fn run_module_predicate(&mut self, module_name: ClauseName, key: PredicateKey) { if let Some(module) = self.indices.modules.get(&module_name) { if let Some(ref code_index) = module.code_dir.get(&key) { let p = code_index.local().unwrap(); self.machine_st.cp = LocalCodePtr::Halt; self.machine_st.p = CodePtr::Local(LocalCodePtr::DirEntry(p)); return self.run_query(); } } unreachable!(); } fn load_file(&mut self, path: String, stream: Stream) { self.machine_st[temp_v!(1)] = Addr::Stream(self.machine_st.heap.push(HeapCellValue::Stream(stream))); self.machine_st[temp_v!(2)] = Addr::Con(self.machine_st.heap.push(HeapCellValue::Atom( clause_name!(path, self.machine_st.atom_tbl), None, ))); self.run_module_predicate(clause_name!("loader"), (clause_name!("file_load"), 2)); } fn load_top_level(&mut self) { let mut path_buf = current_dir(); path_buf.push("toplevel.pl"); let path = path_buf.to_str().unwrap().to_string(); self.load_file(path, Stream::from(include_str!("../toplevel.pl"))); if let Some(toplevel) = self.indices.modules.get(&clause_name!("$toplevel")) { load_module( &mut self.indices.code_dir, &mut self.indices.op_dir, &mut self.indices.meta_predicates, &CompilationTarget::User, toplevel, ); } else { unreachable!() } } fn load_special_forms(&mut self) { let mut path_buf = current_dir(); path_buf.push("machine/attributed_variables.pl"); bootstrapping_compile( Stream::from(include_str!("attributed_variables.pl")), self, ListingSource::from_file_and_path(clause_name!("attributed_variables"), path_buf), ) .unwrap(); let mut path_buf = current_dir(); path_buf.push("machine/project_attributes.pl"); bootstrapping_compile( Stream::from(include_str!("project_attributes.pl")), self, ListingSource::from_file_and_path(clause_name!("project_attributes"), path_buf), ) .unwrap(); if let Some(module) = self.indices.modules.get(&clause_name!("$atts")) { if let Some(code_index) = module.code_dir.get(&(clause_name!("driver"), 2)) { self.machine_st.attr_var_init.verify_attrs_loc = code_index.local().unwrap(); } } if let Some(module) = self.indices.modules.get(&clause_name!("$project_atts")) { if let Some(code_index) = module.code_dir.get(&(clause_name!("driver"), 2)) { self.machine_st.attr_var_init.project_attrs_loc = code_index.local().unwrap(); } } } pub fn run_top_level(&mut self) { use std::env; let mut arg_pstrs = vec![]; for arg in env::args() { arg_pstrs.push(self.machine_st.heap.put_complete_string(&arg)); } let list_addr = Addr::HeapCell(self.machine_st.heap.to_list(arg_pstrs.into_iter())); self.machine_st[temp_v!(1)] = list_addr; self.run_module_predicate(clause_name!("$toplevel"), (clause_name!("$repl"), 1)); } fn configure_modules(&mut self) { fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir) { for arity in 1..66 { let key = (clause_name!("call"), arity); match loader.code_dir.get(&key) { Some(src_code_index) => { let target_code_index = target_code_dir .entry(key.clone()) .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined)); target_code_index.set(src_code_index.get()); } None => { unreachable!(); } } } } if let Some(loader) = self.indices.modules.swap_remove(&clause_name!("loader")) { if let Some(builtins) = self.indices.modules.get_mut(&clause_name!("builtins")) { // Import loader's exports into the builtins module so they will be // implicitly included in every further module. load_module( &mut builtins.code_dir, &mut builtins.op_dir, &mut builtins.meta_predicates, &CompilationTarget::Module(clause_name!("builtins")), &loader, ); for export in &loader.module_decl.exports { builtins.module_decl.exports.push(export.clone()); } for arity in 10..66 { builtins .module_decl .exports .push(ModuleExport::PredicateKey((clause_name!("call"), arity))); } } for (_, target_module) in self.indices.modules.iter_mut() { update_call_n_indices(&loader, &mut target_module.code_dir); } update_call_n_indices(&loader, &mut self.indices.code_dir); self.indices.modules.insert(clause_name!("loader"), loader); } else { unreachable!() } } pub fn new(user_input: Stream, user_output: Stream) -> Self { use ref_thread_local::RefThreadLocal; let mut wam = Machine { machine_st: MachineState::new(), inner_heap: Heap::new(), policies: MachinePolicies::new(), indices: IndexStore::new(), code_repo: CodeRepo::new(), user_input, user_output, load_contexts: vec![], }; let mut lib_path = current_dir(); lib_path.pop(); lib_path.push("lib"); bootstrapping_compile( Stream::from(LIBRARIES.borrow()["ops_and_meta_predicates"]), &mut wam, ListingSource::from_file_and_path( clause_name!("ops_and_meta_predicates.pl"), lib_path.clone(), ), ) .unwrap(); bootstrapping_compile( Stream::from(LIBRARIES.borrow()["builtins"]), &mut wam, ListingSource::from_file_and_path(clause_name!("builtins.pl"), lib_path.clone()), ) .unwrap(); if let Some(builtins) = wam.indices.modules.get(&clause_name!("builtins")) { load_module( &mut wam.indices.code_dir, &mut wam.indices.op_dir, &mut wam.indices.meta_predicates, &CompilationTarget::User, builtins, ); } else { unreachable!() } lib_path.pop(); // remove the "lib" at the end bootstrapping_compile( Stream::from(include_str!("../loader.pl")), &mut wam, ListingSource::from_file_and_path(clause_name!("loader.pl"), lib_path.clone()), ) .unwrap(); wam.configure_modules(); if let Some(loader) = wam.indices.modules.get(&clause_name!("loader")) { load_module( &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 configure_streams(&mut self) { self.user_input.options.alias = Some(clause_name!("user_input")); self.indices .stream_aliases .insert(clause_name!("user_input"), self.user_input.clone()); self.indices.streams.insert(self.user_input.clone()); self.user_output.options.alias = Some(clause_name!("user_output")); self.indices .stream_aliases .insert(clause_name!("user_output"), self.user_output.clone()); self.indices.streams.insert(self.user_output.clone()); } fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) { let h = self.machine_st.heap.h(); let err = MachineError::session_error(h, err); let stub = MachineError::functor_stub(key.0, key.1); let err = self.machine_st.error_form(err, stub); self.machine_st.throw_exception(err); return; } fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) { match code_ptr { REPLCodePtr::AddDynamicPredicate => { self.add_dynamic_predicate(); } REPLCodePtr::AddGoalExpansionClause => { self.add_goal_expansion_clause(); } REPLCodePtr::AddTermExpansionClause => { self.add_term_expansion_clause(); } REPLCodePtr::ClauseToEvacuable => { self.clause_to_evacuable(); } REPLCodePtr::ScopedClauseToEvacuable => { self.scoped_clause_to_evacuable(); } REPLCodePtr::ConcludeLoad => { self.conclude_load(); } REPLCodePtr::PopLoadContext => { self.pop_load_context(); } REPLCodePtr::PushLoadContext => { self.push_load_context(); } REPLCodePtr::PopLoadStatePayload => { self.pop_load_state_payload(); } REPLCodePtr::UseModule => { self.use_module(); } REPLCodePtr::LoadCompiledLibrary => { self.load_compiled_library(); } REPLCodePtr::DeclareModule => { self.declare_module(); } REPLCodePtr::PushLoadStatePayload => { self.push_load_state_payload(); } REPLCodePtr::LoadContextSource => { self.load_context_source(); } REPLCodePtr::LoadContextFile => { self.load_context_file(); } REPLCodePtr::LoadContextDirectory => { self.load_context_directory(); } REPLCodePtr::LoadContextModule => { self.load_context_module(); } REPLCodePtr::LoadContextStream => { self.load_context_stream(); } REPLCodePtr::MetaPredicateProperty => { self.meta_predicate_property(); } REPLCodePtr::BuiltInProperty => { self.builtin_property(); } REPLCodePtr::MultifileProperty => { self.multifile_property(); } REPLCodePtr::DiscontiguousProperty => { self.discontiguous_property(); } REPLCodePtr::DynamicProperty => { self.dynamic_property(); } REPLCodePtr::Assertz => { self.compile_assert(AppendOrPrepend::Append); } REPLCodePtr::Asserta => { self.compile_assert(AppendOrPrepend::Prepend); } REPLCodePtr::Retract => { self.retract_clause(); } REPLCodePtr::AbolishClause => { self.abolish_clause(); } } self.machine_st.p = CodePtr::Local(p); } pub(super) fn run_query(&mut self) { while !self.machine_st.p.is_halt() { self.machine_st.query_stepper( &mut self.indices, &mut self.policies, &mut self.code_repo, &mut self.user_input, &mut self.user_output, ); match self.machine_st.p { CodePtr::REPL(code_ptr, p) => { self.handle_toplevel_command(code_ptr, p); if self.machine_st.fail { self.machine_st.backtrack(); } } _ => { break; } }; } } } impl MachineState { fn dispatch_instr( &mut self, instr: &Line, indices: &mut IndexStore, policies: &mut MachinePolicies, code_repo: &CodeRepo, user_input: &mut Stream, user_output: &mut Stream, ) { match instr { &Line::Arithmetic(ref arith_instr) => self.execute_arith_instr(arith_instr), &Line::Choice(ref choice_instr) => { self.execute_choice_instr(choice_instr, &mut policies.call_policy) } &Line::Cut(ref cut_instr) => { self.execute_cut_instr(cut_instr, &mut policies.cut_policy) } &Line::Control(ref control_instr) => self.execute_ctrl_instr( indices, code_repo, &mut policies.call_policy, &mut policies.cut_policy, user_input, user_output, control_instr, ), &Line::Fact(ref fact_instr) => { self.execute_fact_instr(&fact_instr); self.p += 1; } &Line::IndexingCode(ref indexing_lines) => { self.execute_indexing_instr(indexing_lines, &mut policies.call_policy) } &Line::IndexedChoice(ref choice_instr) => { self.execute_indexed_choice_instr(choice_instr, &mut policies.call_policy) } &Line::Query(ref query_instr) => { self.execute_query_instr(&query_instr); self.p += 1; } } } fn execute_instr( &mut self, indices: &mut IndexStore, policies: &mut MachinePolicies, code_repo: &CodeRepo, user_input: &mut Stream, user_output: &mut Stream, ) { let instr = match code_repo.lookup_instr(self.last_call, &self.p) { Some(instr) => instr, None => return, }; self.dispatch_instr( instr.as_ref(), indices, policies, code_repo, user_input, user_output, ); } fn backtrack(&mut self) { let b = self.b; self.b0 = self.stack.index_or_frame(b).prelude.b0; self.p = CodePtr::Local(self.stack.index_or_frame(b).prelude.bp); self.fail = false; } fn check_machine_index(&mut self, code_repo: &CodeRepo) -> bool { match self.p { CodePtr::Local(LocalCodePtr::DirEntry(p)) | CodePtr::Local(LocalCodePtr::IndexingBuf(p, ..)) if p < code_repo.code.len() => {} CodePtr::Local(LocalCodePtr::Halt) | CodePtr::REPL(..) => { return false; } _ => {} } true } // return true iff verify_attr_interrupt is called. fn verify_attr_stepper( &mut self, indices: &mut IndexStore, policies: &mut MachinePolicies, code_repo: &mut CodeRepo, user_input: &mut Stream, user_output: &mut Stream, ) -> bool { loop { let instr = match code_repo.lookup_instr(self.last_call, &self.p) { Some(instr) => { if instr.as_ref().is_head_instr() { instr } else { let cp = self.p.local(); self.run_verify_attr_interrupt(cp); return true; } } None => return false, }; self.dispatch_instr( instr.as_ref(), indices, policies, code_repo, user_input, user_output, ); if self.fail { self.backtrack(); } if !self.check_machine_index(code_repo) { return false; } } } fn run_verify_attr_interrupt(&mut self, cp: LocalCodePtr) { let p = self.attr_var_init.verify_attrs_loc; self.attr_var_init.cp = cp; self.verify_attr_interrupt(p); } fn query_stepper( &mut self, indices: &mut IndexStore, policies: &mut MachinePolicies, code_repo: &mut CodeRepo, user_input: &mut Stream, user_output: &mut Stream, ) { loop { self.execute_instr(indices, policies, code_repo, user_input, user_output); if self.fail { self.backtrack(); } match self.p { CodePtr::VerifyAttrInterrupt(_) => { self.p = CodePtr::Local(self.attr_var_init.cp); let instigating_p = CodePtr::Local(self.attr_var_init.instigating_p); let instigating_instr = code_repo.lookup_instr(false, &instigating_p).unwrap(); if !instigating_instr.as_ref().is_head_instr() { let cp = self.p.local(); self.run_verify_attr_interrupt(cp); } else if !self.verify_attr_stepper( indices, policies, code_repo, user_input, user_output, ) { if self.fail { break; } let cp = self.p.local(); self.run_verify_attr_interrupt(cp); } } _ => { if !self.check_machine_index(code_repo) { break; } } } } } }