use logicaffeine_base::Symbol;
use std::fmt;
pub use logicaffeine_lexicon::types::{Gender, Number, Case};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimeRelation {
Precedes,
Equals,
}
#[derive(Debug, Clone)]
pub struct TimeConstraint {
pub left: String,
pub relation: TimeRelation,
pub right: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OwnershipState {
#[default]
Owned,
Moved,
Borrowed,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScopeError {
InaccessibleReferent {
gender: Gender,
blocking_scope: BoxType,
reason: String,
},
NoMatchingReferent {
gender: Gender,
number: Number,
},
}
impl fmt::Display for ScopeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ScopeError::InaccessibleReferent { gender, blocking_scope, reason } => {
write!(f, "Cannot resolve {:?} pronoun: referent is trapped in {:?} scope. {}",
gender, blocking_scope, reason)
}
ScopeError::NoMatchingReferent { gender, number } => {
write!(f, "Cannot resolve {:?} {:?} pronoun: no matching referent in accessible scope",
gender, number)
}
}
}
}
impl std::error::Error for ScopeError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScopePath {
QuantifierBody,
ImplicationRight,
ConjunctionRight,
}
#[derive(Debug, Clone)]
pub struct TelescopeCandidate {
pub variable: Symbol,
pub noun_class: Symbol,
pub gender: Gender,
pub origin_box: usize,
pub scope_path: Vec<ScopePath>,
pub in_modal_scope: bool,
}
#[derive(Debug, Clone)]
pub struct ModalContext {
pub active: bool,
pub is_epistemic: bool,
pub force: f32,
}
#[derive(Debug, Clone)]
pub struct WorldState {
pub drs: Drs,
event_counter: usize,
event_history: Vec<String>,
reference_time_counter: usize,
current_reference_time: Option<String>,
time_constraints: Vec<TimeConstraint>,
telescope_candidates: Vec<TelescopeCandidate>,
discourse_mode: bool,
current_modal_context: Option<ModalContext>,
prior_modal_context: Option<ModalContext>,
}
impl WorldState {
pub fn new() -> Self {
Self {
drs: Drs::new(),
event_counter: 0,
event_history: Vec::new(),
reference_time_counter: 0,
current_reference_time: None,
time_constraints: Vec::new(),
telescope_candidates: Vec::new(),
discourse_mode: false,
current_modal_context: None,
prior_modal_context: None,
}
}
pub fn next_event_var(&mut self) -> String {
self.event_counter += 1;
let var = format!("e{}", self.event_counter);
self.event_history.push(var.clone());
var
}
pub fn event_history(&self) -> &[String] {
&self.event_history
}
pub fn next_reference_time(&mut self) -> String {
self.reference_time_counter += 1;
let var = format!("r{}", self.reference_time_counter);
self.current_reference_time = Some(var.clone());
var
}
pub fn current_reference_time(&self) -> String {
self.current_reference_time.clone().unwrap_or_else(|| "S".to_string())
}
pub fn add_time_constraint(&mut self, left: String, relation: TimeRelation, right: String) {
self.time_constraints.push(TimeConstraint { left, relation, right });
}
pub fn time_constraints(&self) -> &[TimeConstraint] {
&self.time_constraints
}
pub fn clear_time_constraints(&mut self) {
self.time_constraints.clear();
self.reference_time_counter = 0;
self.current_reference_time = None;
}
pub fn end_sentence(&mut self) {
let mut candidates = self.drs.get_telescope_candidates();
if self.current_modal_context.is_some() {
for candidate in &mut candidates {
candidate.in_modal_scope = true;
}
}
self.telescope_candidates = candidates;
self.prior_modal_context = self.current_modal_context.take();
self.discourse_mode = true;
}
pub fn in_discourse_mode(&self) -> bool {
self.discourse_mode
}
pub fn telescope_candidates(&self) -> &[TelescopeCandidate] {
&self.telescope_candidates
}
pub fn resolve_via_telescope(&mut self, gender: Gender) -> Option<TelescopeCandidate> {
let can_access_modal = self.in_modal_context();
#[cfg(debug_assertions)]
for candidate in &self.telescope_candidates {
if candidate.in_modal_scope && !can_access_modal {
#[cfg(debug_assertions)]
continue;
}
let gender_match = candidate.gender == gender
|| candidate.gender == Gender::Unknown || gender == Gender::Unknown;
if gender_match {
return Some(candidate.clone());
}
}
None
}
pub fn set_ownership(&mut self, noun_class: Symbol, state: OwnershipState) {
self.drs.set_ownership(noun_class, state);
}
pub fn get_ownership(&self, noun_class: Symbol) -> Option<OwnershipState> {
self.drs.get_ownership(noun_class)
}
pub fn set_ownership_by_var(&mut self, var: Symbol, state: OwnershipState) {
self.drs.set_ownership_by_var(var, state);
}
pub fn get_ownership_by_var(&self, var: Symbol) -> Option<OwnershipState> {
self.drs.get_ownership_by_var(var)
}
pub fn enter_modal_context(&mut self, is_epistemic: bool, force: f32) {
self.current_modal_context = Some(ModalContext {
active: true,
is_epistemic,
force,
});
self.drs.enter_box(BoxType::ModalScope);
}
pub fn exit_modal_context(&mut self) {
self.current_modal_context = None;
self.drs.exit_box();
}
pub fn in_modal_context(&self) -> bool {
self.current_modal_context.is_some()
}
pub fn has_prior_modal_context(&self) -> bool {
self.prior_modal_context.is_some()
}
pub fn can_subordinate(&self) -> bool {
self.prior_modal_context.is_some()
}
pub fn clear(&mut self) {
self.drs.clear();
self.event_counter = 0;
self.event_history.clear();
self.reference_time_counter = 0;
self.current_reference_time = None;
self.time_constraints.clear();
self.telescope_candidates.clear();
self.discourse_mode = false;
self.current_modal_context = None;
self.prior_modal_context = None;
}
}
impl Default for WorldState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReferentSource {
MainClause,
ProperName,
ConditionalAntecedent,
UniversalRestrictor,
NegationScope,
Disjunct,
ModalScope,
}
impl ReferentSource {
pub fn gets_universal_force(&self) -> bool {
matches!(
self,
ReferentSource::ConditionalAntecedent | ReferentSource::UniversalRestrictor
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoxType {
Main,
ConditionalAntecedent,
ConditionalConsequent,
NegationScope,
UniversalRestrictor,
UniversalScope,
Disjunct,
ModalScope,
}
impl BoxType {
pub fn to_referent_source(&self) -> ReferentSource {
match self {
BoxType::Main => ReferentSource::MainClause,
BoxType::ConditionalAntecedent => ReferentSource::ConditionalAntecedent,
BoxType::ConditionalConsequent => ReferentSource::MainClause,
BoxType::NegationScope => ReferentSource::NegationScope,
BoxType::UniversalRestrictor => ReferentSource::UniversalRestrictor,
BoxType::UniversalScope => ReferentSource::MainClause,
BoxType::Disjunct => ReferentSource::Disjunct,
BoxType::ModalScope => ReferentSource::ModalScope,
}
}
pub fn can_telescope(&self) -> bool {
matches!(
self,
BoxType::Main
| BoxType::UniversalScope
| BoxType::UniversalRestrictor
| BoxType::ConditionalConsequent
| BoxType::ConditionalAntecedent
| BoxType::ModalScope )
}
pub fn blocks_accessibility(&self) -> bool {
matches!(self, BoxType::NegationScope | BoxType::Disjunct)
}
}
fn modifier_sets_match(a: &[Symbol], b: &[Symbol]) -> bool {
if a.len() != b.len() || a.is_empty() {
return false;
}
a.iter().all(|m| b.contains(m)) && b.iter().all(|m| a.contains(m))
}
#[derive(Debug, Clone)]
pub struct Referent {
pub variable: Symbol,
pub noun_class: Symbol,
pub gender: Gender,
pub number: Number,
pub source: ReferentSource,
pub used_by_pronoun: bool,
pub ownership: OwnershipState,
pub modifiers: Vec<Symbol>,
}
impl Referent {
pub fn new(variable: Symbol, noun_class: Symbol, gender: Gender, number: Number, source: ReferentSource) -> Self {
Self {
variable,
noun_class,
gender,
number,
source,
used_by_pronoun: false,
ownership: OwnershipState::Owned,
modifiers: Vec::new(),
}
}
pub fn with_modifiers(
variable: Symbol,
noun_class: Symbol,
gender: Gender,
number: Number,
source: ReferentSource,
modifiers: Vec<Symbol>,
) -> Self {
let mut referent = Self::new(variable, noun_class, gender, number, source);
referent.modifiers = modifiers;
referent
}
pub fn should_be_universal(&self) -> bool {
self.source.gets_universal_force() || self.used_by_pronoun
}
}
#[derive(Debug, Clone, Default)]
pub struct DrsBox {
pub universe: Vec<Referent>,
pub box_type: Option<BoxType>,
pub parent: Option<usize>,
}
impl DrsBox {
pub fn new(box_type: BoxType, parent: Option<usize>) -> Self {
Self {
universe: Vec::new(),
box_type: Some(box_type),
parent,
}
}
}
#[derive(Debug, Clone)]
pub struct Drs {
boxes: Vec<DrsBox>,
main_box: usize,
current_box: usize,
item_categories: std::collections::HashMap<Symbol, Symbol>,
}
impl Drs {
pub fn new() -> Self {
let main = DrsBox::new(BoxType::Main, None);
Self {
boxes: vec![main],
main_box: 0,
current_box: 0,
item_categories: std::collections::HashMap::new(),
}
}
pub fn enter_box(&mut self, box_type: BoxType) -> usize {
let parent = self.current_box;
let new_box = DrsBox::new(box_type, Some(parent));
let idx = self.boxes.len();
self.boxes.push(new_box);
self.current_box = idx;
idx
}
pub fn exit_box(&mut self) {
if let Some(parent) = self.boxes[self.current_box].parent {
self.current_box = parent;
}
}
pub fn current_box_index(&self) -> usize {
self.current_box
}
pub fn current_box_type(&self) -> Option<BoxType> {
self.boxes.get(self.current_box).and_then(|b| b.box_type)
}
pub fn introduce_referent(&mut self, variable: Symbol, noun_class: Symbol, gender: Gender, number: Number) {
let source = self.boxes[self.current_box]
.box_type
.map(|bt| bt.to_referent_source())
.unwrap_or(ReferentSource::MainClause);
let referent = Referent::new(variable, noun_class, gender, number, source);
self.boxes[self.current_box].universe.push(referent);
}
pub fn introduce_referent_with_source(&mut self, variable: Symbol, noun_class: Symbol, gender: Gender, number: Number, source: ReferentSource) {
let referent = Referent::new(variable, noun_class, gender, number, source);
self.boxes[self.current_box].universe.push(referent);
}
pub fn introduce_referent_with_modifiers(
&mut self,
variable: Symbol,
noun_class: Symbol,
gender: Gender,
number: Number,
source: ReferentSource,
modifiers: Vec<Symbol>,
) {
let referent =
Referent::with_modifiers(variable, noun_class, gender, number, source, modifiers);
self.boxes[self.current_box].universe.push(referent);
}
pub fn introduce_referent_global(&mut self, variable: Symbol, noun_class: Symbol, gender: Gender, number: Number, source: ReferentSource) {
let referent = Referent::new(variable, noun_class, gender, number, source);
self.boxes[self.main_box].universe.push(referent);
}
pub fn introduce_proper_name(&mut self, variable: Symbol, name: Symbol, gender: Gender) {
let referent = Referent::new(variable, name, gender, Number::Singular, ReferentSource::ProperName);
self.boxes[self.current_box].universe.push(referent);
}
pub fn is_rigid_referent(&self, symbol: Symbol) -> bool {
self.boxes.iter().any(|b| {
b.universe
.iter()
.any(|r| r.variable == symbol && r.source == ReferentSource::ProperName)
})
}
pub fn register_item_category(&mut self, item: Symbol, category: Symbol) {
self.item_categories.insert(item, category);
}
pub fn item_category(&self, item: Symbol) -> Option<Symbol> {
self.item_categories.get(&item).copied()
}
pub fn is_accessible(&self, target_box: usize, from_box: usize) -> bool {
if target_box == from_box {
return true;
}
let target = &self.boxes[target_box];
let from = &self.boxes[from_box];
if let Some(bt) = target.box_type {
match bt {
BoxType::NegationScope | BoxType::Disjunct | BoxType::ModalScope => {
return false;
}
_ => {}
}
}
if let (Some(BoxType::ConditionalConsequent), Some(BoxType::ConditionalAntecedent)) =
(from.box_type, target.box_type)
{
if from.parent == target.parent {
return true;
}
}
if let (Some(BoxType::UniversalScope), Some(BoxType::UniversalRestrictor)) =
(from.box_type, target.box_type)
{
if from.parent == target.parent {
return true;
}
}
let mut current = from_box;
while let Some(parent) = self.boxes[current].parent {
if parent == target_box {
return true;
}
current = parent;
}
false
}
pub fn resolve_pronoun(&mut self, from_box: usize, gender: Gender, number: Number) -> Result<Symbol, ScopeError> {
let mut candidates = Vec::new();
for (box_idx, drs_box) in self.boxes.iter().enumerate() {
let box_accessible = self.is_accessible(box_idx, from_box);
for referent in &drs_box.universe {
if matches!(referent.source, ReferentSource::NegationScope | ReferentSource::Disjunct) {
continue;
}
let has_global_source = matches!(referent.source, ReferentSource::MainClause | ReferentSource::ProperName);
if !box_accessible && !has_global_source {
continue;
}
let gender_match = referent.gender == gender
|| referent.gender == Gender::Unknown
|| gender == Gender::Unknown;
let number_match = referent.number == number;
if gender_match && number_match {
candidates.push((box_idx, referent.variable));
}
}
}
if let Some((box_idx, var)) = candidates.last() {
let box_idx = *box_idx;
let var = *var;
for referent in &mut self.boxes[box_idx].universe {
if referent.variable == var {
referent.used_by_pronoun = true;
return Ok(var);
}
}
}
for (_box_idx, drs_box) in self.boxes.iter().enumerate() {
for referent in &drs_box.universe {
if matches!(referent.source, ReferentSource::MainClause | ReferentSource::ProperName) {
continue;
}
let is_inaccessible = matches!(referent.source, ReferentSource::NegationScope | ReferentSource::Disjunct)
|| !self.is_accessible(_box_idx, from_box);
if is_inaccessible {
let gender_match = referent.gender == gender
|| (gender == Gender::Unknown)
|| (gender == Gender::Neuter && referent.gender == Gender::Unknown);
let number_match = referent.number == number;
if gender_match && number_match {
let blocking_scope = if matches!(referent.source, ReferentSource::NegationScope) {
BoxType::NegationScope
} else if matches!(referent.source, ReferentSource::Disjunct) {
BoxType::Disjunct
} else {
drs_box.box_type.unwrap_or(BoxType::Main)
};
let noun_class_str = format!("{:?}", referent.noun_class);
return Err(ScopeError::InaccessibleReferent {
gender,
blocking_scope,
reason: format!("'{}' is trapped in {:?} scope and cannot be accessed",
noun_class_str, blocking_scope),
});
}
}
}
}
Err(ScopeError::NoMatchingReferent {
gender,
number,
})
}
pub fn resolve_definite(&self, from_box: usize, noun_class: Symbol) -> Option<Symbol> {
for (box_idx, drs_box) in self.boxes.iter().enumerate() {
if self.is_accessible(box_idx, from_box) {
for referent in drs_box.universe.iter().rev() {
if referent.noun_class == noun_class {
return Some(referent.variable);
}
}
}
}
None
}
pub fn resolve_definite_by_modifier(
&self,
interner: &crate::Interner,
from_box: usize,
noun_class: Symbol,
modifiers: &[Symbol],
) -> Option<Symbol> {
use crate::lexicon::lookup_sort;
if modifiers.is_empty() {
return None;
}
let new_sort = lookup_sort(interner.resolve(noun_class))?;
if !new_sort.is_occasion() {
return None;
}
for (box_idx, drs_box) in self.boxes.iter().enumerate() {
if !self.is_accessible(box_idx, from_box) {
continue;
}
for referent in drs_box.universe.iter().rev() {
if !modifier_sets_match(&referent.modifiers, modifiers) {
continue;
}
let Some(prior_sort) = lookup_sort(interner.resolve(referent.noun_class)) else {
continue;
};
if !prior_sort.is_occasion() {
continue;
}
if !new_sort.is_compatible_with(prior_sort)
&& !prior_sort.is_compatible_with(new_sort)
{
continue;
}
return Some(referent.variable);
}
}
None
}
pub fn has_referent_by_variable(&self, var: Symbol) -> bool {
for drs_box in &self.boxes {
for referent in &drs_box.universe {
if referent.variable == var {
return true;
}
}
}
false
}
pub fn resolve_bridging(&self, interner: &crate::Interner, noun_class: Symbol) -> Option<(Symbol, &'static str)> {
use crate::ontology::find_bridging_wholes;
let noun_str = interner.resolve(noun_class);
let Some(wholes) = find_bridging_wholes(noun_str) else {
return None;
};
for whole in wholes {
for drs_box in &self.boxes {
for referent in drs_box.universe.iter().rev() {
let ref_class_str = interner.resolve(referent.noun_class);
if ref_class_str.eq_ignore_ascii_case(whole) {
return Some((referent.variable, *whole));
}
}
}
}
None
}
pub fn get_universal_referents(&self) -> Vec<Symbol> {
let mut result = Vec::new();
for drs_box in &self.boxes {
for referent in &drs_box.universe {
if referent.should_be_universal()
&& !matches!(referent.source, ReferentSource::ProperName)
{
result.push(referent.variable);
}
}
}
result
}
pub fn get_existential_referents(&self) -> Vec<Symbol> {
let mut result = Vec::new();
for drs_box in &self.boxes {
for referent in &drs_box.universe {
if !referent.should_be_universal()
&& !matches!(referent.source, ReferentSource::ProperName)
{
result.push(referent.variable);
}
}
}
result
}
pub fn get_last_event_referent(&self, interner: &crate::intern::Interner) -> Option<Symbol> {
for drs_box in self.boxes.iter().rev() {
for referent in drs_box.universe.iter().rev() {
let class_str = interner.resolve(referent.noun_class);
if class_str == "Event" {
return Some(referent.variable);
}
}
}
None
}
pub fn in_conditional_antecedent(&self) -> bool {
matches!(
self.boxes.get(self.current_box).and_then(|b| b.box_type),
Some(BoxType::ConditionalAntecedent)
)
}
pub fn in_universal_restrictor(&self) -> bool {
matches!(
self.boxes.get(self.current_box).and_then(|b| b.box_type),
Some(BoxType::UniversalRestrictor)
)
}
pub fn get_telescope_candidates(&self) -> Vec<TelescopeCandidate> {
let mut candidates = Vec::new();
for (box_idx, drs_box) in self.boxes.iter().enumerate() {
if let Some(box_type) = drs_box.box_type {
if !box_type.can_telescope() {
continue; }
}
let mut is_blocked = false;
let mut check_idx = box_idx;
while let Some(parent_idx) = self.boxes.get(check_idx).and_then(|b| b.parent) {
if let Some(parent_type) = self.boxes.get(parent_idx).and_then(|b| b.box_type) {
if parent_type.blocks_accessibility() {
is_blocked = true;
break;
}
}
check_idx = parent_idx;
}
if is_blocked {
continue;
}
let is_modal_box = drs_box.box_type == Some(BoxType::ModalScope);
for referent in &drs_box.universe {
if matches!(referent.source, ReferentSource::NegationScope | ReferentSource::Disjunct) {
continue;
}
candidates.push(TelescopeCandidate {
variable: referent.variable,
noun_class: referent.noun_class,
gender: referent.gender,
origin_box: box_idx,
scope_path: Vec::new(), in_modal_scope: is_modal_box || referent.source == ReferentSource::ModalScope,
});
}
}
candidates
}
pub fn find_blocked_referent(&self, from_box: usize, gender: Gender) -> Option<(Symbol, BoxType)> {
for (box_idx, drs_box) in self.boxes.iter().enumerate() {
if self.is_accessible(box_idx, from_box) {
continue;
}
if let Some(box_type) = drs_box.box_type {
if box_type.blocks_accessibility() {
for referent in &drs_box.universe {
let gender_match = gender == Gender::Unknown
|| referent.gender == Gender::Unknown
|| referent.gender == gender
|| gender == Gender::Neuter;
if gender_match {
return Some((referent.variable, box_type));
}
}
}
}
}
None
}
pub fn set_ownership(&mut self, noun_class: Symbol, state: OwnershipState) {
for drs_box in &mut self.boxes {
for referent in &mut drs_box.universe {
if referent.noun_class == noun_class {
referent.ownership = state;
return;
}
}
}
}
pub fn set_ownership_by_var(&mut self, var: Symbol, state: OwnershipState) {
for drs_box in &mut self.boxes {
for referent in &mut drs_box.universe {
if referent.variable == var {
referent.ownership = state;
return;
}
}
}
}
pub fn get_ownership(&self, noun_class: Symbol) -> Option<OwnershipState> {
for drs_box in &self.boxes {
for referent in &drs_box.universe {
if referent.noun_class == noun_class {
return Some(referent.ownership);
}
}
}
None
}
pub fn get_ownership_by_var(&self, var: Symbol) -> Option<OwnershipState> {
for drs_box in &self.boxes {
for referent in &drs_box.universe {
if referent.variable == var {
return Some(referent.ownership);
}
}
}
None
}
pub fn clear(&mut self) {
self.boxes.clear();
let main = DrsBox::new(BoxType::Main, None);
self.boxes.push(main);
self.main_box = 0;
self.current_box = 0;
self.item_categories.clear();
}
}
impl Default for Drs {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use logicaffeine_base::Interner;
#[test]
fn referent_source_universal_force() {
assert!(ReferentSource::ConditionalAntecedent.gets_universal_force());
assert!(ReferentSource::UniversalRestrictor.gets_universal_force());
assert!(!ReferentSource::MainClause.gets_universal_force());
assert!(!ReferentSource::ProperName.gets_universal_force());
}
#[test]
fn drs_new_has_main_box() {
let drs = Drs::new();
assert_eq!(drs.boxes.len(), 1);
assert_eq!(drs.current_box, 0);
assert_eq!(drs.boxes[0].box_type, Some(BoxType::Main));
}
#[test]
fn drs_enter_exit_box() {
let mut drs = Drs::new();
assert_eq!(drs.current_box, 0);
let ant_idx = drs.enter_box(BoxType::ConditionalAntecedent);
assert_eq!(ant_idx, 1);
assert_eq!(drs.current_box, 1);
assert_eq!(drs.boxes[1].parent, Some(0));
drs.exit_box();
assert_eq!(drs.current_box, 0);
}
#[test]
fn drs_introduce_referent_tracks_source() {
let mut interner = Interner::new();
let mut drs = Drs::new();
let x = interner.intern("x");
let farmer = interner.intern("Farmer");
drs.introduce_referent(x, farmer, Gender::Male, Number::Singular);
assert_eq!(drs.boxes[0].universe[0].source, ReferentSource::MainClause);
drs.enter_box(BoxType::ConditionalAntecedent);
let y = interner.intern("y");
let donkey = interner.intern("Donkey");
drs.introduce_referent(y, donkey, Gender::Neuter, Number::Singular);
assert_eq!(
drs.boxes[1].universe[0].source,
ReferentSource::ConditionalAntecedent
);
}
#[test]
fn drs_conditional_antecedent_accessible_from_consequent() {
let mut interner = Interner::new();
let mut drs = Drs::new();
let ant_idx = drs.enter_box(BoxType::ConditionalAntecedent);
let y = interner.intern("y");
let donkey = interner.intern("Donkey");
drs.introduce_referent(y, donkey, Gender::Neuter, Number::Singular);
drs.exit_box();
let cons_idx = drs.enter_box(BoxType::ConditionalConsequent);
assert!(drs.is_accessible(ant_idx, cons_idx));
}
#[test]
fn drs_negation_blocks_accessibility() {
let mut drs = Drs::new();
let neg_idx = drs.enter_box(BoxType::NegationScope);
drs.exit_box();
assert!(!drs.is_accessible(neg_idx, 0));
}
#[test]
fn drs_get_universal_referents() {
let mut interner = Interner::new();
let mut drs = Drs::new();
let x = interner.intern("x");
let farmer = interner.intern("Farmer");
drs.introduce_referent(x, farmer, Gender::Male, Number::Singular);
drs.enter_box(BoxType::ConditionalAntecedent);
let y = interner.intern("y");
let donkey = interner.intern("Donkey");
drs.introduce_referent(y, donkey, Gender::Neuter, Number::Singular);
let universals = drs.get_universal_referents();
assert_eq!(universals.len(), 1);
assert_eq!(universals[0], y);
}
#[test]
fn drs_pronoun_resolution_marks_used() {
let mut interner = Interner::new();
let mut drs = Drs::new();
drs.enter_box(BoxType::UniversalRestrictor);
let y = interner.intern("y");
let donkey = interner.intern("Donkey");
drs.introduce_referent(y, donkey, Gender::Neuter, Number::Singular);
let resolved = drs.resolve_pronoun(drs.current_box, Gender::Neuter, Number::Singular);
assert_eq!(resolved, Ok(y));
assert!(drs.boxes[1].universe[0].used_by_pronoun);
}
}