Files
scryer-prolog/src/indexing.rs
2022-05-04 21:54:46 -06:00

1556 lines
54 KiB
Rust

use crate::atom_table::*;
use crate::parser::ast::*;
use crate::forms::*;
use crate::instructions::*;
use fxhash::FxBuildHasher;
use indexmap::IndexMap;
use std::collections::VecDeque;
use std::hash::Hash;
use std::iter::once;
use std::mem;
#[derive(Debug, Clone, Copy)]
enum OptArgIndexKeyType {
Structure,
Literal,
// List,
}
impl OptArgIndexKey {
#[inline]
fn has_key_type(&self, key_type: OptArgIndexKeyType) -> bool {
match (self, key_type) {
(OptArgIndexKey::Literal(..), OptArgIndexKeyType::Literal)
| (OptArgIndexKey::Structure(..), OptArgIndexKeyType::Structure)
// | (OptArgIndexKey::List(..), OptArgIndexKeyType::List)
=> true,
_ => false,
}
}
}
#[inline]
fn search_skeleton_for_first_key_type<'a>(
skeleton: &'a [ClauseIndexInfo],
retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
key_type: OptArgIndexKeyType,
append_or_prepend: AppendOrPrepend,
) -> Option<&'a OptArgIndexKey> {
if append_or_prepend.is_append() {
for clause_index_info in skeleton.iter().rev() {
if clause_index_info.opt_arg_index_key.has_key_type(key_type) {
return Some(&clause_index_info.opt_arg_index_key);
}
}
} else {
for clause_index_info in skeleton.iter() {
if clause_index_info.opt_arg_index_key.has_key_type(key_type) {
return Some(&clause_index_info.opt_arg_index_key);
}
}
}
if let Some(retracted_clauses) = retracted_dynamic_clauses {
for clause_index_info in retracted_clauses.iter().rev() {
if clause_index_info.opt_arg_index_key.has_key_type(key_type) {
return Some(&clause_index_info.opt_arg_index_key);
}
}
}
None
}
struct IndexingCodeMergingPtr<'a> {
skeleton: &'a mut [ClauseIndexInfo],
retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
indexing_code: &'a mut Vec<IndexingLine>,
offset: usize,
append_or_prepend: AppendOrPrepend,
is_dynamic: bool,
}
impl<'a> IndexingCodeMergingPtr<'a> {
#[inline]
fn new(
skeleton: &'a mut [ClauseIndexInfo],
retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
indexing_code: &'a mut Vec<IndexingLine>,
append_or_prepend: AppendOrPrepend,
) -> Self {
let is_dynamic = match &indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => match v {
IndexingCodePtr::External(_) => false,
IndexingCodePtr::DynamicExternal(_) => true,
_ => unreachable!(),
},
_ => unreachable!(),
};
Self {
skeleton,
retracted_dynamic_clauses,
indexing_code,
offset: 0,
append_or_prepend,
is_dynamic,
}
}
fn internalize_constant(&mut self, constant_ptr: IndexingCodePtr) {
let constant_key = search_skeleton_for_first_key_type(
self.skeleton,
self.retracted_dynamic_clauses,
OptArgIndexKeyType::Literal,
self.append_or_prepend,
);
let mut constants = IndexMap::with_hasher(FxBuildHasher::default());
match constant_key {
Some(OptArgIndexKey::Literal(_, _, constant, _)) => {
constants.insert(*constant, constant_ptr);
}
_ => {
if let IndexingCodePtr::DynamicExternal(_) = constant_ptr {
// this must be a defunct clause, because it's been deleted
// from the skeleton.
} else {
unreachable!()
}
}
}
if let IndexingCodePtr::Internal(_) = constant_ptr {
let last_index = self.indexing_code.len();
self.indexing_code.push(IndexingLine::Indexing(
IndexingInstruction::SwitchOnConstant(constants),
));
self.indexing_code.swap(self.offset, last_index);
} else {
self.offset = self.indexing_code.len();
self.indexing_code.push(IndexingLine::Indexing(
IndexingInstruction::SwitchOnConstant(constants),
));
}
}
fn add_static_indexed_choice_for_constant(
&mut self,
external: usize,
constant: Literal,
index: usize,
) {
let third_level_index = if self.append_or_prepend.is_append() {
vec![
IndexedChoiceInstruction::Try(external),
IndexedChoiceInstruction::Trust(index)
].into()
} else {
vec![
IndexedChoiceInstruction::Try(index),
IndexedChoiceInstruction::Trust(external)
].into()
};
let indexing_code_len = self.indexing_code.len();
self.indexing_code.push(IndexingLine::IndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => {
constants.insert(
constant,
IndexingCodePtr::Internal(indexing_code_len - self.offset),
);
}
_ => {
unreachable!()
}
}
}
fn add_dynamic_indexed_choice_for_constant(
&mut self,
external: usize,
constant: Literal,
index: usize,
) {
let third_level_index = if self.append_or_prepend.is_append() {
vec![external, index].into()
} else {
vec![index, external].into()
};
let indexing_code_len = self.indexing_code.len();
self.indexing_code.push(IndexingLine::DynamicIndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => {
constants.insert(
constant,
IndexingCodePtr::Internal(indexing_code_len - self.offset),
);
}
_ => {
unreachable!()
}
}
}
fn extend_indexed_choice(&mut self, index: usize) {
match &mut self.indexing_code[self.offset] {
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs)
if self.append_or_prepend.is_append() =>
{
uncap_choice_seq_with_trust(indexed_choice_instrs.make_contiguous());
indexed_choice_instrs.push_back(IndexedChoiceInstruction::Trust(index));
}
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
uncap_choice_seq_with_try(indexed_choice_instrs.make_contiguous());
indexed_choice_instrs.push_front(IndexedChoiceInstruction::Try(index));
}
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs)
if self.append_or_prepend.is_append() =>
{
indexed_choice_instrs.push_back(index);
}
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
indexed_choice_instrs.push_front(index);
}
_ => {
unreachable!()
}
}
}
fn index_overlapping_constant(
&mut self,
orig_constant: Literal,
overlapping_constant: Literal,
index: usize,
) {
loop {
let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
match *c {
IndexingCodePtr::Fail if self.is_dynamic => {
*c = IndexingCodePtr::DynamicExternal(index);
break;
}
IndexingCodePtr::Fail => {
*c = IndexingCodePtr::External(index);
break;
}
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
let mut constants = IndexMap::with_hasher(FxBuildHasher::default());
constants.insert(orig_constant, *c);
*c = IndexingCodePtr::Internal(indexing_code_len);
self.indexing_code.push(IndexingLine::Indexing(
IndexingInstruction::SwitchOnConstant(constants),
));
self.offset = indexing_code_len;
}
IndexingCodePtr::Internal(o) => {
self.offset += o;
}
}
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
match constants.get(&overlapping_constant).cloned() {
None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
constants.insert(
overlapping_constant,
IndexingCodePtr::DynamicExternal(index),
);
}
None | Some(IndexingCodePtr::Fail) => {
constants.insert(
overlapping_constant,
IndexingCodePtr::External(index),
);
}
Some(IndexingCodePtr::DynamicExternal(o)) => {
self.add_dynamic_indexed_choice_for_constant(
o,
overlapping_constant,
index,
);
}
Some(IndexingCodePtr::External(o)) => {
self.add_static_indexed_choice_for_constant(
o,
overlapping_constant,
index,
);
}
Some(IndexingCodePtr::Internal(o)) => {
self.offset += o;
self.extend_indexed_choice(index);
}
}
break;
}
IndexingLine::IndexedChoice(_) | IndexingLine::DynamicIndexedChoice(_) => {
self.internalize_constant(IndexingCodePtr::Internal(
indexing_code_len - self.offset,
));
}
_ => {
unreachable!()
}
}
}
}
fn index_constant(&mut self, constant: Literal, index: usize) {
loop {
let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
match *c {
IndexingCodePtr::Fail if self.is_dynamic => {
*c = IndexingCodePtr::DynamicExternal(index);
break;
}
IndexingCodePtr::Fail => {
*c = IndexingCodePtr::External(index);
break;
}
IndexingCodePtr::External(o) => {
*c = IndexingCodePtr::Internal(indexing_code_len - self.offset);
self.internalize_constant(IndexingCodePtr::External(o));
}
IndexingCodePtr::DynamicExternal(o) => {
*c = IndexingCodePtr::Internal(indexing_code_len - self.offset);
self.internalize_constant(IndexingCodePtr::DynamicExternal(o));
}
IndexingCodePtr::Internal(o) => {
self.offset += o;
}
}
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
match constants.get(&constant).cloned() {
None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
constants.insert(
constant,
IndexingCodePtr::DynamicExternal(index),
);
}
None | Some(IndexingCodePtr::Fail) => {
constants.insert(
constant,
IndexingCodePtr::External(index),
);
}
Some(IndexingCodePtr::DynamicExternal(o)) => {
self.add_dynamic_indexed_choice_for_constant(o, constant, index);
}
Some(IndexingCodePtr::External(o)) => {
self.add_static_indexed_choice_for_constant(o, constant, index);
}
Some(IndexingCodePtr::Internal(o)) => {
self.offset += o;
self.extend_indexed_choice(index);
}
}
break;
}
IndexingLine::IndexedChoice(_) | IndexingLine::DynamicIndexedChoice(_) => {
self.internalize_constant(IndexingCodePtr::Internal(
indexing_code_len - self.offset,
));
}
_ => {
unreachable!()
}
}
}
}
fn internalize_structure(&mut self, structure_ptr: IndexingCodePtr) {
let structure_key = search_skeleton_for_first_key_type(
self.skeleton,
self.retracted_dynamic_clauses,
OptArgIndexKeyType::Structure,
self.append_or_prepend,
);
let mut structures = IndexMap::with_hasher(FxBuildHasher::default());
match structure_key {
Some(OptArgIndexKey::Structure(_, _, name, arity)) => {
structures.insert((*name, *arity), structure_ptr);
}
_ => {
if let IndexingCodePtr::DynamicExternal(_) = structure_ptr {
// this must be a defunct clause, because it's been deleted
// from the skeleton.
} else {
unreachable!()
}
}
}
if let IndexingCodePtr::Internal(_) = structure_ptr {
let last_index = self.indexing_code.len();
self.indexing_code.push(IndexingLine::Indexing(
IndexingInstruction::SwitchOnStructure(structures),
));
self.indexing_code.swap(self.offset, last_index);
} else {
self.offset = self.indexing_code.len();
self.indexing_code.push(IndexingLine::Indexing(
IndexingInstruction::SwitchOnStructure(structures),
));
}
}
fn add_static_indexed_choice_for_structure(
&mut self,
external: usize,
key: PredicateKey,
index: usize,
) {
let third_level_index = if self.append_or_prepend.is_append() {
vec![
IndexedChoiceInstruction::Try(external),
IndexedChoiceInstruction::Trust(index)
].into()
} else {
vec![
IndexedChoiceInstruction::Try(index),
IndexedChoiceInstruction::Trust(external)
].into()
};
let indexing_code_len = self.indexing_code.len();
self.indexing_code.push(IndexingLine::IndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
structures.insert(
key,
IndexingCodePtr::Internal(indexing_code_len - self.offset),
);
}
_ => {
unreachable!()
}
}
}
fn add_dynamic_indexed_choice_for_structure(
&mut self,
external: usize,
key: PredicateKey,
index: usize,
) {
let third_level_index = if self.append_or_prepend.is_append() {
vec![external, index].into()
} else {
vec![index, external].into()
};
let indexing_code_len = self.indexing_code.len();
self.indexing_code.push(IndexingLine::DynamicIndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
structures.insert(
key,
IndexingCodePtr::Internal(indexing_code_len - self.offset),
);
}
_ => {
unreachable!()
}
}
}
fn index_structure(&mut self, key: PredicateKey, index: usize) {
loop {
let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
_,
ref mut s,
)) => match *s {
IndexingCodePtr::Fail if self.is_dynamic => {
*s = IndexingCodePtr::DynamicExternal(index);
break;
}
IndexingCodePtr::Fail => {
*s = IndexingCodePtr::External(index);
break;
}
IndexingCodePtr::DynamicExternal(o) => {
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
self.internalize_structure(IndexingCodePtr::DynamicExternal(o));
}
IndexingCodePtr::External(o) => {
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
self.internalize_structure(IndexingCodePtr::External(o));
}
IndexingCodePtr::Internal(o) => {
self.offset += o;
}
},
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
match structures.get(&key).cloned() {
None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
structures.insert(key, IndexingCodePtr::DynamicExternal(index));
}
None | Some(IndexingCodePtr::Fail) => {
structures.insert(key, IndexingCodePtr::External(index));
}
Some(IndexingCodePtr::DynamicExternal(o)) => {
self.add_dynamic_indexed_choice_for_structure(o, key, index);
}
Some(IndexingCodePtr::External(o)) => {
self.add_static_indexed_choice_for_structure(o, key, index);
}
Some(IndexingCodePtr::Internal(o)) => {
self.offset += o;
self.extend_indexed_choice(index);
}
}
break;
}
IndexingLine::IndexedChoice(_) | IndexingLine::DynamicIndexedChoice(_) => {
// replace this value, at self.offset, with
// SwitchOnStructures, and swap this IndexedChoice
// vector to the end of self.indexing_code.
self.internalize_structure(IndexingCodePtr::Internal(
indexing_code_len - self.offset,
));
}
_ => {
unreachable!()
}
}
}
}
fn index_list(&mut self, index: usize) {
let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => {
match *l {
IndexingCodePtr::Fail if self.is_dynamic => {
*l = IndexingCodePtr::DynamicExternal(index);
}
IndexingCodePtr::Fail => {
*l = IndexingCodePtr::External(index);
}
IndexingCodePtr::DynamicExternal(o) => {
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
let third_level_index = if self.append_or_prepend.is_append() {
vec![o, index].into()
} else {
vec![index, o].into()
};
self.indexing_code
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
}
IndexingCodePtr::External(o) => {
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
let third_level_index = if self.append_or_prepend.is_append() {
vec![
IndexedChoiceInstruction::Try(o),
IndexedChoiceInstruction::Trust(index)
].into()
} else {
vec![
IndexedChoiceInstruction::Try(index),
IndexedChoiceInstruction::Trust(o)
].into()
};
self.indexing_code
.push(IndexingLine::IndexedChoice(third_level_index));
}
IndexingCodePtr::Internal(o) => {
self.offset += o;
self.extend_indexed_choice(index);
}
}
}
_ => {
unreachable!()
}
}
}
}
pub(crate) fn merge_clause_index(
target_indexing_code: &mut Vec<IndexingLine>,
skeleton: &mut [ClauseIndexInfo], // the clause to be merged is the last element in the skeleton.
retracted_clauses: &Option<Vec<ClauseIndexInfo>>,
new_clause_loc: usize, // the absolute location of the new clause in the code vector.
append_or_prepend: AppendOrPrepend,
) {
let opt_arg_index_key = match append_or_prepend {
AppendOrPrepend::Append => skeleton.last_mut().unwrap().opt_arg_index_key.take(),
AppendOrPrepend::Prepend => skeleton.first_mut().unwrap().opt_arg_index_key.take(),
};
let mut merging_ptr = IndexingCodeMergingPtr::new(
skeleton,
retracted_clauses,
target_indexing_code,
append_or_prepend,
);
match &opt_arg_index_key {
OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => {
let offset = new_clause_loc - index_loc + 1;
merging_ptr.index_constant(*constant, offset);
for overlapping_constant in overlapping_constants {
merging_ptr.offset = 0;
merging_ptr.index_overlapping_constant(
*constant,
*overlapping_constant,
offset,
);
}
}
OptArgIndexKey::Structure(_, index_loc, name, arity) => {
merging_ptr.index_structure((*name, *arity), new_clause_loc - index_loc + 1);
}
OptArgIndexKey::List(_, index_loc) => {
merging_ptr.index_list(new_clause_loc - index_loc + 1);
}
OptArgIndexKey::None => {
unreachable!()
}
}
match append_or_prepend {
AppendOrPrepend::Append => {
skeleton.last_mut().unwrap().opt_arg_index_key = opt_arg_index_key;
}
AppendOrPrepend::Prepend => {
skeleton.first_mut().unwrap().opt_arg_index_key = opt_arg_index_key;
}
}
}
pub(crate) fn remove_constant_indices(
constant: Literal,
overlapping_constants: &[Literal],
indexing_code: &mut Vec<IndexingLine>,
offset: usize,
) {
let mut index = 0;
let iter = once(&constant).chain(overlapping_constants.iter());
match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
match *c {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*c = IndexingCodePtr::Fail;
return;
}
IndexingCodePtr::Internal(o) => {
index += o;
}
IndexingCodePtr::Fail => {
return;
}
}
}
_ => {
unreachable!()
}
}
let mut constants_index = 0;
for constant in iter {
loop {
match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
ref mut constants,
)) => {
constants_index = index;
let constant = *constant;
match constants.get(&constant).cloned() {
Some(IndexingCodePtr::DynamicExternal(_))
| Some(IndexingCodePtr::External(_))
| Some(IndexingCodePtr::Fail) => {
constants.remove(&constant);
break;
}
Some(IndexingCodePtr::Internal(o)) => {
index += o;
}
None => {
break;
}
}
}
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset(
indexed_choice_instrs,
offset,
);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::External(indexed_choice_instr.offset());
match &mut indexing_code[constants_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
ref mut c,
..,
)) => {
*c = ext;
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
ref mut constants,
)) => {
constants.insert(*constant, ext);
}
_ => {
unreachable!()
}
}
}
}
break;
}
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset(
indexed_choice_instrs,
offset,
);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::DynamicExternal(indexed_choice_instr);
match &mut indexing_code[constants_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
ref mut c,
..,
)) => {
*c = ext;
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
ref mut constants,
)) => {
constants.insert(*constant, ext);
}
_ => {
unreachable!()
}
}
}
}
break;
}
_ => {
unreachable!()
}
}
}
}
match &indexing_code[constants_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref constants))
if constants.is_empty() =>
{
match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
*c = IndexingCodePtr::Fail;
}
_ => {
unreachable!()
}
}
}
_ => {}
}
}
pub(crate) fn remove_structure_index(
name: Atom,
arity: usize,
indexing_code: &mut Vec<IndexingLine>,
offset: usize,
) {
let mut index = 0;
match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, ref mut s)) => {
match *s {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*s = IndexingCodePtr::Fail;
return;
}
IndexingCodePtr::Internal(o) => {
index += o;
}
IndexingCodePtr::Fail => {
return;
}
}
}
_ => {
unreachable!()
}
}
let mut structures_index = 0;
loop {
match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
structures_index = index;
match structures.get(&(name.clone(), arity)).cloned() {
Some(IndexingCodePtr::DynamicExternal(_))
| Some(IndexingCodePtr::External(_)) => {
structures.remove(&(name.clone(), arity));
break;
}
Some(IndexingCodePtr::Internal(o)) => {
index += o;
}
Some(IndexingCodePtr::Fail) | None => {
return;
}
}
}
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::External(indexed_choice_instr.offset());
match &mut indexing_code[structures_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
_,
ref mut s,
)) => {
*s = ext;
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures,
)) => {
structures.insert((name.clone(), arity), ext);
}
_ => {
unreachable!()
}
}
}
}
break;
}
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::DynamicExternal(indexed_choice_instr);
match &mut indexing_code[structures_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
_,
ref mut s,
)) => {
*s = ext;
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures,
)) => {
structures.insert((name.clone(), arity), ext);
}
_ => {
unreachable!()
}
}
}
}
break;
}
_ => {
unreachable!()
}
}
}
match &indexing_code[structures_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref structures))
if structures.is_empty() =>
{
match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
_,
ref mut s,
)) => {
*s = IndexingCodePtr::Fail;
}
_ => {
unreachable!()
}
}
}
_ => {}
}
}
pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: usize) {
let mut index = 0;
match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => {
match *l {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*l = IndexingCodePtr::Fail;
return;
}
IndexingCodePtr::Internal(o) => {
index += o;
}
IndexingCodePtr::Fail => {
return;
}
}
}
_ => {
unreachable!()
}
}
match &mut indexing_code[index] {
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::External(indexed_choice_instr.offset());
match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
ref mut l,
_,
)) => {
*l = ext;
}
_ => {
unreachable!()
}
}
}
}
}
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 {
if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
let ext = IndexingCodePtr::DynamicExternal(indexed_choice_instr);
match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_,
_,
_,
ref mut l,
_,
)) => {
*l = ext;
}
_ => {
unreachable!()
}
}
}
}
}
_ => {
unreachable!()
}
}
}
pub(crate) fn remove_index(
opt_arg_index_key: &OptArgIndexKey,
indexing_code: &mut Vec<IndexingLine>,
clause_loc: usize,
) {
match opt_arg_index_key {
OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => {
remove_constant_indices(*constant, overlapping_constants, indexing_code, clause_loc);
}
OptArgIndexKey::Structure(_, _, name, arity) => {
remove_structure_index(*name, *arity, indexing_code, clause_loc);
}
OptArgIndexKey::List(..) => {
remove_list_index(indexing_code, clause_loc);
}
OptArgIndexKey::None => {
unreachable!()
}
}
}
#[inline]
fn cap_choice_seq(prelude: &mut [IndexedChoiceInstruction]) {
prelude.first_mut().map(|instr| {
*instr = IndexedChoiceInstruction::Try(instr.offset());
});
cap_choice_seq_with_trust(prelude);
}
#[inline]
fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
prelude.last_mut().map(|instr| {
if let IndexedChoiceInstruction::Retry(i) = instr {
*instr = IndexedChoiceInstruction::Trust(*i);
}
});
}
#[inline]
fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
prelude.last_mut().map(|instr| {
if let IndexedChoiceInstruction::Trust(i) = instr {
*instr = IndexedChoiceInstruction::Retry(*i);
}
});
}
#[inline]
fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
prelude.first_mut().map(|instr| {
if let IndexedChoiceInstruction::Try(i) = instr {
*instr = IndexedChoiceInstruction::Retry(*i);
}
});
}
pub(crate) fn constant_key_alternatives(
constant: Literal,
atom_tbl: &mut AtomTable,
// arena: &mut Arena,
) -> Vec<Literal> {
let mut constants = vec![];
match constant {
Literal::Atom(ref name) => {
if let Some(c) = name.as_char() {
constants.push(Literal::Char(c));
}
}
Literal::Char(c) => {
let atom = atom_tbl.build_with(&c.to_string());
constants.push(Literal::Atom(atom));
}
/*
Literal::Fixnum(ref n) => {
constants.push(Literal::Integer(arena_alloc!(n, arena))); //Rc::new(Integer::from(*n))));
/*
if *n >= 0 {
if let Ok(n) = usize::try_from(*n) {
constants.push(Literal::Usize(n));
}
}
*/
}
*/
Literal::Integer(ref n) => {
if let Some(n) = n.to_isize() {
Fixnum::build_with_checked(n as i64).map(|n| {
constants.push(Literal::Fixnum(n));
}).unwrap();
}
}
_ => {}
}
constants
}
#[derive(Debug)]
pub(crate) struct StaticCodeIndices {
constants: IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
lists: VecDeque<IndexedChoiceInstruction>,
structures: IndexMap<(Atom, usize), VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
}
#[derive(Debug)]
pub(crate) struct DynamicCodeIndices {
constants: IndexMap<Literal, VecDeque<usize>, FxBuildHasher>,
lists: VecDeque<usize>,
structures: IndexMap<(Atom, usize), VecDeque<usize>, FxBuildHasher>,
}
pub(crate) trait Indexer {
type ThirdLevelIndex;
fn new() -> Self;
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
fn lists(&mut self) -> &mut VecDeque<Self::ThirdLevelIndex>;
fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
fn compute_index(is_initial_index: bool, index: usize) -> Self::ThirdLevelIndex;
fn second_level_index<IndexKey: Eq + Hash>(
indices: IndexMap<IndexKey, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>;
fn switch_on<IndexKey: Eq + Hash>(
instr_fn: impl FnMut(IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>) -> IndexingInstruction,
index: &mut IndexMap<IndexKey, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr;
fn switch_on_list(
lists: &mut VecDeque<Self::ThirdLevelIndex>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr;
fn remove_instruction_with_offset(code: &mut VecDeque<Self::ThirdLevelIndex>, offset: usize);
fn var_offset_wrapper(var_offset: usize) -> IndexingCodePtr;
}
impl Indexer for StaticCodeIndices {
type ThirdLevelIndex = IndexedChoiceInstruction;
#[inline]
fn new() -> Self {
Self {
constants: IndexMap::with_hasher(FxBuildHasher::default()),
lists: VecDeque::new(),
structures: IndexMap::with_hasher(FxBuildHasher::default()),
}
}
#[inline]
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
&mut self.constants
}
#[inline]
fn lists(&mut self) -> &mut VecDeque<IndexedChoiceInstruction> {
&mut self.lists
}
#[inline]
fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
&mut self.structures
}
fn compute_index(is_initial_index: bool, index: usize) -> IndexedChoiceInstruction {
if is_initial_index {
IndexedChoiceInstruction::Try(index + 1)
} else {
IndexedChoiceInstruction::Retry(index + 1)
}
}
fn second_level_index<IndexKey: Eq + Hash>(
indices: IndexMap<IndexKey, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher> {
let mut index_locs = IndexMap::with_hasher(FxBuildHasher::default());
for (key, mut code) in indices.into_iter() {
if code.len() > 1 {
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
cap_choice_seq_with_trust(code.make_contiguous());
prelude.push_back(IndexingLine::from(code));
} else {
code.front().map(|i| {
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
});
}
}
index_locs
}
fn switch_on<IndexKey: Eq + Hash>(
mut instr_fn: impl FnMut(IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>) -> IndexingInstruction,
index: &mut IndexMap<IndexKey, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr {
let index = mem::replace(index, IndexMap::with_hasher(FxBuildHasher::default()));
let index = Self::second_level_index(index, prelude);
if index.len() > 1 {
let instr = instr_fn(index);
prelude.push_front(IndexingLine::from(instr));
IndexingCodePtr::Internal(1)
} else {
index
.into_iter()
.next()
.map(|(_, v)| v)
.unwrap_or(IndexingCodePtr::Fail)
}
}
fn switch_on_list(
lists: &mut VecDeque<IndexedChoiceInstruction>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr {
if lists.len() > 1 {
cap_choice_seq_with_trust(lists.make_contiguous());
let lists = mem::replace(lists, VecDeque::new());
prelude.push_back(IndexingLine::from(lists));
IndexingCodePtr::Internal(1)
} else {
lists
.front()
.map(|i| IndexingCodePtr::External(i.offset()))
.unwrap_or(IndexingCodePtr::Fail)
}
}
#[inline]
fn remove_instruction_with_offset(
code: &mut VecDeque<IndexedChoiceInstruction>,
offset: usize,
) {
for (index, line) in code.iter().enumerate() {
if offset == line.offset() {
code.remove(index);
cap_choice_seq(code.make_contiguous());
return;
}
}
}
#[inline]
fn var_offset_wrapper(var_offset: usize) -> IndexingCodePtr {
IndexingCodePtr::External(var_offset)
}
}
impl Indexer for DynamicCodeIndices {
type ThirdLevelIndex = usize;
#[inline]
fn new() -> Self {
Self {
constants: IndexMap::with_hasher(FxBuildHasher::default()),
lists: VecDeque::new(),
structures: IndexMap::with_hasher(FxBuildHasher::default()),
}
}
#[inline]
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<usize>, FxBuildHasher> {
&mut self.constants
}
#[inline]
fn lists(&mut self) -> &mut VecDeque<usize> {
&mut self.lists
}
#[inline]
fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque<usize>, FxBuildHasher> {
&mut self.structures
}
#[inline]
fn compute_index(_: bool, index: usize) -> usize {
index + 1
}
fn second_level_index<IndexKey: Eq + Hash>(
indices: IndexMap<IndexKey, VecDeque<usize>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher> {
let mut index_locs = IndexMap::with_hasher(FxBuildHasher::default());
for (key, code) in indices.into_iter() {
if code.len() > 1 {
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
prelude.push_back(IndexingLine::DynamicIndexedChoice(code.into_iter().collect()));
} else {
code.front().map(|i| {
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
});
}
}
index_locs
}
fn switch_on<IndexKey: Eq + Hash>(
mut instr_fn: impl FnMut(IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>) -> IndexingInstruction,
index: &mut IndexMap<IndexKey, VecDeque<usize>, FxBuildHasher>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr {
let index = mem::replace(index, IndexMap::with_hasher(FxBuildHasher::default()));
let index = Self::second_level_index(index, prelude);
if index.len() > 1 {
let instr = instr_fn(index);
prelude.push_front(IndexingLine::from(instr));
IndexingCodePtr::Internal(1)
} else {
index
.into_iter()
.next()
.map(|(_, v)| v)
.unwrap_or(IndexingCodePtr::Fail)
}
}
fn switch_on_list(
lists: &mut VecDeque<usize>,
prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr {
if lists.len() > 1 {
let lists = mem::replace(lists, VecDeque::new());
prelude.push_back(IndexingLine::DynamicIndexedChoice(lists.into_iter().collect()));
IndexingCodePtr::Internal(1)
} else {
lists
.front()
.map(|i| IndexingCodePtr::DynamicExternal(*i))
.unwrap_or(IndexingCodePtr::Fail)
}
}
#[inline]
fn remove_instruction_with_offset(code: &mut VecDeque<usize>, offset: usize) {
for (index, line) in code.iter().enumerate() {
if offset == *line {
code.remove(index);
return;
}
}
}
#[inline]
fn var_offset_wrapper(var_offset: usize) -> IndexingCodePtr {
IndexingCodePtr::DynamicExternal(var_offset)
}
}
#[derive(Debug)]
pub(crate) struct CodeOffsets<I: Indexer> {
indices: I,
optimal_index: usize,
}
impl<I: Indexer> CodeOffsets<I> {
pub(crate) fn new(indices: I, optimal_index: usize) -> Self {
CodeOffsets {
indices,
optimal_index,
}
}
fn index_list(&mut self, index: usize) {
let is_initial_index = self.indices.lists().is_empty();
let index = I::compute_index(is_initial_index, index);
self.indices.lists().push_back(index);
}
fn index_constant(
&mut self,
atom_tbl: &mut AtomTable,
constant: Literal,
index: usize,
) -> Vec<Literal> {
let overlapping_constants = constant_key_alternatives(constant, atom_tbl);
let code = self.indices.constants().entry(constant).or_insert(VecDeque::new());
let is_initial_index = code.is_empty();
code.push_back(I::compute_index(is_initial_index, index));
for constant in &overlapping_constants {
let code = self
.indices
.constants()
.entry(*constant)
.or_insert(VecDeque::new());
let is_initial_index = code.is_empty();
let index = I::compute_index(is_initial_index, index);
code.push_back(index);
}
overlapping_constants
}
fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
let code = self
.indices
.structures()
.entry((name.clone(), arity))
.or_insert(VecDeque::new());
let code_len = code.len();
let is_initial_index = code.is_empty();
code.push_back(I::compute_index(is_initial_index, index));
code_len
}
pub(crate) fn index_term(
&mut self,
optimal_arg: &Term,
index: usize,
clause_index_info: &mut ClauseIndexInfo,
atom_tbl: &mut AtomTable,
) {
match optimal_arg {
&Term::Clause(_, name, ref terms) => {
clause_index_info.opt_arg_index_key =
OptArgIndexKey::Structure(self.optimal_index, 0, name.clone(), terms.len());
self.index_structure(name, terms.len(), index);
}
&Term::Cons(..) | &Term::Literal(_, Literal::String(_)) | &Term::PartialString(..) => {
clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0);
self.index_list(index);
}
&Term::Literal(_, constant) => {
let overlapping_constants = self.index_constant(atom_tbl, constant, index);
clause_index_info.opt_arg_index_key =
OptArgIndexKey::Literal(self.optimal_index, 0, constant, overlapping_constants);
}
_ => {}
}
}
pub(crate) fn no_indices(&mut self) -> bool {
let no_constants = self.indices.constants().is_empty();
let no_structures = self.indices.structures().is_empty();
let no_lists = self.indices.lists().is_empty();
no_constants && no_structures && no_lists
}
pub(crate) fn compute_indices(mut self, skip_stub_try_me_else: bool) -> Vec<IndexingLine> {
if self.no_indices() {
return vec![];
}
let mut prelude = VecDeque::new();
let mut emitted_switch_on_structure = false;
let mut emitted_switch_on_constant = false;
let mut lst_loc = I::switch_on_list(self.indices.lists(), &mut prelude);
let mut str_loc = I::switch_on(
|index| {
emitted_switch_on_structure = true;
IndexingInstruction::SwitchOnStructure(index)
},
self.indices.structures(),
&mut prelude,
);
let con_loc = I::switch_on(
|index| {
emitted_switch_on_constant = true;
IndexingInstruction::SwitchOnConstant(index)
},
self.indices.constants(),
&mut prelude,
);
match &mut str_loc {
IndexingCodePtr::Internal(ref mut i) => {
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
}
_ => {}
};
match &mut lst_loc {
IndexingCodePtr::Internal(ref mut i) => {
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
*i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize;
}
_ => {}
};
let var_offset = 1 + skip_stub_try_me_else as usize;
prelude.push_front(IndexingLine::from(IndexingInstruction::SwitchOnTerm(
self.optimal_index,
I::var_offset_wrapper(var_offset),
con_loc,
lst_loc,
str_loc,
)));
prelude.into_iter().collect()
}
}