1710 lines
62 KiB
Rust
1710 lines
62 KiB
Rust
use prolog_parser_rebis::ast::*;
|
|
|
|
use crate::forms::*;
|
|
use crate::indexing::*;
|
|
use crate::machine::*;
|
|
use crate::machine::load_state::*;
|
|
use crate::machine::machine_indices::*;
|
|
use crate::machine::preprocessor::*;
|
|
|
|
use indexmap::IndexSet;
|
|
|
|
use std::cell::Cell;
|
|
use std::convert::TryFrom;
|
|
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. TopLevelBatchWorker 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(ClauseName, PredicateKey),
|
|
ReplacedMetaPredicate(ClauseName, ClauseName, Vec<MetaSpec>),
|
|
AddedModule(ClauseName),
|
|
ReplacedModule(ModuleDecl, ListingSource),
|
|
AddedModuleDynamicPredicate(ClauseName, PredicateKey),
|
|
AddedModuleExtensiblePredicate(ClauseName, PredicateKey),
|
|
AppendedModuleExtensiblePredicate(ClauseName, PredicateKey),
|
|
PrependedModuleExtensiblePredicate(ClauseName, PredicateKey),
|
|
AddedModuleOp(ClauseName, OpDecl),
|
|
ReplacedModuleOp(ClauseName, OpDecl, usize, Specifier),
|
|
AddedModulePredicate(ClauseName, PredicateKey),
|
|
ReplacedModulePredicate(ClauseName, PredicateKey, IndexPtr),
|
|
AddedUserDynamicPredicate(PredicateKey),
|
|
AddedUserOp(OpDecl),
|
|
ReplacedUserOp(OpDecl, usize, Specifier),
|
|
AddedUserExtensiblePredicate(PredicateKey),
|
|
AppendedUserExtensiblePredicate(PredicateKey),
|
|
PrependedUserExtensiblePredicate(PredicateKey),
|
|
AddedUserPredicate(PredicateKey),
|
|
ReplacedUserPredicate(PredicateKey, IndexPtr),
|
|
AddedIndex(OptArgIndexKey, usize), //, Vec<usize>),
|
|
RemovedIndex(usize, OptArgIndexKey, usize),
|
|
ReplacedChoiceOffset(usize, usize),
|
|
AppendedTrustMe(usize, usize, bool),
|
|
ReplacedSwitchOnTermVarIndex(usize, usize),
|
|
ModifiedTryMeElse(usize, usize),
|
|
ModifiedRetryMeElse(usize, usize),
|
|
ModifiedRevJmpBy(usize, usize),
|
|
IncreasedClauseAssertMargin(ClauseName, usize),
|
|
SkeletonClauseClausesTruncateBack(CompilationTarget, PredicateKey, usize),
|
|
SkeletonClauseClausesTruncateFront(CompilationTarget, PredicateKey, usize),
|
|
SkeletonClausePopBack(CompilationTarget, PredicateKey),
|
|
SkeletonClausePopFront(CompilationTarget, PredicateKey),
|
|
SkeletonClauseTruncateBack(CompilationTarget, PredicateKey, usize),
|
|
SkeletonClauseStartReplaced(CompilationTarget, PredicateKey, usize, usize),
|
|
RemovedDynamicSkeletonClause(CompilationTarget, PredicateKey, usize, ClauseIndexInfo, usize),
|
|
ReplacedIndexingLine(usize, Vec<IndexingLine>),
|
|
}
|
|
|
|
/*
|
|
* 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![], //BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
#[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![]), //BTreeMap::new()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Drop for LoadState<'a> {
|
|
fn drop(&mut self) {
|
|
while let Some(record) = self.retraction_info.records.pop() {
|
|
match record {
|
|
RetractionRecord::AddedMetaPredicate(target_module_name, key) => {
|
|
match target_module_name.as_str() {
|
|
"user" => {
|
|
self.wam.indices.meta_predicates.remove(&key);
|
|
}
|
|
_ => {
|
|
match self.wam.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.as_str() {
|
|
"user" => {
|
|
self.wam.indices.meta_predicates.insert(
|
|
(name, meta_specs.len()),
|
|
meta_specs,
|
|
);
|
|
}
|
|
_ => {
|
|
match self.wam.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.indices.modules.remove(&module_name);
|
|
}
|
|
RetractionRecord::ReplacedModule(module_decl, listing_src) => {
|
|
match self.wam.indices.modules.get_mut(&module_decl.name) {
|
|
Some(ref mut module) => {
|
|
module.module_decl = module_decl;
|
|
module.listing_src = listing_src;
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::AddedModuleDynamicPredicate(module_name, key) => {
|
|
match self.wam.indices.modules.get_mut(&module_name) {
|
|
Some(ref mut module) => {
|
|
module.extensible_predicates.get_mut(&key)
|
|
.map(|skeleton| {
|
|
skeleton.is_dynamic = false;
|
|
});
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::AddedModuleExtensiblePredicate(module_name, key) => {
|
|
self.wam.indices.remove_predicate_skeleton(
|
|
&CompilationTarget::Module(module_name),
|
|
&key,
|
|
);
|
|
}
|
|
RetractionRecord::AppendedModuleExtensiblePredicate(module_name, key) => {
|
|
self.wam.indices.get_predicate_skeleton(
|
|
&CompilationTarget::Module(module_name),
|
|
&key,
|
|
).map(|skeleton| {
|
|
skeleton.clauses.pop_back();
|
|
});
|
|
}
|
|
RetractionRecord::PrependedModuleExtensiblePredicate(module_name, key) => {
|
|
self.wam.indices.get_predicate_skeleton(
|
|
&CompilationTarget::Module(module_name),
|
|
&key,
|
|
).map(|skeleton| {
|
|
skeleton.clauses.pop_front();
|
|
});
|
|
}
|
|
RetractionRecord::AddedModuleOp(module_name, mut op_decl) => {
|
|
match self.wam.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, prec, spec) => {
|
|
match self.wam.indices.modules.get_mut(&module_name) {
|
|
Some(ref mut module) => {
|
|
op_decl.prec = prec;
|
|
op_decl.spec = spec;
|
|
op_decl.insert_into_op_dir(&mut module.op_dir);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::AddedModulePredicate(module_name, key) => {
|
|
match self.wam.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.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::AddedUserDynamicPredicate(key) => {
|
|
self.wam.indices.extensible_predicates.get_mut(&key)
|
|
.map(|skeleton| {
|
|
skeleton.is_dynamic = false;
|
|
});
|
|
}
|
|
RetractionRecord::AddedUserExtensiblePredicate(key) => {
|
|
self.wam.indices.remove_predicate_skeleton(
|
|
&CompilationTarget::User,
|
|
&key,
|
|
);
|
|
}
|
|
RetractionRecord::AppendedUserExtensiblePredicate(key) => {
|
|
self.wam.indices.get_predicate_skeleton(
|
|
&CompilationTarget::User,
|
|
&key,
|
|
).map(|skeleton| {
|
|
skeleton.clauses.pop_back();
|
|
});
|
|
}
|
|
RetractionRecord::PrependedUserExtensiblePredicate(key) => {
|
|
self.wam.indices.get_predicate_skeleton(
|
|
&CompilationTarget::User,
|
|
&key,
|
|
).map(|skeleton| {
|
|
skeleton.clauses.pop_front();
|
|
});
|
|
}
|
|
RetractionRecord::AddedUserOp(mut op_decl) => {
|
|
op_decl.remove(&mut self.wam.indices.op_dir);
|
|
}
|
|
RetractionRecord::ReplacedUserOp(mut op_decl, prec, spec) => {
|
|
op_decl.prec = prec;
|
|
op_decl.spec = spec;
|
|
op_decl.insert_into_op_dir(&mut self.wam.indices.op_dir);
|
|
}
|
|
RetractionRecord::AddedUserPredicate(key) => {
|
|
self.wam.indices.code_dir.remove(&key);
|
|
}
|
|
RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
|
|
self.wam.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.code_repo.code[index_loc] {
|
|
Line::IndexingCode(indexing_code) => {
|
|
indexing_code
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
};
|
|
|
|
match index_key {
|
|
OptArgIndexKey::Constant(_, 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 &mut self.wam.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 &mut self.wam.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 &mut self.wam.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.code_repo.code[instr_loc] =
|
|
Line::Choice(ChoiceInstruction::TryMeElse(o));
|
|
}
|
|
RetractionRecord::ModifiedRetryMeElse(instr_loc, o) => {
|
|
self.wam.code_repo.code[instr_loc] =
|
|
Line::Choice(ChoiceInstruction::RetryMeElse(o));
|
|
}
|
|
RetractionRecord::ModifiedRevJmpBy(instr_loc, o) => {
|
|
self.wam.code_repo.code[instr_loc] =
|
|
Line::Control(ControlInstruction::RevJmpBy(o));
|
|
}
|
|
RetractionRecord::IncreasedClauseAssertMargin(module_name, incr) => {
|
|
if let Some(module) = self.wam.indices.modules.get_mut(&module_name) {
|
|
module.clause_assert_margin -= incr;
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClauseClausesTruncateFront(compilation_target, key, len) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clause_clause_locs.truncate_front(len);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
|
|
let compilation_target =
|
|
match compilation_target {
|
|
CompilationTarget::User => {
|
|
CompilationTarget::Module(clause_name!("builtins"))
|
|
}
|
|
_ => {
|
|
compilation_target
|
|
}
|
|
};
|
|
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&(clause_name!("$clause"), 2),
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clause_clause_locs.truncate_front(len);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClauseClausesTruncateBack(compilation_target, key, len) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clause_clause_locs.truncate_back(len);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
|
|
let compilation_target =
|
|
match compilation_target {
|
|
CompilationTarget::User => {
|
|
CompilationTarget::Module(clause_name!("builtins"))
|
|
}
|
|
_ => {
|
|
compilation_target
|
|
}
|
|
};
|
|
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&(clause_name!("$clause"), 2),
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clause_clause_locs.truncate_back(len);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClausePopBack(compilation_target, key) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clauses.pop_back();
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClausePopFront(compilation_target, key) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clauses.pop_front();
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clauses.truncate_back(len);
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::SkeletonClauseStartReplaced(
|
|
compilation_target,
|
|
key,
|
|
target_pos,
|
|
clause_start,
|
|
) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.clauses[target_pos].clause_start = clause_start;
|
|
}
|
|
None => {
|
|
}
|
|
}
|
|
}
|
|
RetractionRecord::RemovedDynamicSkeletonClause(
|
|
compilation_target,
|
|
key,
|
|
target_pos,
|
|
clause_index_info,
|
|
clause_clause_loc,
|
|
) => {
|
|
match self.wam.indices.get_predicate_skeleton(
|
|
&compilation_target,
|
|
&key,
|
|
) {
|
|
Some(skeleton) => {
|
|
skeleton.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.code_repo.code[index_loc] = Line::IndexingCode(indexing_code);
|
|
}
|
|
}
|
|
}
|
|
// TODO: necessary? unnecessary?
|
|
// self.wam.code_repo.code.truncate(self.retraction_info.orig_code_extent);
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum CompilationTarget {
|
|
Module(ClauseName),
|
|
User,
|
|
}
|
|
|
|
impl Default for CompilationTarget {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
CompilationTarget::User
|
|
}
|
|
}
|
|
|
|
impl CompilationTarget {
|
|
#[inline]
|
|
pub(super)
|
|
fn take(&mut self) -> CompilationTarget {
|
|
mem::replace(self, CompilationTarget::User)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct Loader<'a, TermStream> {
|
|
pub(super) load_state: LoadState<'a>,
|
|
pub(super) predicates: Vec<PredicateClause>,
|
|
pub(super) clause_clauses: Vec<(Term, Term)>,
|
|
term_stream: TermStream,
|
|
pub(super) non_counted_bt_preds: IndexSet<PredicateKey>,
|
|
pub(super) preprocessor: Preprocessor,
|
|
}
|
|
|
|
impl<'a, TS: TermStream> Loader<'a, TS> {
|
|
#[inline]
|
|
pub(super)
|
|
fn new(term_stream: TS, wam: &'a mut Machine) -> Self {
|
|
let flags = wam.machine_st.flags;
|
|
let load_state = LoadState {
|
|
compilation_target: CompilationTarget::User,
|
|
module_op_exports: vec![],
|
|
retraction_info: RetractionInfo::new(wam.code_repo.code.len()),
|
|
wam,
|
|
};
|
|
|
|
Self {
|
|
load_state,
|
|
term_stream,
|
|
non_counted_bt_preds: IndexSet::new(),
|
|
preprocessor: Preprocessor::new(flags),
|
|
predicates: vec![],
|
|
clause_clauses: vec![],
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn load(mut self) -> Result<TS::Evacuable, SessionError> {
|
|
while let Some(decl) = self.dequeue_terms()? {
|
|
self.load_decl(decl)?;
|
|
}
|
|
|
|
TS::evacuate(self)
|
|
}
|
|
|
|
fn dequeue_terms(&mut self) -> Result<Option<Declaration>, SessionError> {
|
|
while !self.term_stream.eof()? {
|
|
let term = self.term_stream.next(&self.load_state.composite_op_dir())?;
|
|
|
|
// if is_consistent is false, self.predicates is not empty.
|
|
if !term.is_consistent(&self.predicates) {
|
|
self.compile_and_submit()?;
|
|
}
|
|
|
|
let tl = self.preprocessor.try_term_to_tl(
|
|
&mut self.load_state,
|
|
term,
|
|
CutContext::BlocksCuts,
|
|
)?;
|
|
|
|
match tl {
|
|
TopLevel::Fact(fact) =>
|
|
self.predicates.push(PredicateClause::Fact(fact)),
|
|
TopLevel::Rule(rule) =>
|
|
self.predicates.push(PredicateClause::Rule(rule)),
|
|
TopLevel::Predicate(pred) =>
|
|
self.predicates.extend(pred),
|
|
TopLevel::Declaration(decl) =>
|
|
return Ok(Some(decl)),
|
|
TopLevel::Query(_) =>
|
|
return Err(SessionError::QueryCannotBeDefinedAsFact),
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
pub(super)
|
|
fn load_decl(&mut self, decl: Declaration) -> Result<(), SessionError> {
|
|
match decl {
|
|
Declaration::Dynamic(name, arity) => {
|
|
self.add_dynamic_predicate(name, arity);
|
|
}
|
|
Declaration::MetaPredicate(module_name, name, meta_specs) => {
|
|
self.load_state.add_meta_predicate_record(
|
|
module_name,
|
|
name,
|
|
meta_specs,
|
|
);
|
|
}
|
|
Declaration::Module(module_decl) => {
|
|
self.load_state.compilation_target =
|
|
CompilationTarget::Module(module_decl.name.clone());
|
|
|
|
self.load_state.add_module(
|
|
module_decl,
|
|
self.term_stream.listing_src().clone(),
|
|
);
|
|
}
|
|
Declaration::NonCountedBacktracking(name, arity) => {
|
|
self.non_counted_bt_preds.insert((name, arity));
|
|
}
|
|
Declaration::Op(op_decl) => {
|
|
self.load_state.add_op_decl(&op_decl);
|
|
}
|
|
Declaration::UseModule(module_src) => {
|
|
self.load_state.use_module(module_src)?;
|
|
}
|
|
Declaration::UseQualifiedModule(module_src, exports) => {
|
|
self.load_state.use_qualified_module(module_src, exports)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(super)
|
|
fn read_term_from_heap(&self, heap_term_loc: RegType) -> Result<Term, SessionError> {
|
|
let machine_st = &self.load_state.wam.machine_st;
|
|
let term_addr = machine_st[heap_term_loc];
|
|
|
|
if machine_st.is_cyclic_term(term_addr) {
|
|
return Err(SessionError::from(CompilationError::CannotParseCyclicTerm));
|
|
}
|
|
|
|
let mut term_stack = vec![];
|
|
|
|
for addr in machine_st.post_order_iter(term_addr) {
|
|
match machine_st.heap.index_addr(&addr).as_ref() {
|
|
HeapCellValue::Addr(Addr::Lis(_)) |
|
|
HeapCellValue::Addr(Addr::PStrLocation(..)) => {
|
|
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),
|
|
));
|
|
}
|
|
HeapCellValue::Addr(addr) => {
|
|
if let Some(r) = addr.as_var() {
|
|
let offset_string =
|
|
match r {
|
|
Ref::HeapCell(h) | Ref::AttrVar(h) =>
|
|
format!("_{}", h),
|
|
Ref::StackCell(fr, sc) =>
|
|
format!("_s_{}_{}", fr, sc),
|
|
};
|
|
|
|
term_stack.push(Term::Var(
|
|
Cell::default(),
|
|
Rc::new(offset_string),
|
|
));
|
|
} else {
|
|
match addr.as_constant_index(machine_st) {
|
|
Some(constant) => {
|
|
term_stack.push(Term::Constant(Cell::default(), constant));
|
|
}
|
|
None => {
|
|
return Err(SessionError::from(CompilationError::UnreadableTerm));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
HeapCellValue::Atom(ref name, ref shared_op_desc) => {
|
|
term_stack.push(Term::Constant(
|
|
Cell::default(),
|
|
Constant::Atom(name.clone(), shared_op_desc.clone()),
|
|
));
|
|
}
|
|
HeapCellValue::Integer(ref integer) => {
|
|
term_stack.push(Term::Constant(
|
|
Cell::default(),
|
|
Constant::Integer(integer.clone()),
|
|
));
|
|
}
|
|
HeapCellValue::NamedStr(arity, ref name, ref shared_op_desc) => {
|
|
let subterms = term_stack.drain(term_stack.len() - arity ..)
|
|
.map(Box::new)
|
|
.collect();
|
|
|
|
term_stack.push(Term::Clause(
|
|
Cell::default(),
|
|
name.clone(),
|
|
subterms,
|
|
shared_op_desc.clone(),
|
|
));
|
|
}
|
|
HeapCellValue::PartialString(..) => {
|
|
let string = machine_st.heap_pstr_iter(addr).to_string();
|
|
term_stack.push(Term::Constant(
|
|
Cell::default(),
|
|
Constant::String(Rc::new(string)),
|
|
));
|
|
}
|
|
HeapCellValue::Rational(ref rational) => {
|
|
term_stack.push(Term::Constant(
|
|
Cell::default(),
|
|
Constant::Rational(rational.clone()),
|
|
));
|
|
}
|
|
_ => {
|
|
return Err(SessionError::from(CompilationError::UnreadableTerm));
|
|
}
|
|
}
|
|
}
|
|
|
|
debug_assert!(term_stack.len() == 1);
|
|
Ok(term_stack.pop().unwrap())
|
|
}
|
|
|
|
fn extract_module_export_list_from_heap(
|
|
&self,
|
|
r: RegType,
|
|
) -> Result<IndexSet<ModuleExport>, SessionError> {
|
|
let export_list = self.read_term_from_heap(r)?;
|
|
let atom_tbl = self.load_state.wam.machine_st.atom_tbl.clone();
|
|
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(_, turnstile, mut terms, _)
|
|
if turnstile.as_str() == ":-" && terms.len() == 2 => {
|
|
let body = *terms.pop().unwrap();
|
|
let head = *terms.pop().unwrap();
|
|
|
|
self.clause_clauses.push((head, body));
|
|
}
|
|
head @ Term::Constant(_, Constant::Atom(..)) |
|
|
head @ Term::Clause(..) => {
|
|
let body = Term::Constant(
|
|
Cell::default(),
|
|
Constant::Atom(clause_name!("true"), None),
|
|
);
|
|
|
|
self.clause_clauses.push((head, body));
|
|
}
|
|
_ => {
|
|
return Err(CompilationError::InadmissibleFact);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_dynamic_predicate(&mut self, name: ClauseName, arity: usize) {
|
|
let key = (name, arity);
|
|
|
|
match &self.load_state.compilation_target {
|
|
CompilationTarget::User => {
|
|
match self.load_state.wam.indices.extensible_predicates.get_mut(&key) {
|
|
Some(ref mut skeleton) => {
|
|
if !skeleton.is_dynamic {
|
|
skeleton.is_dynamic = true;
|
|
|
|
self.load_state.retraction_info.push_record(
|
|
RetractionRecord::AddedUserDynamicPredicate(key.clone())
|
|
);
|
|
}
|
|
}
|
|
None => {
|
|
self.load_state.wam.indices.extensible_predicates.insert(
|
|
key.clone(),
|
|
PredicateSkeleton::new().set_dynamic(true),
|
|
);
|
|
|
|
self.load_state.retraction_info.push_record(
|
|
RetractionRecord::AddedUserExtensiblePredicate(key.clone())
|
|
);
|
|
}
|
|
}
|
|
}
|
|
CompilationTarget::Module(ref module_name) => {
|
|
match self.load_state.wam.indices.modules.get_mut(module_name) {
|
|
Some(ref mut module) => {
|
|
match module.extensible_predicates.get_mut(&key) {
|
|
Some(ref mut skeleton) => {
|
|
if !skeleton.is_dynamic {
|
|
skeleton.is_dynamic = true;
|
|
|
|
self.load_state.retraction_info.push_record(
|
|
RetractionRecord::AddedModuleDynamicPredicate(
|
|
module_name.clone(),
|
|
key.clone(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
None => {
|
|
module.extensible_predicates.insert(
|
|
key.clone(),
|
|
PredicateSkeleton::new().set_dynamic(true),
|
|
);
|
|
|
|
self.load_state.retraction_info.push_record(
|
|
RetractionRecord::AddedModuleExtensiblePredicate(
|
|
module_name.clone(),
|
|
key.clone(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
unreachable!();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let code_index = self.load_state.get_or_insert_code_index(key.clone());
|
|
|
|
set_code_index(
|
|
&mut self.load_state.retraction_info,
|
|
&self.load_state.compilation_target,
|
|
key,
|
|
&code_index,
|
|
IndexPtr::DynamicUndefined,
|
|
);
|
|
}
|
|
}
|
|
|
|
impl Machine {
|
|
pub(crate)
|
|
fn use_module(&mut self) {
|
|
let subevacuable_addr =
|
|
self.machine_st.store(self.machine_st.deref(self.machine_st[temp_v!(2)]));
|
|
|
|
let module_src =
|
|
ModuleSource::Library(
|
|
match subevacuable_addr {
|
|
Addr::LoadStatePayload(payload) => {
|
|
match &self.machine_st.heap[payload] {
|
|
HeapCellValue::LoadStatePayload(payload) => {
|
|
match &payload.compilation_target {
|
|
CompilationTarget::Module(ref module_name) => {
|
|
module_name.clone()
|
|
}
|
|
CompilationTarget::User => {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
);
|
|
|
|
let (mut loader, evacuable_h) = 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.load_state.use_module(module_src)?;
|
|
} else {
|
|
loader.load_state.use_qualified_module(module_src, export_list)?;
|
|
}
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = use_module();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn load_compiled_library(&mut self) {
|
|
let library = atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(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, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(2));
|
|
|
|
let import_module = || {
|
|
loader.load_state.import_module(library)?;
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = import_module();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
} else {
|
|
self.machine_st.fail = true;
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn declare_module(&mut self) {
|
|
let module_name = atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(1)]
|
|
))
|
|
);
|
|
|
|
// let export_list = self.machine_st.extract_module_export_list(temp_v!(2));
|
|
let (mut loader, evacuable_h) = 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))?;
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = declare_module();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn add_dynamic_predicate(&mut self) {
|
|
let predicate_name = atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(1)]
|
|
))
|
|
);
|
|
|
|
let arity =
|
|
self.machine_st.store(self.machine_st.deref(self.machine_st[temp_v!(2)]));
|
|
|
|
let arity =
|
|
match Number::try_from((arity, &self.machine_st.heap)) {
|
|
Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY =>
|
|
Ok(n.to_usize().unwrap()),
|
|
Ok(Number::Fixnum(n)) if n >= 0 && n <= MAX_ARITY as isize =>
|
|
Ok(usize::try_from(n).unwrap()),
|
|
_ =>
|
|
Err(SessionError::from(CompilationError::InvalidRuleHead))
|
|
};
|
|
|
|
let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
|
|
|
|
let add_dynamic_predicate = || {
|
|
loader.add_dynamic_predicate(predicate_name, arity?);
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = add_dynamic_predicate();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn add_term_expansion_clause(&mut self) {
|
|
let (mut loader, evacuable_h) = 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(
|
|
(clause_name!("term_expansion"), 2),
|
|
term,
|
|
CompilationTarget::User,
|
|
false,
|
|
AppendOrPrepend::Append,
|
|
)?;
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = add_clause();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn add_goal_expansion_clause(&mut self) {
|
|
let target_module_name = atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(1)]
|
|
))
|
|
);
|
|
|
|
let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
|
|
|
|
let compilation_target =
|
|
match target_module_name.as_str() {
|
|
"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(
|
|
(clause_name!("goal_expansion"), 2),
|
|
term,
|
|
compilation_target,
|
|
false, // backtracking inferences are counted by call_with_inference_limit.
|
|
AppendOrPrepend::Append,
|
|
)?;
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = add_clause();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn loader_from_heap_evacuable(&mut self, r: RegType) -> (Loader<LiveTermStream>, usize) {
|
|
let (load_state_payload, evacuable_h) =
|
|
match self.machine_st.store(self.machine_st.deref(self.machine_st[r])) {
|
|
Addr::LoadStatePayload(h) => {
|
|
( mem::replace(
|
|
&mut self.machine_st.heap[h],
|
|
HeapCellValue::Addr(Addr::EmptyList),
|
|
),
|
|
h,
|
|
)
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
};
|
|
|
|
match load_state_payload {
|
|
HeapCellValue::LoadStatePayload(payload) => {
|
|
(Loader::from_load_state_payload(self, payload), evacuable_h)
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate)
|
|
fn push_load_state_payload(&mut self) {
|
|
let payload = LoadStatePayload::new(self);
|
|
let addr = Addr::LoadStatePayload(
|
|
self.machine_st.heap.push(HeapCellValue::LoadStatePayload(payload))
|
|
);
|
|
|
|
self.machine_st.bind(self.machine_st[temp_v!(1)].as_var().unwrap(), addr);
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate)
|
|
fn pop_load_state_payload(&mut self) {
|
|
let load_state_payload =
|
|
match self.machine_st.store(self.machine_st.deref(self.machine_st[temp_v!(1)])) {
|
|
Addr::LoadStatePayload(h) => {
|
|
mem::replace(
|
|
&mut self.machine_st.heap[h],
|
|
HeapCellValue::Addr(Addr::EmptyList),
|
|
)
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
};
|
|
|
|
match load_state_payload {
|
|
HeapCellValue::LoadStatePayload(payload) => {
|
|
Loader::from_load_state_payload(self, payload);
|
|
}
|
|
_ => {
|
|
// 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.
|
|
}
|
|
}
|
|
}
|
|
|
|
#[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[temp_v!(1)],
|
|
&self.indices,
|
|
"$push_load_context",
|
|
2,
|
|
)
|
|
);
|
|
|
|
let path =
|
|
atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(2)]
|
|
))
|
|
);
|
|
|
|
self.load_contexts.push(LoadContext::new(path.as_str(), stream));
|
|
}
|
|
|
|
pub(crate)
|
|
fn restore_load_state_payload(
|
|
&mut self,
|
|
result: Result<LoadStatePayload, SessionError>,
|
|
evacuable_h: usize,
|
|
) {
|
|
match result {
|
|
Ok(payload) => {
|
|
self.machine_st.heap[evacuable_h] = HeapCellValue::LoadStatePayload(
|
|
payload
|
|
);
|
|
}
|
|
Err(e) => {
|
|
self.throw_session_error(e, (clause_name!("load"), 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn clause_to_evacuable(&mut self) {
|
|
let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(2));
|
|
|
|
let enqueue_term = || {
|
|
let term = loader.read_term_from_heap(temp_v!(1))?;
|
|
|
|
if let Some(predicate_name) = ClauseInfo::name(&term) {
|
|
let arity = ClauseInfo::arity(&term);
|
|
|
|
let is_dynamic =
|
|
loader.load_state.wam.indices.get_predicate_skeleton(
|
|
&loader.load_state.compilation_target,
|
|
&(predicate_name, arity),
|
|
).map(|skeleton| skeleton.is_dynamic)
|
|
.unwrap_or(false);
|
|
|
|
if is_dynamic {
|
|
loader.add_clause_clause(term.clone())?;
|
|
}
|
|
}
|
|
|
|
loader.enqueue_term(term);
|
|
loader.load()
|
|
};
|
|
|
|
let result = enqueue_term();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
pub(crate)
|
|
fn conclude_load(&mut self) {
|
|
let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
|
|
|
|
let compile_final_terms = || {
|
|
if !loader.predicates.is_empty() {
|
|
loader.compile_and_submit()?;
|
|
}
|
|
|
|
loader.load_state.remove_module_op_exports();
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = compile_final_terms();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
|
|
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 =
|
|
clause_name!(path_str.to_string(), self.machine_st.atom_tbl);
|
|
|
|
let path_addr = Addr::Con(
|
|
self.machine_st.heap.push(HeapCellValue::Atom(path_atom, None))
|
|
);
|
|
|
|
self.machine_st.unify(path_addr, self.machine_st[temp_v!(1)]);
|
|
} else {
|
|
self.machine_st.fail = true;
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn load_context_file(&mut self) {
|
|
if let Some(load_context) = self.load_contexts.last() {
|
|
if let Some(file_name) = load_context.path.file_name() {
|
|
let file_name_str = file_name.to_str().unwrap();
|
|
let file_name_atom =
|
|
clause_name!(file_name_str.to_string(), self.machine_st.atom_tbl);
|
|
|
|
let file_name_addr = Addr::Con(
|
|
self.machine_st.heap.push(HeapCellValue::Atom(file_name_atom, None))
|
|
);
|
|
|
|
self.machine_st.unify(file_name_addr, self.machine_st[temp_v!(1)]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
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.ancestors().next() {
|
|
let directory_str = directory.to_str().unwrap();
|
|
let directory_atom =
|
|
clause_name!(directory_str.to_string(), self.machine_st.atom_tbl);
|
|
|
|
let directory_addr = Addr::Con(
|
|
self.machine_st.heap.push(HeapCellValue::Atom(directory_atom, None))
|
|
);
|
|
|
|
self.machine_st.unify(directory_addr, self.machine_st[temp_v!(1)]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.machine_st.fail = true;
|
|
}
|
|
|
|
pub(crate)
|
|
fn load_context_module(&mut self) {
|
|
if let Some(load_context) = self.load_contexts.last() {
|
|
let module_name_addr = Addr::Con(
|
|
self.machine_st.heap.push(HeapCellValue::Atom(
|
|
load_context.module.clone(),
|
|
None,
|
|
))
|
|
);
|
|
|
|
self.machine_st.unify(module_name_addr, self.machine_st[temp_v!(1)]);
|
|
} else {
|
|
self.machine_st.fail = true;
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn load_context_stream(&mut self) {
|
|
if let Some(load_context) = self.load_contexts.last() {
|
|
let stream_addr = Addr::Stream(
|
|
self.machine_st.heap.push(HeapCellValue::Stream(
|
|
load_context.stream.clone()
|
|
))
|
|
);
|
|
|
|
self.machine_st.unify(stream_addr, self.machine_st[temp_v!(1)]);
|
|
} else {
|
|
self.machine_st.fail = true;
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn compile_user_assert(&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 compile_user_assert = || {
|
|
let mut loader = Loader::new(LiveTermStream::new(ListingSource::User), self);
|
|
|
|
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(),
|
|
clause_name!(":-"),
|
|
vec![Box::new(head.clone()), Box::new(body.clone())],
|
|
fetch_op_spec(clause_name!(":-"), 2, &loader.load_state.wam.indices.op_dir),
|
|
);
|
|
|
|
loader.incremental_compile_clause(
|
|
key.clone(),
|
|
asserted_clause,
|
|
CompilationTarget::User,
|
|
false,
|
|
append_or_prepend,
|
|
)?;
|
|
|
|
// if a new predicate was just created, make it dynamic.
|
|
loader.load_state.wam.indices.get_predicate_skeleton(
|
|
&loader.load_state.compilation_target,
|
|
&key,
|
|
).map(|skeleton| skeleton.is_dynamic = true);
|
|
|
|
loader.compile_clause_clauses(key, std::iter::once((head, body)), append_or_prepend)?;
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
match compile_user_assert() {
|
|
Ok(_) => {
|
|
}
|
|
Err(e) => {
|
|
let error_pi =
|
|
match append_or_prepend {
|
|
AppendOrPrepend::Append => (clause_name!("assertz"), 1),
|
|
AppendOrPrepend::Prepend => (clause_name!("asserta"), 1),
|
|
};
|
|
|
|
self.throw_session_error(e, error_pi);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn retract_user_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, &self.machine_st.heap)) {
|
|
Ok(Number::Integer(n)) =>
|
|
n.to_usize().unwrap(),
|
|
Ok(Number::Fixnum(n)) =>
|
|
usize::try_from(n).unwrap(),
|
|
_ =>
|
|
unreachable!()
|
|
};
|
|
|
|
let retract_user_clause = || {
|
|
let mut loader = Loader::new(LiveTermStream::new(ListingSource::User), self);
|
|
let clause_clause_loc = loader.load_state.retract_clause(key, target_pos);
|
|
|
|
let clause_assert_margin =
|
|
loader.load_state.wam.indices.modules.get(&clause_name!("builtins"))
|
|
.map(|builtins| builtins.clause_assert_margin)
|
|
.unwrap();
|
|
|
|
let target_pos =
|
|
match loader.load_state.wam.indices.get_predicate_skeleton(
|
|
&CompilationTarget::Module(clause_name!("builtins")),
|
|
&(clause_name!("$clause"), 2),
|
|
) {
|
|
Some(skeleton) => {
|
|
let search_result =
|
|
skeleton.clause_clause_locs[0 .. clause_assert_margin]
|
|
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
|
|
|
|
let result =
|
|
search_result.unwrap_or_else(|_| {
|
|
skeleton.clause_clause_locs[clause_assert_margin ..]
|
|
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
|
|
.unwrap() + clause_assert_margin
|
|
});
|
|
|
|
if result < clause_assert_margin {
|
|
loader.load_state.wam.indices.modules.get_mut(&clause_name!("builtins"))
|
|
.map(|builtins| builtins.clause_assert_margin -= 1);
|
|
}
|
|
|
|
result
|
|
}
|
|
None => {
|
|
unreachable!();
|
|
}
|
|
};
|
|
|
|
|
|
let compilation_target = mem::replace(
|
|
&mut loader.load_state.compilation_target,
|
|
CompilationTarget::Module(clause_name!("builtins")),
|
|
);
|
|
|
|
loader.load_state.retract_clause((clause_name!("$clause"), 2), target_pos);
|
|
loader.load_state.compilation_target = compilation_target;
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
match retract_user_clause() {
|
|
Ok(_) => {
|
|
}
|
|
Err(e) => {
|
|
self.throw_session_error(e, (clause_name!("retract"), 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn meta_predicate_property(&mut self) {
|
|
let module_name = atom_from!(
|
|
self.machine_st,
|
|
self.machine_st.store(self.machine_st.deref(
|
|
self.machine_st[temp_v!(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.as_str() {
|
|
"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 = self.machine_st.heap.to_list(
|
|
meta_specs.iter().map(|meta_spec| {
|
|
match meta_spec {
|
|
MetaSpec::Minus => HeapCellValue::Atom(clause_name!("+"), None),
|
|
MetaSpec::Plus => HeapCellValue::Atom(clause_name!("-"), None),
|
|
MetaSpec::Either => HeapCellValue::Atom(clause_name!("?"), None),
|
|
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
|
|
HeapCellValue::Addr(Addr::Usize(*arg_num))
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
|
|
let heap_loc = self.machine_st.heap.push(
|
|
HeapCellValue::NamedStr(1, clause_name!("meta_predicate"), None),
|
|
);
|
|
|
|
self.machine_st.heap.push(HeapCellValue::Addr(Addr::HeapCell(list_loc)));
|
|
self.machine_st.unify(Addr::HeapCell(heap_loc), self.machine_st[temp_v!(4)]);
|
|
}
|
|
None => {
|
|
self.machine_st.fail = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate)
|
|
fn compile_pending_predicates(&mut self) {
|
|
let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
|
|
|
|
let compile_pending_predicates = || {
|
|
if !loader.predicates.is_empty() {
|
|
loader.compile_and_submit()?;
|
|
}
|
|
|
|
LiveTermStream::evacuate(loader)
|
|
};
|
|
|
|
let result = compile_pending_predicates();
|
|
self.restore_load_state_payload(result, evacuable_h);
|
|
}
|
|
}
|
|
|
|
impl<'a> Loader<'a, LiveTermStream> {
|
|
pub(super)
|
|
fn to_load_state_payload(mut self) -> LoadStatePayload {
|
|
LoadStatePayload {
|
|
term_stream:
|
|
mem::replace(
|
|
&mut self.term_stream,
|
|
LiveTermStream::new(ListingSource::User),
|
|
),
|
|
preprocessor:
|
|
mem::replace(
|
|
&mut self.preprocessor,
|
|
Preprocessor::new(self.load_state.wam.machine_st.flags),
|
|
),
|
|
non_counted_bt_preds:
|
|
mem::replace(
|
|
&mut self.non_counted_bt_preds,
|
|
IndexSet::new(),
|
|
),
|
|
compilation_target:
|
|
self.load_state.compilation_target.take(),
|
|
retraction_info:
|
|
mem::replace(
|
|
&mut self.load_state.retraction_info,
|
|
RetractionInfo::new(self.load_state.wam.code_repo.code.len()),
|
|
),
|
|
predicates:
|
|
mem::replace(
|
|
&mut self.predicates,
|
|
vec![],
|
|
),
|
|
clause_clauses:
|
|
mem::replace(
|
|
&mut self.clause_clauses,
|
|
vec![],
|
|
),
|
|
module_op_exports:
|
|
mem::replace(
|
|
&mut self.load_state.module_op_exports,
|
|
vec![],
|
|
),
|
|
}
|
|
}
|
|
|
|
pub(super)
|
|
fn from_load_state_payload(wam: &'a mut Machine, mut payload: LoadStatePayload) -> Self {
|
|
Loader {
|
|
term_stream:
|
|
mem::replace(
|
|
&mut payload.term_stream,
|
|
LiveTermStream::new(ListingSource::User),
|
|
),
|
|
preprocessor:
|
|
mem::replace(
|
|
&mut payload.preprocessor,
|
|
Preprocessor::new(MachineFlags::default()),
|
|
),
|
|
non_counted_bt_preds:
|
|
mem::replace(
|
|
&mut payload.non_counted_bt_preds,
|
|
IndexSet::new(),
|
|
),
|
|
clause_clauses:
|
|
mem::replace(
|
|
&mut payload.clause_clauses,
|
|
vec![],
|
|
),
|
|
predicates:
|
|
mem::replace(
|
|
&mut payload.predicates,
|
|
vec![],
|
|
),
|
|
load_state: LoadState {
|
|
compilation_target: payload.compilation_target.take(),
|
|
module_op_exports:
|
|
mem::replace(
|
|
&mut payload.module_op_exports,
|
|
vec![],
|
|
),
|
|
retraction_info:
|
|
mem::replace(
|
|
&mut payload.retraction_info,
|
|
RetractionInfo::new(0),
|
|
),
|
|
wam,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn incremental_compile_clause(
|
|
&mut self,
|
|
key: PredicateKey,
|
|
term: Term,
|
|
compilation_target: CompilationTarget,
|
|
non_counted_bt: bool,
|
|
append_or_prepend: AppendOrPrepend,
|
|
) -> Result<(), SessionError> {
|
|
let mut preprocessor = Preprocessor::new(self.load_state.wam.machine_st.flags);
|
|
|
|
let tl = preprocessor.try_term_to_tl(
|
|
&mut self.load_state,
|
|
term,
|
|
CutContext::BlocksCuts,
|
|
)?;
|
|
|
|
let queue = preprocessor.parse_queue(&mut self.load_state)?;
|
|
|
|
let clause =
|
|
match tl {
|
|
TopLevel::Fact(fact) => {
|
|
PredicateClause::Fact(fact)
|
|
}
|
|
TopLevel::Rule(rule) => {
|
|
PredicateClause::Rule(rule)
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
};
|
|
|
|
let compilation_target =
|
|
mem::replace(&mut self.load_state.compilation_target, compilation_target);
|
|
|
|
let result = self.load_state.incremental_compile_clause(
|
|
key,
|
|
clause,
|
|
queue,
|
|
non_counted_bt,
|
|
append_or_prepend,
|
|
);
|
|
|
|
self.load_state.compilation_target = compilation_target;
|
|
result?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[inline]
|
|
fn enqueue_term(&mut self, term: Term) {
|
|
self.term_stream.term_queue.push_back(term);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(super)
|
|
fn load_module(
|
|
code_dir: &mut CodeDir,
|
|
op_dir: &mut OpDir,
|
|
meta_predicate_dir: &mut MetaPredicateDir,
|
|
module: &Module,
|
|
) {
|
|
import_module_exports(
|
|
&mut RetractionInfo::new(0),
|
|
&CompilationTarget::User,
|
|
module,
|
|
code_dir,
|
|
op_dir,
|
|
meta_predicate_dir,
|
|
);
|
|
}
|