use crate::atom_table::*; use crate::parser::ast::*; use crate::forms::*; use crate::instructions::*; use crate::types::HeapCellValue; 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>, 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>, indexing_code: &'a mut Vec, 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>, indexing_code: &'a mut Vec, 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(HeapCellValue::from(*constant), constant_ptr); } _ if constant_ptr.is_external() => { // this must be a defunct clause, because it's been deleted // from the skeleton. debug_assert!(constant_key.is_none()); } _ => { 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: HeapCellValue, 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(constants)) => { constants.insert( constant, IndexingCodePtr::Internal(indexing_code_len - self.offset), ); } _ => { unreachable!() } } } fn add_dynamic_indexed_choice_for_constant( &mut self, external: usize, constant: HeapCellValue, 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(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(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(indexed_choice_instrs) => { uncap_choice_seq_with_try(indexed_choice_instrs.make_contiguous()); indexed_choice_instrs.push_front(IndexedChoiceInstruction::Try(index)); } IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) if self.append_or_prepend.is_append() => { indexed_choice_instrs.push_back(index); } IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => { indexed_choice_instrs.push_front(index); } _ => { unreachable!() } } } fn index_overlapping_constant( &mut self, orig_constant: HeapCellValue, overlapping_constant: HeapCellValue, index: usize, ) { loop { let indexing_code_len = self.indexing_code.len(); match &mut self.indexing_code[self.offset] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, 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: HeapCellValue, index: usize) { loop { let indexing_code_len = self.indexing_code.len(); match &mut self.indexing_code[self.offset] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, 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(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(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(_, _, _, _, 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(_, _, _, 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, skeleton: &mut [ClauseIndexInfo], // the clause to be merged is the last element in the skeleton. retracted_clauses: &Option>, 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, overlapping_constants) => { let offset = new_clause_loc - index_loc + 1; merging_ptr.index_constant(HeapCellValue::from(*constant), offset); if let Some(overlapping_constant) = overlapping_constants { merging_ptr.offset = 0; merging_ptr.index_overlapping_constant( HeapCellValue::from(*constant), HeapCellValue::from(*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: Option, indexing_code: &mut [IndexingLine], offset: usize, ) { let mut index = 0; let iter = once(&constant).chain(overlapping_constants.iter()); match &mut indexing_code[index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, 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.map(|l| HeapCellValue::from(*l)) { loop { match &mut indexing_code[index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => { constants_index = index; match constants.get(&constant).cloned() { Some(IndexingCodePtr::DynamicExternal(_)) | Some(IndexingCodePtr::External(_)) | Some(IndexingCodePtr::Fail) => { constants.swap_remove(&constant); break; } Some(IndexingCodePtr::Internal(o)) => { index += o; } None => { break; } } } IndexingLine::IndexedChoice(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( _, _, c, .., )) => { *c = ext; } IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( constants, )) => { constants.insert(constant, ext); } _ => { unreachable!() } } } } break; } IndexingLine::DynamicIndexedChoice(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( _, _, c, .., )) => { *c = ext; } IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( constants, )) => { constants.insert(constant, ext); } _ => { unreachable!() } } } } break; } _ => { unreachable!() } } } } match &indexing_code[constants_index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) if constants.is_empty() => { match &mut indexing_code[0] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => { *c = IndexingCodePtr::Fail; } _ => { unreachable!() } } } _ => {} } } pub(crate) fn remove_structure_index( name: Atom, arity: usize, indexing_code: &mut [IndexingLine], offset: usize, ) { let mut index = 0; match &mut indexing_code[index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, 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(structures)) => { structures_index = index; match structures.get(&(name, arity)).cloned() { Some(IndexingCodePtr::DynamicExternal(_)) | Some(IndexingCodePtr::External(_)) => { structures.swap_remove(&(name, arity)); break; } Some(IndexingCodePtr::Internal(o)) => { index += o; } Some(IndexingCodePtr::Fail) | None => { return; } } } IndexingLine::IndexedChoice(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( _, _, _, _, s, )) => { *s = ext; } IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( structures, )) => { structures.insert((name, arity), ext); } _ => { unreachable!() } } } } break; } IndexingLine::DynamicIndexedChoice(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( _, _, _, _, s, )) => { *s = ext; } IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( structures, )) => { structures.insert((name, arity), ext); } _ => { unreachable!() } } } } break; } _ => { unreachable!() } } } match &indexing_code[structures_index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) if structures.is_empty() => { match &mut indexing_code[0] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => { *s = IndexingCodePtr::Fail; } _ => { unreachable!() } } } _ => {} } } pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usize) { let mut index = 0; match &mut indexing_code[index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, 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(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( _, _, _, l, _, )) => { *l = ext; } _ => { unreachable!() } } } } } IndexingLine::DynamicIndexedChoice(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( _, _, _, l, _, )) => { *l = ext; } _ => { unreachable!() } } } } } _ => { unreachable!() } } } pub(crate) fn remove_index( opt_arg_index_key: &OptArgIndexKey, indexing_code: &mut [IndexingLine], clause_loc: usize, ) { match opt_arg_index_key { OptArgIndexKey::Literal(_, _, constant, 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]) { if let Some(instr) = prelude.first_mut() { *instr = IndexedChoiceInstruction::Try(instr.offset()); } cap_choice_seq_with_trust(prelude); } #[inline] fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) { if let Some(instr) = prelude.last_mut() { match instr { IndexedChoiceInstruction::Retry(i) => { *instr = IndexedChoiceInstruction::Trust(*i); } IndexedChoiceInstruction::DefaultRetry(i) => { *instr = IndexedChoiceInstruction::DefaultTrust(*i); } _ => {} } } } #[inline] fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) { if let Some(instr) = prelude.last_mut() { match instr { IndexedChoiceInstruction::Trust(i) => { *instr = IndexedChoiceInstruction::Retry(*i); } IndexedChoiceInstruction::DefaultTrust(i) => { *instr = IndexedChoiceInstruction::DefaultRetry(*i); } _ => {} } } } #[inline] fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) { if let Some(instr) = prelude.first_mut() { if let IndexedChoiceInstruction::Try(i) = instr { *instr = IndexedChoiceInstruction::Retry(*i); } } } pub(crate) fn constant_key_alternatives(constant: Literal) -> Option { let n = match &constant { Literal::Rational(n) if n.denominator().is_one() => n.numerator(), Literal::Integer(n) => n, _ => return None, }; Fixnum::build_with_checked(n).map(Literal::Fixnum).ok() } #[derive(Debug)] pub(crate) struct StaticCodeIndices { constants: IndexMap, FxBuildHasher>, lists: VecDeque, structures: IndexMap<(Atom, usize), VecDeque, FxBuildHasher>, } #[derive(Debug)] pub(crate) struct DynamicCodeIndices { constants: IndexMap, FxBuildHasher>, lists: VecDeque, structures: IndexMap<(Atom, usize), VecDeque, FxBuildHasher>, } pub(crate) trait Indexer { type ThirdLevelIndex; fn new() -> Self; fn constants( &mut self, ) -> &mut IndexMap, FxBuildHasher>; fn lists(&mut self) -> &mut VecDeque; fn structures( &mut self, ) -> &mut IndexMap<(Atom, usize), VecDeque, FxBuildHasher>; fn compute_index( is_initial_index: bool, index: usize, non_counted_bt: bool, ) -> Self::ThirdLevelIndex; fn second_level_index( indices: IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> IndexMap; fn switch_on( instr_fn: impl FnMut(IndexMap) -> IndexingInstruction, index: &mut IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> IndexingCodePtr; fn switch_on_list( lists: &mut VecDeque, prelude: &mut VecDeque, ) -> IndexingCodePtr; fn remove_instruction_with_offset(code: &mut VecDeque, 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, FxBuildHasher> { &mut self.constants } #[inline] fn lists(&mut self) -> &mut VecDeque { &mut self.lists } #[inline] fn structures( &mut self, ) -> &mut IndexMap<(Atom, usize), VecDeque, FxBuildHasher> { &mut self.structures } fn compute_index( is_initial_index: bool, index: usize, non_counted_bt: bool, ) -> IndexedChoiceInstruction { if is_initial_index { IndexedChoiceInstruction::Try(index + 1) } else if non_counted_bt { IndexedChoiceInstruction::DefaultRetry(index + 1) } else { IndexedChoiceInstruction::Retry(index + 1) } } fn second_level_index( indices: IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> IndexMap { 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 if let Some(i) = code.front() { index_locs.insert(key, IndexingCodePtr::External(i.offset())); } } index_locs } fn switch_on( mut instr_fn: impl FnMut( IndexMap, ) -> IndexingInstruction, index: &mut IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> 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, prelude: &mut VecDeque, ) -> IndexingCodePtr { if lists.len() > 1 { cap_choice_seq_with_trust(lists.make_contiguous()); let lists = std::mem::take(lists); 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, 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, FxBuildHasher> { &mut self.constants } #[inline] fn lists(&mut self) -> &mut VecDeque { &mut self.lists } #[inline] fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque, FxBuildHasher> { &mut self.structures } #[inline] fn compute_index(_: bool, index: usize, _: bool) -> usize { index + 1 } fn second_level_index( indices: IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> IndexMap { 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)); } else if let Some(i) = code.front() { index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i)); } } index_locs } fn switch_on( mut instr_fn: impl FnMut( IndexMap, ) -> IndexingInstruction, index: &mut IndexMap, FxBuildHasher>, prelude: &mut VecDeque, ) -> 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, prelude: &mut VecDeque, ) -> IndexingCodePtr { if lists.len() > 1 { let lists = std::mem::take(lists); prelude.push_back(IndexingLine::DynamicIndexedChoice(lists)); IndexingCodePtr::Internal(1) } else { lists .front() .map(|i| IndexingCodePtr::DynamicExternal(*i)) .unwrap_or(IndexingCodePtr::Fail) } } #[inline] fn remove_instruction_with_offset(code: &mut VecDeque, 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 { indices: I, optimal_index: usize, non_counted_bt: bool, } impl CodeOffsets { pub(crate) fn new(indices: I, optimal_index: usize, non_counted_bt: bool) -> Self { CodeOffsets { indices, optimal_index, non_counted_bt, } } 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.non_counted_bt); self.indices.lists().push_back(index); } fn index_constant(&mut self, constant: Literal, index: usize) -> Option { let overlapping_constant_opt = constant_key_alternatives(constant); let code = self .indices .constants() .entry(HeapCellValue::from(constant)) .or_default(); let is_initial_index = code.is_empty(); code.push_back(I::compute_index( is_initial_index, index, self.non_counted_bt, )); if let Some(constant) = overlapping_constant_opt.map(HeapCellValue::from) { let code = self.indices.constants().entry(constant).or_default(); let is_initial_index = code.is_empty(); let index = I::compute_index(is_initial_index, index, self.non_counted_bt); code.push_back(index); } overlapping_constant_opt } fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize { let code = self.indices.structures().entry((name, arity)).or_default(); let code_len = code.len(); let is_initial_index = code.is_empty(); code.push_back(I::compute_index( is_initial_index, index, self.non_counted_bt, )); code_len } pub(crate) fn index_term( &mut self, optimal_arg: &Term, index: usize, clause_index_info: &mut ClauseIndexInfo, ) { match optimal_arg { &Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => { clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0); self.index_list(index); } &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0); self.index_list(index); } &Term::Clause(_, name, ref terms) => { clause_index_info.opt_arg_index_key = OptArgIndexKey::Structure(self.optimal_index, 0, name, terms.len()); self.index_structure(name, terms.len(), index); } &Term::Literal(_, constant) => { let overlapping_constants = self.index_constant(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 { 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, ); if let IndexingCodePtr::Internal(i) = &mut str_loc { *i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize; } if let IndexingCodePtr::Internal(i) = &mut lst_loc { *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() } }