Files
scryer-prolog/src/machine/loader.rs

2323 lines
84 KiB
Rust

use crate::arena::*;
use crate::atom_table::*;
use crate::clause_types::*;
use crate::forms::*;
use crate::heap_iter::*;
use crate::indexing::*;
use crate::instructions::*;
use crate::machine::load_state::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::machine::preprocessor::*;
use crate::machine::term_stream::*;
use crate::machine::*;
use crate::parser::ast::*;
use crate::types::*;
use indexmap::IndexSet;
use slice_deque::{sdeq, SliceDeque};
use std::cell::Cell;
use std::convert::TryFrom;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
/*
* The loader compiles Prolog terms read from a TermStream instance,
* which may be incremental or monolithic. The monolithic term stream
* reads from a file. It's used only to bootstrap Scryer at
* start-up. Once Scryer is bootstrapped, all compilation and loading
* work is divided between loader.pl and loader.rs.
*
* loader.pl does a few high-level things more easily handled from
* Prolog that are not supported (or needed) during bootstrapping:
* term and goal expansion, loading modules from different streams,
* verifying certain kinds of declarations, perhaps (in the future?)
* compiling inline disjunctions.
*
* Since the loader can operate incrementally, it uses an intermittent
* structure to rebuild the loader between invocations. Preprocessor
* needs access to a &'a mut Machine for as long as it lives, and we
* can't have copies of &'a mut Machine distributed among multiple
* owners.
*
* When loading a module, we modify the records of the WAM with the
* location of new predicates, with new meta-predicate information,
* new term and goal expansions, new dynamic clauses, etc. Should the
* loader fail later, all changes must be rolled back, restoring the
* WAM to its prior state. Retraction records describe individual changes
* made by the loader, and they may be used later.
*/
#[derive(Debug)]
pub(crate) enum RetractionRecord {
AddedMetaPredicate(Atom, PredicateKey),
ReplacedMetaPredicate(Atom, Atom, Vec<MetaSpec>),
AddedModule(Atom),
ReplacedModule(ModuleDecl, ListingSource, LocalExtensiblePredicates),
AddedModuleOp(Atom, OpDecl),
ReplacedModuleOp(Atom, OpDecl, OpDesc),
AddedModulePredicate(Atom, PredicateKey),
ReplacedModulePredicate(Atom, PredicateKey, IndexPtr),
AddedDiscontiguousPredicate(CompilationTarget, PredicateKey),
AddedDynamicPredicate(CompilationTarget, PredicateKey),
AddedMultifilePredicate(CompilationTarget, PredicateKey),
AddedUserOp(OpDecl),
ReplacedUserOp(OpDecl, OpDesc),
AddedExtensiblePredicate(CompilationTarget, PredicateKey),
AddedUserPredicate(PredicateKey),
ReplacedUserPredicate(PredicateKey, IndexPtr),
AddedIndex(OptArgIndexKey, usize), //, Vec<usize>),
RemovedIndex(usize, OptArgIndexKey, usize),
ReplacedChoiceOffset(usize, usize),
AppendedTrustMe(usize, usize, bool),
ReplacedSwitchOnTermVarIndex(usize, IndexingCodePtr),
ModifiedTryMeElse(usize, usize),
ModifiedRetryMeElse(usize, usize),
ModifiedRevJmpBy(usize, usize),
SkeletonClausePopBack(CompilationTarget, PredicateKey),
SkeletonClausePopFront(CompilationTarget, PredicateKey),
SkeletonLocalClauseClausePopBack(CompilationTarget, CompilationTarget, PredicateKey),
SkeletonLocalClauseClausePopFront(CompilationTarget, CompilationTarget, PredicateKey),
SkeletonLocalClauseTruncateBack(CompilationTarget, CompilationTarget, PredicateKey, usize),
SkeletonClauseTruncateBack(CompilationTarget, PredicateKey, usize),
SkeletonClauseStartReplaced(CompilationTarget, PredicateKey, usize, usize),
RemovedDynamicSkeletonClause(CompilationTarget, PredicateKey, usize, usize),
RemovedSkeletonClause(
CompilationTarget,
PredicateKey,
usize,
ClauseIndexInfo,
usize,
),
ReplacedIndexingLine(usize, Vec<IndexingLine>),
RemovedLocalSkeletonClauseLocations(
CompilationTarget,
CompilationTarget,
PredicateKey,
SliceDeque<usize>,
),
RemovedSkeleton(CompilationTarget, PredicateKey, PredicateSkeleton),
ReplacedDynamicElseOffset(usize, usize),
AppendedNextOrFail(usize, NextOrFail),
}
/*
* Retractions to be performed on rollback are represented by
* individual records, and the original extent of the code vector of
* the IndexStore, of which there are several (one per module). The
* "extent" of a code vector is its length prior to an attempted
* module load. The only code vector of the WAM's IndexStore, "code",
* is shared by all modules, including the default "user" module.
*/
pub(super) struct RetractionInfo {
orig_code_extent: usize,
records: Vec<RetractionRecord>,
}
impl RetractionInfo {
#[inline]
pub(super) fn new(orig_code_extent: usize) -> Self {
Self {
orig_code_extent,
records: vec![],
}
}
#[inline]
pub(crate) fn push_record(&mut self, record: RetractionRecord) {
self.records.push(record);
}
#[inline]
pub(crate) fn reset(&mut self, code_len: usize) -> Self {
let orig_code_extent = self.orig_code_extent;
self.orig_code_extent = code_len;
Self {
orig_code_extent,
records: mem::replace(&mut self.records, vec![]),
}
}
}
impl<'a, LS: LoadState<'a>> Drop for Loader<'a, LS> {
fn drop(&mut self) {
if LS::should_drop_load_state(self) {
LS::reset_machine(self);
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum CompilationTarget {
Module(Atom),
User,
}
impl Default for CompilationTarget {
#[inline]
fn default() -> Self {
CompilationTarget::User
}
}
impl CompilationTarget {
#[inline]
pub(crate) fn module_name(&self) -> Atom {
match self {
CompilationTarget::User => {
atom!("user")
}
CompilationTarget::Module(module_name) => *module_name,
}
}
}
pub struct PredicateQueue {
pub(super) predicates: Vec<Term>,
pub(super) compilation_target: CompilationTarget,
}
impl PredicateQueue {
#[inline]
pub(super) fn push(&mut self, clause: Term) {
self.predicates.push(clause);
}
#[inline]
pub(crate) fn first(&self) -> Option<&Term> {
self.predicates.first()
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.predicates.is_empty()
}
#[inline]
pub(super) fn take(&mut self) -> Self {
Self {
predicates: mem::replace(&mut self.predicates, vec![]),
compilation_target: self.compilation_target.clone(),
}
}
#[inline]
pub(super) fn len(&self) -> usize {
self.predicates.len()
}
}
#[macro_export]
macro_rules! predicate_queue {
[$($v:expr),*] => (
PredicateQueue {
predicates: vec![$($v,)*],
compilation_target: CompilationTarget::default(),
}
)
}
pub type LiveLoadState = LoadStatePayload<LiveTermStream>;
pub struct BootstrappingLoadState<'a>(
pub LoadStatePayload<BootstrappingTermStream<'a>>
);
impl<'a> Deref for BootstrappingLoadState<'a> {
type Target = LoadStatePayload<BootstrappingTermStream<'a>>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> DerefMut for BootstrappingLoadState<'a> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub trait LoadState<'a>: Sized {
type Evacuable;
type TS: TermStream;
type LoaderFieldType: DerefMut<Target=LoadStatePayload<Self::TS>>;
fn new(machine_st: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType;
fn evacuate(loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError>;
fn should_drop_load_state(loader: &Loader<'a, Self>) -> bool;
fn reset_machine(loader: &mut Loader<'a, Self>);
fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState;
}
pub struct LiveLoadAndMachineState<'a> {
load_state: TypedArenaPtr<LiveLoadState>,
machine_st: &'a mut MachineState,
}
impl<'a> Deref for LiveLoadAndMachineState<'a> {
type Target = LiveLoadState;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.load_state
}
}
impl<'a> DerefMut for LiveLoadAndMachineState<'a> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.load_state
}
}
impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
type TS = LiveTermStream;
type LoaderFieldType = LiveLoadAndMachineState<'a>;
type Evacuable = TypedArenaPtr<LiveLoadState>;
#[inline(always)]
fn new(machine_st: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType {
let load_state = arena_alloc!(payload, &mut machine_st.arena);
LiveLoadAndMachineState { load_state, machine_st }
}
#[inline(always)]
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
loader.payload.load_state.set_tag(ArenaHeaderTag::InactiveLoadState);
Ok(loader.payload.load_state)
}
#[inline(always)]
fn should_drop_load_state(loader: &Loader<'a, Self>) -> bool {
loader.payload.load_state.get_tag() == ArenaHeaderTag::LiveLoadState
}
#[inline(always)]
fn reset_machine(loader: &mut Loader<'a, Self>) {
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
loader.payload.load_state.set_tag(ArenaHeaderTag::Dropped);
loader.reset_machine();
}
}
#[inline(always)]
fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
loader.machine_st
}
}
impl<'a> LoadState<'a> for BootstrappingLoadState<'a> {
type TS = BootstrappingTermStream<'a>;
type LoaderFieldType = BootstrappingLoadState<'a>;
type Evacuable = CompilationTarget;
#[inline(always)]
fn new(_: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType {
BootstrappingLoadState(payload)
}
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
if !loader.payload.predicates.is_empty() {
loader.compile_and_submit()?;
}
let repo_len = loader.wam_prelude.code_repo.code.len();
loader
.payload
.retraction_info
.reset(repo_len);
loader.remove_module_op_exports();
Ok(loader.payload.compilation_target)
}
#[inline(always)]
fn should_drop_load_state(_loader: &Loader<'a, Self>) -> bool {
true
}
#[inline(always)]
fn reset_machine(loader: &mut Loader<'a, Self>) {
loader.reset_machine();
}
#[inline(always)]
fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
&mut loader.term_stream.parser.lexer.machine_st
}
}
pub struct Loader<'a, LS: LoadState<'a>> {
pub(super) payload: LS::LoaderFieldType,
pub(super) wam_prelude: MachinePreludeView<'a>,
}
impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
#[inline]
pub(super) fn new(wam: &'a mut Machine, term_stream: <LS as LoadState<'a>>::TS) -> Self {
let payload = LoadStatePayload::new(wam.code_repo.code.len(), term_stream);
let (wam_prelude, machine_st) = wam.prelude_view_and_machine_st();
Self {
payload: LS::new(machine_st, payload),
wam_prelude,
}
}
pub(crate) fn load(mut self) -> Result<LS::Evacuable, SessionError> {
while let Some(decl) = self.dequeue_terms()? {
self.load_decl(decl)?;
}
LS::evacuate(self)
}
fn dequeue_terms(&mut self) -> Result<Option<Declaration>, SessionError> {
while !self.payload.term_stream.eof()? {
let load_state = &mut self.payload;
let compilation_target = &load_state.compilation_target;
let composite_op_dir = self.wam_prelude.composite_op_dir(compilation_target);
let term = load_state.term_stream.next(&composite_op_dir)?;
if !term.is_consistent(&load_state.predicates) {
self.compile_and_submit()?;
}
let term = match term {
Term::Clause(_, name, terms) if name == atom!(":-") && terms.len() == 1 => {
return Ok(Some(setup_declaration(self, terms)?));
}
term => term,
};
self.payload.predicates.push(term);
}
Ok(None)
}
pub(super) fn load_decl(&mut self, decl: Declaration) -> Result<(), SessionError> {
match decl {
Declaration::Dynamic(name, arity) => {
let compilation_target = self.payload.compilation_target;
self.add_dynamic_predicate(compilation_target, name, arity)?;
}
Declaration::MetaPredicate(module_name, name, meta_specs) => {
self.add_meta_predicate_record(module_name, name, meta_specs);
}
Declaration::Module(module_decl) => {
self.payload.compilation_target = CompilationTarget::Module(module_decl.name);
self.payload.predicates.compilation_target = self.payload.compilation_target;
let listing_src = self.payload.term_stream.listing_src().clone();
self.add_module(module_decl, listing_src);
}
Declaration::NonCountedBacktracking(name, arity) => {
self.payload.non_counted_bt_preds.insert((name, arity));
}
Declaration::Op(op_decl) => {
self.add_op_decl(&op_decl);
}
Declaration::UseModule(module_src) => {
self.use_module(module_src)?;
}
Declaration::UseQualifiedModule(module_src, exports) => {
self.use_qualified_module(module_src, exports)?;
}
}
Ok(())
}
pub(super) fn read_term_from_heap(&mut self, heap_term_loc: RegType) -> Result<Term, SessionError> {
let machine_st = LS::machine_st(&mut self.payload);
let term_addr = machine_st[heap_term_loc];
let mut term_stack = vec![];
let mut iter = stackful_post_order_iter(&mut machine_st.heap, term_addr);
while let Some(addr) = iter.next() {
read_heap_cell!(addr,
(HeapCellValueTag::Lis) => {
let tail = term_stack.pop().unwrap();
let head = term_stack.pop().unwrap();
term_stack.push(Term::Cons(Cell::default(), Box::new(head), Box::new(tail)));
}
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => {
let offset_string = format!("_{}", h);
term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string)));
}
(HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
HeapCellValueTag::Char | HeapCellValueTag::F64) => {
let addr = unmark_cell_bits!(addr);
term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
}
(HeapCellValueTag::Atom, (name, arity)) => {
if arity == 0 {
term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name)));
} else {
let subterms = term_stack
.drain(term_stack.len() - arity ..)
.collect();
term_stack.push(Term::Clause(Cell::default(), name, subterms));
}
}
(HeapCellValueTag::PStr, string) => {
let tail = term_stack.pop().unwrap();
if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail {
term_stack.push(Term::PartialString(
Cell::default(),
string,
None,
));
} else {
term_stack.push(Term::PartialString(
Cell::default(),
string,
Some(Box::new(tail)),
));
}
}
(HeapCellValueTag::PStrOffset, h) => {
let string = cell_as_atom_cell!(iter.heap[h]).get_name();
let tail = term_stack.pop().unwrap();
term_stack.push(Term::PartialString(
Cell::default(),
string,
Some(Box::new(tail)),
));
}
_ => {
}
);
}
debug_assert!(term_stack.len() == 1);
Ok(term_stack.pop().unwrap())
}
fn reset_machine(&mut self) {
while let Some(record) = self.payload.retraction_info.records.pop() {
match record {
RetractionRecord::AddedMetaPredicate(target_module_name, key) => {
match target_module_name {
atom!("user") => {
self.wam_prelude.indices.meta_predicates.remove(&key);
}
_ => match self.wam_prelude.indices.modules.get_mut(&target_module_name) {
Some(ref mut module) => {
module.meta_predicates.remove(&key);
}
_ => {
unreachable!()
}
},
}
}
RetractionRecord::ReplacedMetaPredicate(target_module_name, name, meta_specs) => {
match target_module_name {
atom!("user") => {
self.wam_prelude
.indices
.meta_predicates
.insert((name, meta_specs.len()), meta_specs);
}
_ => match self.wam_prelude.indices.modules.get_mut(&target_module_name) {
Some(ref mut module) => {
module
.meta_predicates
.insert((name, meta_specs.len()), meta_specs);
}
_ => {
unreachable!()
}
},
}
}
RetractionRecord::AddedModule(module_name) => {
self.wam_prelude.indices.modules.remove(&module_name);
}
RetractionRecord::ReplacedModule(
module_decl,
listing_src,
local_extensible_predicates,
) => match self.wam_prelude.indices.modules.get_mut(&module_decl.name) {
Some(ref mut module) => {
module.module_decl = module_decl;
module.listing_src = listing_src;
module.local_extensible_predicates = local_extensible_predicates;
}
_ => {
unreachable!()
}
},
RetractionRecord::AddedDiscontiguousPredicate(compilation_target, key) => {
match compilation_target {
CompilationTarget::User => {
self.wam_prelude
.indices
.extensible_predicates
.get_mut(&key)
.map(|skeleton| {
skeleton.core.is_discontiguous = false;
});
}
CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
module.extensible_predicates.get_mut(&key).map(|skeleton| {
skeleton.core.is_discontiguous = false;
});
}
None => {}
}
}
}
}
RetractionRecord::AddedDynamicPredicate(compilation_target, key) => {
match compilation_target {
CompilationTarget::User => {
self.wam_prelude
.indices
.extensible_predicates
.get_mut(&key)
.map(|skeleton| {
skeleton.core.is_dynamic = false;
});
}
CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
module.extensible_predicates.get_mut(&key).map(|skeleton| {
skeleton.core.is_dynamic = false;
skeleton.core.retracted_dynamic_clauses = None;
});
}
None => {}
}
}
}
}
RetractionRecord::AddedMultifilePredicate(compilation_target, key) => {
match compilation_target {
CompilationTarget::User => {
self.wam_prelude
.indices
.extensible_predicates
.get_mut(&key)
.map(|skeleton| {
skeleton.core.is_multifile = false;
});
}
CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
module.extensible_predicates.get_mut(&key).map(|skeleton| {
skeleton.core.is_multifile = false;
});
}
None => {}
}
}
}
}
RetractionRecord::AddedModuleOp(module_name, mut op_decl) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
op_decl.remove(&mut module.op_dir);
}
None => {}
}
}
RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, op_desc) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
op_decl.op_desc = op_desc;
op_decl.insert_into_op_dir(&mut module.op_dir);
}
None => {}
}
}
RetractionRecord::AddedModulePredicate(module_name, key) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
module.code_dir.remove(&key);
}
None => {}
}
}
RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => {
module
.code_dir
.get_mut(&key)
.map(|code_idx| code_idx.replace(old_code_idx));
}
None => {}
}
}
RetractionRecord::AddedExtensiblePredicate(compilation_target, key) => {
self.wam_prelude
.indices
.remove_predicate_skeleton(&compilation_target, &key);
}
RetractionRecord::AddedUserOp(mut op_decl) => {
op_decl.remove(&mut self.wam_prelude.indices.op_dir);
}
RetractionRecord::ReplacedUserOp(mut op_decl, op_desc) => {
op_decl.op_desc = op_desc;
op_decl.insert_into_op_dir(&mut self.wam_prelude.indices.op_dir);
}
RetractionRecord::AddedUserPredicate(key) => {
self.wam_prelude.indices.code_dir.remove(&key);
}
RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
self.wam_prelude
.indices
.code_dir
.get_mut(&key)
.map(|code_idx| code_idx.replace(old_code_idx));
}
RetractionRecord::AddedIndex(index_key, clause_loc) => {
// WAS: inner_index_locs) => {
if let Some(index_loc) = index_key.switch_on_term_loc() {
let indexing_code = match &mut self.wam_prelude.code_repo.code[index_loc] {
Line::IndexingCode(indexing_code) => indexing_code,
_ => {
unreachable!()
}
};
match index_key {
OptArgIndexKey::Literal(
_,
index_loc,
constant,
overlapping_constants,
) => {
remove_constant_indices(
constant,
&overlapping_constants,
indexing_code,
clause_loc - index_loc, // WAS: &inner_index_locs,
);
}
OptArgIndexKey::Structure(_, index_loc, name, arity) => {
remove_structure_index(
name,
arity,
indexing_code,
clause_loc - index_loc, // WAS: &inner_index_locs,
);
}
OptArgIndexKey::List(_, index_loc) => {
remove_list_index(
indexing_code,
clause_loc - index_loc, // WAS: &inner_index_locs,
);
}
OptArgIndexKey::None => {
unreachable!();
}
}
}
}
RetractionRecord::RemovedIndex(_index_loc, _index_key, _clause_loc) => {
// TODO: this needs to be fixed! RemovedIndex doesn't provide
// enough information to restore the index. Correct that, then
// write the retraction logic of this arm.
}
RetractionRecord::ReplacedChoiceOffset(instr_loc, offset) => {
match self.wam_prelude.code_repo.code[instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o))
| Line::Choice(ChoiceInstruction::RetryMeElse(ref mut o))
| Line::Choice(ChoiceInstruction::DefaultRetryMeElse(ref mut o)) => {
*o = offset;
}
_ => {
unreachable!();
}
}
}
RetractionRecord::AppendedTrustMe(instr_loc, offset, is_default) => {
match self.wam_prelude.code_repo.code[instr_loc] {
Line::Choice(ref mut choice_instr) => {
*choice_instr = if is_default {
ChoiceInstruction::DefaultTrustMe(offset)
} else {
ChoiceInstruction::TrustMe(offset)
};
}
_ => {
unreachable!();
}
}
}
RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v) => {
match self.wam_prelude.code_repo.code[index_loc] {
Line::IndexingCode(ref mut indexing_code) => match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
ref mut v,
..,
)) => {
*v = old_v;
}
_ => {}
},
_ => {}
}
}
RetractionRecord::ModifiedTryMeElse(instr_loc, o) => {
self.wam_prelude.code_repo.code[instr_loc] =
Line::Choice(ChoiceInstruction::TryMeElse(o));
}
RetractionRecord::ModifiedRetryMeElse(instr_loc, o) => {
self.wam_prelude.code_repo.code[instr_loc] =
Line::Choice(ChoiceInstruction::RetryMeElse(o));
}
RetractionRecord::ModifiedRevJmpBy(instr_loc, o) => {
self.wam_prelude.code_repo.code[instr_loc] =
Line::Control(ControlInstruction::RevJmpBy(o));
}
RetractionRecord::SkeletonClausePopBack(compilation_target, key) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
skeleton.clauses.pop_back();
skeleton.core.clause_clause_locs.pop_back();
}
None => {}
}
}
RetractionRecord::SkeletonClausePopFront(compilation_target, key) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
skeleton.clauses.pop_front();
skeleton.core.clause_clause_locs.pop_front();
skeleton.core.clause_assert_margin -= 1;
}
None => {}
}
}
RetractionRecord::SkeletonLocalClauseClausePopFront(
src_compilation_target,
local_compilation_target,
key,
) => {
let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
src_compilation_target,
local_compilation_target,
listing_src_file_name,
key,
) {
Some(skeleton) => {
skeleton.clause_clause_locs.pop_front();
}
None => {}
}
}
RetractionRecord::SkeletonLocalClauseClausePopBack(
src_compilation_target,
local_compilation_target,
key,
) => {
let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
src_compilation_target,
local_compilation_target,
listing_src_file_name,
key,
) {
Some(skeleton) => {
skeleton.clause_clause_locs.pop_back();
}
None => {}
}
}
RetractionRecord::SkeletonLocalClauseTruncateBack(
src_compilation_target,
local_compilation_target,
key,
len,
) => {
let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
src_compilation_target,
local_compilation_target,
listing_src_file_name,
key,
) {
Some(skeleton) => {
skeleton.clause_clause_locs.truncate_back(len);
}
None => {}
}
}
RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
skeleton.clauses.truncate_back(len);
skeleton.core.clause_clause_locs.truncate_back(len);
}
None => {}
}
}
RetractionRecord::SkeletonClauseStartReplaced(
compilation_target,
key,
target_pos,
clause_start,
) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
skeleton.clauses[target_pos].clause_start = clause_start;
}
None => {}
}
}
RetractionRecord::RemovedDynamicSkeletonClause(
compilation_target,
key,
target_pos,
clause_clause_loc,
) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
if let Some(removed_clauses) = &mut skeleton.core.retracted_dynamic_clauses {
let clause_index_info = removed_clauses.pop().unwrap();
skeleton
.core
.clause_clause_locs
.insert(target_pos, clause_clause_loc);
skeleton.clauses.insert(target_pos, clause_index_info);
}
}
None => {}
}
}
RetractionRecord::RemovedSkeletonClause(
compilation_target,
key,
target_pos,
clause_index_info,
clause_clause_loc,
) => {
match self
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
{
Some(skeleton) => {
skeleton
.core
.clause_clause_locs
.insert(target_pos, clause_clause_loc);
skeleton.clauses.insert(target_pos, clause_index_info);
}
None => {}
}
}
RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => {
self.wam_prelude.code_repo.code[index_loc] = Line::IndexingCode(indexing_code);
}
RetractionRecord::RemovedLocalSkeletonClauseLocations(
compilation_target,
local_compilation_target,
key,
clause_locs,
) => {
let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
compilation_target,
local_compilation_target,
listing_src_file_name,
key,
) {
Some(skeleton) => skeleton.clause_clause_locs = clause_locs,
None => {}
}
}
RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => {
match compilation_target {
CompilationTarget::User => {
self.wam_prelude.indices.extensible_predicates.insert(key, skeleton);
}
CompilationTarget::Module(module_name) => {
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
module.extensible_predicates.insert(key, skeleton);
}
}
}
}
RetractionRecord::ReplacedDynamicElseOffset(instr_loc, next) => {
match self.wam_prelude.code_repo.code[instr_loc] {
Line::Choice(ChoiceInstruction::DynamicElse(
_,
_,
NextOrFail::Next(ref mut o),
))
| Line::Choice(ChoiceInstruction::DynamicInternalElse(
_,
_,
NextOrFail::Next(ref mut o),
)) => {
*o = next;
}
_ => {}
}
}
RetractionRecord::AppendedNextOrFail(instr_loc, fail) => {
match self.wam_prelude.code_repo.code[instr_loc] {
Line::Choice(ChoiceInstruction::DynamicElse(
_,
_,
ref mut next_or_fail,
))
| Line::Choice(ChoiceInstruction::DynamicInternalElse(
_,
_,
ref mut next_or_fail,
)) => {
*next_or_fail = fail;
}
_ => {}
}
}
}
}
}
fn extract_module_export_list_from_heap(
&mut self,
r: RegType,
) -> Result<IndexSet<ModuleExport>, SessionError> {
let export_list = self.read_term_from_heap(r)?;
let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
let export_list = setup_module_export_list(export_list, atom_tbl)?;
Ok(export_list.into_iter().collect())
}
fn add_clause_clause(&mut self, term: Term) -> Result<(), CompilationError> {
match term {
Term::Clause(_, atom!(":-"), mut terms) if terms.len() == 2 =>
{
let body = terms.pop().unwrap();
let head = terms.pop().unwrap();
self.payload.clause_clauses.push((head, body));
}
head @ Term::Literal(_, Literal::Atom(..)) | head @ Term::Clause(..) => {
let body = Term::Literal(Cell::default(), Literal::Atom(atom!("true")));
self.payload.clause_clauses.push((head, body));
}
_ => {
return Err(CompilationError::InadmissibleFact);
}
}
Ok(())
}
fn add_extensible_predicate_declaration(
&mut self,
compilation_target: CompilationTarget,
name: Atom,
arity: usize,
flag_accessor: impl Fn(&mut LocalPredicateSkeleton) -> &mut bool,
retraction_fn: impl Fn(CompilationTarget, PredicateKey) -> RetractionRecord,
) -> Result<(), SessionError> {
let key = (name, arity);
let mut throw_permission_error = false;
match &compilation_target {
CompilationTarget::User => {
match self
.wam_prelude
.indices
.extensible_predicates
.get_mut(&key)
{
Some(skeleton) => {
if !*flag_accessor(&mut skeleton.core) {
*flag_accessor(&mut skeleton.core) = true;
self.payload.retraction_info.push_record(retraction_fn(
compilation_target,
key,
));
}
}
None => {
if self.payload.compilation_target == compilation_target {
let mut skeleton = PredicateSkeleton::new();
*flag_accessor(&mut skeleton.core) = true;
self.add_extensible_predicate(
key,
skeleton,
CompilationTarget::User,
);
} else {
throw_permission_error = true;
}
}
}
}
CompilationTarget::Module(ref module_name) => {
match self.wam_prelude.indices.modules.get_mut(module_name) {
Some(ref mut module) => match module.extensible_predicates.get_mut(&key) {
Some(ref mut skeleton) => {
if !*flag_accessor(&mut skeleton.core) {
*flag_accessor(&mut skeleton.core) = true;
self.payload.retraction_info.push_record(retraction_fn(
compilation_target,
key,
));
}
}
None => {
if self.payload.compilation_target == compilation_target {
let mut skeleton = PredicateSkeleton::new();
*flag_accessor(&mut skeleton.core) = true;
self.add_extensible_predicate(
key,
skeleton,
compilation_target,
);
} else {
throw_permission_error = true;
}
}
},
None => {
self.add_dynamically_generated_module(*module_name);
let mut skeleton = PredicateSkeleton::new();
*flag_accessor(&mut skeleton.core) = true;
self.add_extensible_predicate(
key,
skeleton,
compilation_target,
);
}
}
}
}
if !throw_permission_error {
let listing_src_file_name = self.listing_src_file_name();
let payload_compilation_target = self.payload.compilation_target;
match payload_compilation_target {
CompilationTarget::User => {
match self
.wam_prelude
.indices
.get_local_predicate_skeleton_mut(
payload_compilation_target,
compilation_target,
listing_src_file_name,
key,
) {
Some(skeleton) => {
if !*flag_accessor(skeleton) {
*flag_accessor(skeleton) = true;
}
}
None => {
let mut skeleton = LocalPredicateSkeleton::new();
*flag_accessor(&mut skeleton) = true;
self.add_local_extensible_predicate(
compilation_target,
key,
skeleton,
);
}
}
}
CompilationTarget::Module(ref module_name) => {
match self.wam_prelude.indices.modules.get_mut(module_name) {
Some(module) => match module
.local_extensible_predicates
.get_mut(&(compilation_target, key))
{
Some(skeleton) => {
if !*flag_accessor(skeleton) {
*flag_accessor(skeleton) = true;
}
}
None => {
let mut skeleton = LocalPredicateSkeleton::new();
*flag_accessor(&mut skeleton) = true;
self.add_local_extensible_predicate(
compilation_target,
key,
skeleton,
);
}
},
None => {
self.add_dynamically_generated_module(*module_name);
let mut skeleton = LocalPredicateSkeleton::new();
*flag_accessor(&mut skeleton) = true;
self.add_local_extensible_predicate(
compilation_target,
key,
skeleton,
);
}
}
}
}
self.fail_on_undefined(compilation_target, key);
Ok(())
} else {
Err(SessionError::PredicateNotMultifileOrDiscontiguous(
compilation_target,
key,
))
}
}
fn fail_on_undefined(&mut self, compilation_target: CompilationTarget, key: PredicateKey) {
/*
* DynamicUndefined isn't only applied to dynamic predicates
* but to multifile and discontiguous predicates as well.
*/
let code_index = self.get_or_insert_code_index(key, compilation_target);
if let IndexPtr::Undefined = code_index.get() {
set_code_index(
&mut self.payload.retraction_info,
&compilation_target,
key,
&code_index,
IndexPtr::DynamicUndefined,
);
}
}
fn add_discontiguous_predicate(
&mut self,
compilation_target: CompilationTarget,
name: Atom,
arity: usize,
) -> Result<(), SessionError> {
self.add_extensible_predicate_declaration(
compilation_target,
name,
arity,
|skeleton| &mut skeleton.is_discontiguous,
RetractionRecord::AddedDiscontiguousPredicate,
)
}
fn add_dynamic_predicate(
&mut self,
compilation_target: CompilationTarget,
name: Atom,
arity: usize,
) -> Result<(), SessionError> {
self.add_extensible_predicate_declaration(
compilation_target,
name,
arity,
|skeleton| &mut skeleton.is_dynamic,
RetractionRecord::AddedDynamicPredicate,
)
}
fn add_multifile_predicate(
&mut self,
compilation_target: CompilationTarget,
name: Atom,
arity: usize,
) -> Result<(), SessionError> {
self.add_extensible_predicate_declaration(
compilation_target,
name,
arity,
|skeleton| &mut skeleton.is_multifile,
RetractionRecord::AddedMultifilePredicate,
)
}
fn add_clause_clause_if_dynamic(&mut self, term: &Term) -> Result<(), SessionError> {
if let Some(predicate_name) = ClauseInfo::name(term) {
let arity = ClauseInfo::arity(term);
let predicates_compilation_target = self.payload.predicates.compilation_target;
let is_dynamic = self
.wam_prelude
.indices
.get_predicate_skeleton(
&predicates_compilation_target,
&(predicate_name, arity),
)
.map(|skeleton| skeleton.core.is_dynamic)
.unwrap_or(false);
if is_dynamic {
self.add_clause_clause(term.clone())?;
}
}
Ok(())
}
pub(super) fn retract_local_clauses(&mut self, key: &PredicateKey, is_dynamic: bool) {
let payload_compilation_target = self.payload.compilation_target;
let predicates_compilation_target = self.payload.predicates.compilation_target;
let listing_src_file_name = self.listing_src_file_name();
let clause_locs = match self
.wam_prelude
.indices
.get_local_predicate_skeleton_mut(
payload_compilation_target,
predicates_compilation_target,
listing_src_file_name,
*key,
) {
Some(skeleton) if !skeleton.clause_clause_locs.is_empty() => {
mem::replace(&mut skeleton.clause_clause_locs, sdeq![])
}
_ => return,
};
self.payload.retraction_info.push_record(
RetractionRecord::RemovedLocalSkeletonClauseLocations(
payload_compilation_target,
predicates_compilation_target,
*key,
clause_locs.clone(),
),
);
self.retract_local_clauses_impl(
predicates_compilation_target,
*key,
&clause_locs,
);
if is_dynamic {
let clause_clause_compilation_target = match predicates_compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
module_name => module_name,
};
self.retract_local_clause_clauses(clause_clause_compilation_target, &clause_locs);
}
}
}
impl<'a> MachinePreludeView<'a> {
#[inline]
pub(super) fn composite_op_dir(&self, compilation_target: &CompilationTarget) -> CompositeOpDir {
match compilation_target {
CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
CompilationTarget::Module(ref module_name) => {
match self.indices.modules.get(module_name) {
Some(ref module) => {
CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir))
}
None => {
unreachable!()
}
}
}
}
}
}
impl Machine {
pub(crate) fn use_module(&mut self) {
let subevacuable_addr = self
.machine_st
.store(self.machine_st.deref(self.machine_st.registers[2]));
let module_src = ModuleSource::Library({
let payload = cell_as_load_state_payload!(subevacuable_addr);
match payload.compilation_target {
CompilationTarget::Module(module_name) => module_name,
CompilationTarget::User => {
return;
}
}
});
let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
let use_module = || {
let export_list = loader.extract_module_export_list_from_heap(temp_v!(3))?;
if export_list.is_empty() {
loader.use_module(module_src)?;
} else {
loader.use_qualified_module(module_src, export_list)?;
}
LiveLoadAndMachineState::evacuate(loader)
};
let result = use_module();
self.restore_load_state_payload(result);
}
pub(crate) fn load_compiled_library(&mut self) {
let library = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
if let Some(module) = self.indices.modules.get(&library) {
if let ListingSource::DynamicallyGenerated = module.listing_src {
self.machine_st.fail = true;
return;
}
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
let import_module = || {
let export_list = loader.extract_module_export_list_from_heap(temp_v!(2))?;
if export_list.is_empty() {
loader.import_module(library)?;
} else {
loader.import_qualified_module(library, export_list)?;
}
LiveLoadAndMachineState::evacuate(loader)
};
let result = import_module();
self.restore_load_state_payload(result);
} else {
self.machine_st.fail = true;
}
}
pub(crate) fn declare_module(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
let declare_module = || {
// let export_list = export_list?;
let exports = loader.extract_module_export_list_from_heap(temp_v!(2))?;
let module_decl = ModuleDecl {
name: module_name,
exports: exports.into_iter().collect(),
};
loader.load_decl(Declaration::Module(module_decl))?;
LiveLoadAndMachineState::evacuate(loader)
};
let result = declare_module();
self.restore_load_state_payload(result);
}
#[inline]
pub(crate) fn add_discontiguous_predicate(&mut self) {
self.add_extensible_predicate_declaration(
|loader, compilation_target, clause_name, arity| {
loader.add_discontiguous_predicate(compilation_target, clause_name, arity)
},
);
}
#[inline]
pub(crate) fn add_dynamic_predicate(&mut self) {
self.add_extensible_predicate_declaration(
|loader, compilation_target, clause_name, arity| {
loader.add_dynamic_predicate(compilation_target, clause_name, arity)
},
);
}
#[inline]
pub(crate) fn add_multifile_predicate(&mut self) {
self.add_extensible_predicate_declaration(
|loader, compilation_target, clause_name, arity| {
loader.add_multifile_predicate(compilation_target, clause_name, arity)
},
);
}
fn add_extensible_predicate_declaration(
&mut self,
decl_adder: impl for<'a> Fn(
&mut Loader<'a, LiveLoadAndMachineState<'a>>,
CompilationTarget,
Atom,
usize,
) -> Result<(), SessionError>,
) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let predicate_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))
);
let arity = self
.machine_st
.store(self.machine_st.deref(self.machine_st.registers[3]));
let arity = match Number::try_from(arity) {
Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY => Ok(n.to_usize().unwrap()),
Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => {
Ok(usize::try_from(n.get_num()).unwrap())
}
_ => Err(SessionError::from(CompilationError::InvalidRuleHead)),
};
let mut loader = self.loader_from_heap_evacuable(temp_v!(4));
let add_predicate_decl = move || {
decl_adder(&mut loader, compilation_target, predicate_name, arity?)?;
LiveLoadAndMachineState::evacuate(loader)
};
let result = add_predicate_decl();
self.restore_load_state_payload(result);
}
pub(crate) fn add_term_expansion_clause(&mut self) {
let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
let add_clause = || {
let term = loader.read_term_from_heap(temp_v!(1))?;
loader.incremental_compile_clause(
(atom!("term_expansion"), 2),
term,
CompilationTarget::User,
false,
AppendOrPrepend::Append,
)?;
LiveLoadAndMachineState::evacuate(loader)
};
let result = add_clause();
self.restore_load_state_payload(result);
}
pub(crate) fn add_goal_expansion_clause(&mut self) {
let target_module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
let compilation_target = match target_module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(target_module_name),
};
let add_clause = || {
let term = loader.read_term_from_heap(temp_v!(2))?;
loader.incremental_compile_clause(
(atom!("goal_expansion"), 2),
term,
compilation_target,
false, // backtracking inferences are counted by call_with_inference_limit.
AppendOrPrepend::Append,
)?;
LiveLoadAndMachineState::evacuate(loader)
};
let result = add_clause();
self.restore_load_state_payload(result);
}
pub(crate) fn add_in_situ_filename_module(&mut self) {
let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
let add_in_situ_filename_module = || {
if let Some(filename) = loader.listing_src_file_name() {
let module_decl = ModuleDecl {
name: filename,
exports: vec![],
};
let module_name = module_decl.name;
if !loader
.wam_prelude
.indices
.modules
.contains_key(&module_decl.name)
{
let module = Module::new_in_situ(module_decl);
loader
.wam_prelude
.indices
.modules
.insert(module_name, module);
} else {
loader.reset_in_situ_module(
module_decl.clone(),
&ListingSource::DynamicallyGenerated,
);
match loader.wam_prelude.indices.modules.get_mut(&module_name) {
Some(module) => {
for (key, value) in module.op_dir.drain(0..) {
let mut op_decl = OpDecl::new(value, key.0);
op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
}
}
None => {}
}
}
}
LiveLoadAndMachineState::evacuate(loader)
};
let result = add_in_situ_filename_module();
self.restore_load_state_payload(result);
}
pub(crate) fn loader_from_heap_evacuable<'a>(
&'a mut self,
r: RegType,
) -> Loader<'a, LiveLoadAndMachineState<'a>> {
let mut load_state = cell_as_load_state_payload!(
self.machine_st.store(self.machine_st.deref(self.machine_st[r]))
);
load_state.set_tag(ArenaHeaderTag::LiveLoadState);
let (wam_prelude, machine_st) = self.prelude_view_and_machine_st();
Loader {
payload: LiveLoadAndMachineState { load_state, machine_st },
wam_prelude,
}
}
#[inline]
pub(crate) fn push_load_state_payload(&mut self) {
let payload = arena_alloc!(
LoadStatePayload::new(
self.code_repo.code.len(),
LiveTermStream::new(ListingSource::User),
),
&mut self.machine_st.arena
);
let var = self.machine_st.deref(
self.machine_st.registers[1]
);
self.machine_st.bind(
var.as_var().unwrap(),
typed_arena_ptr_as_cell!(payload),
);
}
#[inline]
pub(crate) fn pop_load_state_payload(&mut self) {
let load_state_payload = self.machine_st.store(
self.machine_st.deref(self.machine_st.registers[1])
);
// unlike in loader_from_heap_evacuable,
// pop_load_state_payload is allowed to fail to find a
// LoadStatePayload in the heap, as a Rust-side
// top-level command may have failed to write the
// load state payload back to the heap.
read_heap_cell!(load_state_payload,
(HeapCellValueTag::Cons, cons_ptr) => {
match_untyped_arena_ptr!(cons_ptr,
(ArenaHeaderTag::LiveLoadState, payload) => {
unsafe {
std::ptr::drop_in_place(
payload.as_ptr() as *mut LiveLoadState,
);
}
}
_ => {}
);
}
_ => {}
);
}
#[inline]
pub(crate) fn pop_load_context(&mut self) {
self.load_contexts.pop();
}
pub(crate) fn push_load_context(&mut self) {
let stream = try_or_fail!(
self.machine_st,
self.machine_st.get_stream_or_alias(
self.machine_st.registers[1],
&self.indices.stream_aliases,
atom!("$push_load_context"),
2,
)
);
let path = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))
);
self.load_contexts.push(LoadContext::new(path.as_str(), stream));
}
pub(crate) fn restore_load_state_payload(
&mut self,
result: Result<TypedArenaPtr<LiveLoadState>, SessionError>,
) {
match result {
Ok(_payload) => {
}
Err(e) => {
self.throw_session_error(e, (atom!("load"), 1));
}
}
}
pub(crate) fn scoped_clause_to_evacuable(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let loader = self.loader_from_heap_evacuable(temp_v!(3));
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let result = loader.read_and_enqueue_term(temp_v!(2), compilation_target);
self.restore_load_state_payload(result);
}
pub(crate) fn clause_to_evacuable(&mut self) {
let loader = self.loader_from_heap_evacuable(temp_v!(2));
let compilation_target = loader.payload.compilation_target;
let result = loader.read_and_enqueue_term(temp_v!(1), compilation_target);
self.restore_load_state_payload(result);
}
pub(crate) fn conclude_load(&mut self) {
let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
let compile_final_terms = || {
if !loader.payload.predicates.is_empty() {
loader.compile_and_submit()?;
}
loader.remove_module_op_exports();
LiveLoadAndMachineState::evacuate(loader)
};
let result = compile_final_terms();
self.restore_load_state_payload(result);
}
pub(crate) fn load_context_source(&mut self) {
if let Some(load_context) = self.load_contexts.last() {
let path_str = load_context.path.to_str().unwrap();
let path_atom = self.machine_st.atom_tbl.build_with(path_str);
self.machine_st.unify_atom(path_atom, self.machine_st.registers[1]);
} else {
self.machine_st.fail = true;
}
}
pub(crate) fn load_context_file(&mut self) {
if let Some(load_context) = self.load_contexts.last() {
match load_context.path.file_name() {
Some(file_name) if load_context.path.is_file() => {
let file_name_str = file_name.to_str().unwrap();
let file_name_atom = self.machine_st.atom_tbl.build_with(file_name_str);
self.machine_st.unify_atom(file_name_atom, self.machine_st.registers[1]);
return;
}
_ => {
return self.load_context_module();
}
}
}
self.machine_st.fail = true;
}
pub(crate) fn load_context_directory(&mut self) {
if let Some(load_context) = self.load_contexts.last() {
if let Some(directory) = load_context.path.parent() {
let directory_str = directory.to_str().unwrap();
let directory_atom = self.machine_st.atom_tbl.build_with(directory_str);
self.machine_st.unify_atom(directory_atom, self.machine_st.registers[1]);
return;
}
}
self.machine_st.fail = true;
}
pub(crate) fn load_context_module(&mut self) {
if let Some(load_context) = self.load_contexts.last() {
self.machine_st.unify_atom(load_context.module, self.machine_st.registers[1]);
} else {
self.machine_st.fail = true;
}
}
pub(crate) fn load_context_stream(&mut self) {
if let Some(load_context) = self.load_contexts.last() {
self.machine_st.unify_constant(
UntypedArenaPtr::from(load_context.stream.as_ptr()),
self.machine_st.registers[1],
);
} else {
self.machine_st.fail = true;
}
}
pub(crate) fn compile_assert<'a>(&'a mut self, append_or_prepend: AppendOrPrepend) {
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(3)], self.machine_st[temp_v!(4)]);
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5]))
);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let mut compile_assert = || {
let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> =
Loader::new(self, LiveTermStream::new(ListingSource::User));
loader.payload.compilation_target = compilation_target;
let head = loader.read_term_from_heap(temp_v!(1))?;
let body = loader.read_term_from_heap(temp_v!(2))?;
let asserted_clause = Term::Clause(
Cell::default(),
atom!(":-"),
vec![head.clone(), body.clone()],
);
// if a new predicate was just created, make it dynamic.
loader.add_dynamic_predicate(compilation_target, key.0, key.1)?;
loader.incremental_compile_clause(
key,
asserted_clause,
compilation_target,
false,
append_or_prepend,
)?;
// the global clock is incremented after each assertion.
LiveLoadAndMachineState::machine_st(&mut loader.payload).global_clock += 1;
loader.compile_clause_clauses(
key,
compilation_target,
std::iter::once((head, body)),
append_or_prepend,
)?;
LiveLoadAndMachineState::evacuate(loader)
};
match compile_assert() {
Ok(_) => {}
Err(e) => {
let error_pi = match append_or_prepend {
AppendOrPrepend::Append => (atom!("assertz"), 1),
AppendOrPrepend::Prepend => (atom!("asserta"), 1),
};
self.throw_session_error(e, error_pi);
}
}
}
pub(crate) fn abolish_clause(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let mut abolish_clause = || {
let mut loader: Loader<'_, LiveLoadAndMachineState<'_>>
= Loader::new(self, LiveTermStream::new(ListingSource::User));
loader.payload.compilation_target = compilation_target;
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
module => module,
};
let mut clause_clause_target_poses: Vec<_> = loader
.wam_prelude
.indices
.get_predicate_skeleton(&compilation_target, &key)
.map(|skeleton| {
loader
.wam_prelude
.indices
.get_predicate_skeleton(
&clause_clause_compilation_target,
&(atom!("$clause"), 2),
)
.map(|clause_clause_skeleton| {
skeleton
.core
.clause_clause_locs
.iter()
.map(|clause_clause_loc| {
clause_clause_skeleton
.target_pos_of_clause_clause_loc(*clause_clause_loc)
.unwrap()
})
.collect()
})
.unwrap()
})
.unwrap();
loader
.wam_prelude
.indices
.get_predicate_skeleton_mut(&compilation_target, &key)
.map(|skeleton| skeleton.reset());
let code_index = loader
.get_or_insert_code_index(key, compilation_target);
code_index.set(IndexPtr::DynamicUndefined);
loader.payload.compilation_target = clause_clause_compilation_target;
while let Some(target_pos) = clause_clause_target_poses.pop() {
loader.retract_clause((atom!("$clause"), 2), target_pos);
}
LiveLoadAndMachineState::evacuate(loader)
};
match abolish_clause() {
Ok(_) => {}
Err(e) => {
self.throw_session_error(e, (atom!("abolish"), 1));
}
}
}
pub(crate) fn retract_clause(&mut self) {
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(1)], self.machine_st[temp_v!(2)]);
let target_pos = self
.machine_st
.store(self.machine_st.deref(self.machine_st[temp_v!(3)]));
let target_pos = match Number::try_from(target_pos) {
Ok(Number::Integer(n)) => n.to_usize().unwrap(),
Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
_ => unreachable!(),
};
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4]))
);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
_ => compilation_target,
};
let mut retract_clause = || {
let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> =
Loader::new(self, LiveTermStream::new(ListingSource::User));
loader.payload.compilation_target = compilation_target;
let clause_clause_loc = loader.retract_dynamic_clause(key, target_pos);
// the global clock is incremented after each retraction.
LiveLoadAndMachineState::machine_st(&mut loader.payload).global_clock += 1;
let target_pos = match loader.wam_prelude.indices.get_predicate_skeleton(
&clause_clause_compilation_target,
&(atom!("$clause"), 2),
) {
Some(skeleton) => skeleton
.target_pos_of_clause_clause_loc(clause_clause_loc)
.unwrap(),
None => {
unreachable!();
}
};
loader.payload.compilation_target = clause_clause_compilation_target;
loader.retract_clause((atom!("$clause"), 2), target_pos);
LiveLoadAndMachineState::evacuate(loader)
};
match retract_clause() {
Ok(_) => {}
Err(e) => {
self.throw_session_error(e, (atom!("retract"), 1));
}
}
}
pub(crate) fn is_consistent_with_term_queue(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
let mut loader = self.loader_from_heap_evacuable(temp_v!(4));
LiveLoadAndMachineState::machine_st(&mut loader.payload).fail =
(!loader.payload.predicates.is_empty()
&& loader.payload.predicates.compilation_target != compilation_target)
|| !key.is_consistent(&loader.payload.predicates);
let result = LiveLoadAndMachineState::evacuate(loader);
self.restore_load_state_payload(result);
}
pub(crate) fn flush_term_queue(&mut self) {
let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
let flush_term_queue = || {
if !loader.payload.predicates.is_empty() {
loader.compile_and_submit()?;
}
LiveLoadAndMachineState::evacuate(loader)
};
let result = flush_term_queue();
self.restore_load_state_payload(result);
}
pub(crate) fn remove_module_exports(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
let remove_module_exports = || {
loader.remove_module_exports(module_name);
LiveLoadAndMachineState::evacuate(loader)
};
let result = remove_module_exports();
self.restore_load_state_payload(result);
}
pub(crate) fn add_non_counted_backtracking(&mut self) {
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(1)], self.machine_st[temp_v!(2)]);
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
loader.payload.non_counted_bt_preds.insert(key);
let result = LiveLoadAndMachineState::evacuate(loader);
self.restore_load_state_payload(result);
}
pub(crate) fn meta_predicate_property(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let (predicate_name, arity) = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
match self
.indices
.get_meta_predicate_spec(predicate_name, arity, &compilation_target)
{
Some(meta_specs) => {
let list_loc = iter_to_heap_list(
&mut self.machine_st.heap,
meta_specs.iter().map(|meta_spec| match meta_spec {
MetaSpec::Minus => atom_as_cell!(atom!("+")),
MetaSpec::Plus => atom_as_cell!(atom!("-")),
MetaSpec::Either => atom_as_cell!(atom!("?")),
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
fixnum_as_cell!(Fixnum::build_with(*arg_num as i64))
}
}));
let heap_loc = self.machine_st.heap.len();
self.machine_st.heap.push(atom_as_cell!(atom!("meta_predicate"), 1));
self.machine_st.heap.push(heap_loc_as_cell!(list_loc));
unify!(self.machine_st, str_loc_as_cell!(heap_loc), self.machine_st.registers[4]);
}
None => {
self.machine_st.fail = true;
}
}
}
pub(crate) fn dynamic_property(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
match self
.indices
.get_predicate_skeleton(&compilation_target, &key)
{
Some(skeleton) => {
self.machine_st.fail = !skeleton.core.is_dynamic;
}
None => {
self.machine_st.fail = true;
}
}
}
pub(crate) fn multifile_property(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
match self
.indices
.get_predicate_skeleton(&compilation_target, &key)
{
Some(skeleton) => {
self.machine_st.fail = !skeleton.core.is_multifile;
}
None => {
self.machine_st.fail = true;
}
}
}
pub(crate) fn discontiguous_property(&mut self) {
let module_name = cell_as_atom!(
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self
.machine_st
.read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
let compilation_target = match module_name {
atom!("user") => CompilationTarget::User,
_ => CompilationTarget::Module(module_name),
};
match self
.indices
.get_predicate_skeleton(&compilation_target, &key)
{
Some(skeleton) => {
self.machine_st.fail = !skeleton.core.is_discontiguous;
}
None => {
self.machine_st.fail = true;
}
}
}
pub(crate) fn builtin_property(&mut self) {
let key = self
.machine_st
.read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]);
match ClauseType::from(key.0, key.1) {
ClauseType::BuiltIn(_) | ClauseType::Inlined(..) | ClauseType::CallN => {
return;
}
ClauseType::Named(name, arity, _) => {
if let Some(module) = self.indices.modules.get(&(atom!("builtins"))) {
self.machine_st.fail = !module.code_dir.contains_key(&(name, arity));
return;
}
}
_ => {}
}
self.machine_st.fail = true;
}
}
impl<'a> Loader<'a, LiveLoadAndMachineState<'a>> {
fn read_and_enqueue_term(
mut self,
term_reg: RegType,
compilation_target: CompilationTarget,
) -> Result<TypedArenaPtr<LoadStatePayload<LiveTermStream>>, SessionError> {
if self.payload.predicates.compilation_target != compilation_target {
if !self.payload.predicates.is_empty() {
self.compile_and_submit()?;
}
self.payload.predicates.compilation_target = compilation_target;
}
let term = self.read_term_from_heap(term_reg)?;
self.add_clause_clause_if_dynamic(&term)?;
self.payload.term_stream.term_queue.push_back(term);
self.load()
}
}
#[inline]
pub(super) fn load_module(
code_dir: &mut CodeDir,
op_dir: &mut OpDir,
meta_predicate_dir: &mut MetaPredicateDir,
compilation_target: &CompilationTarget,
module: &Module,
) {
import_module_exports(
&mut RetractionInfo::new(0),
&compilation_target,
module,
code_dir,
op_dir,
meta_predicate_dir,
)
.unwrap();
}