logicaffeine_language/parser/
quantifier.rs

1//! Quantifier parsing and scope management.
2//!
3//! This module handles determiners with quantificational force:
4//!
5//! - **Universal**: every, all, each → `∀x`
6//! - **Existential**: a, an, some → `∃x`
7//! - **Negative**: no, neither → `¬∃x` or `∀x(... → ¬...)`
8//! - **Proportional**: most, few, many → generalized quantifiers
9//! - **Definite**: the → uniqueness presupposition (ιx)
10//!
11//! # Quantifier Scope
12//!
13//! Quantifiers are assigned to scope islands during parsing. The `island_id` field
14//! tracks which island a quantifier belongs to, preventing illicit scope inversions
15//! (e.g., extracting from relative clauses).
16//!
17//! # Donkey Anaphora
18//!
19//! Indefinites in conditional antecedents or relative clauses receive universal
20//! force when bound by a pronoun in the main clause:
21//!
22//! "If a farmer owns a donkey, he beats it" → `∀x∀y((Farmer(x) ∧ Donkey(y) ∧ Owns(x,y)) → Beats(x,y))`
23
24use super::clause::ClauseParsing;
25use super::modal::ModalParsing;
26use super::noun::NounParsing;
27use super::{NegativeScopeMode, ParseResult, Parser};
28use crate::ast::{LogicExpr, NeoEventData, NounPhrase, QuantifierKind, Term, ThematicRole};
29use crate::drs::{Gender, Number};
30use crate::drs::ReferentSource;
31use crate::error::{ParseError, ParseErrorKind};
32use logicaffeine_base::Symbol;
33use crate::lexer::Lexer;
34use crate::lexicon::{get_canonical_verb, is_subsective, lookup_verb_db, Definiteness, Feature, Time};
35use crate::token::{PresupKind, TokenType};
36
37/// Trait for parsing quantified expressions and managing scope.
38///
39/// Provides methods for parsing quantifiers (every, some, no, most),
40/// their restrictions, and wrapping expressions with appropriate scope.
41pub trait QuantifierParsing<'a, 'ctx, 'int> {
42    /// Parses a quantified expression from a quantifier determiner.
43    fn parse_quantified(&mut self) -> ParseResult<&'a LogicExpr<'a>>;
44    /// Parses the restrictor clause for a quantifier.
45    fn parse_restriction(&mut self, var_name: Symbol) -> ParseResult<&'a LogicExpr<'a>>;
46    /// Parses a verb phrase as the nuclear scope of a quantifier.
47    fn parse_verb_phrase_for_restriction(&mut self, var_name: Symbol) -> ParseResult<&'a LogicExpr<'a>>;
48    /// Combines multiple expressions with conjunction.
49    fn combine_with_and(&self, exprs: Vec<&'a LogicExpr<'a>>) -> ParseResult<&'a LogicExpr<'a>>;
50    fn wrap_with_definiteness_full(
51        &mut self,
52        np: &NounPhrase<'a>,
53        predicate: &'a LogicExpr<'a>,
54    ) -> ParseResult<&'a LogicExpr<'a>>;
55    fn wrap_with_definiteness(
56        &mut self,
57        definiteness: Option<Definiteness>,
58        noun: Symbol,
59        predicate: &'a LogicExpr<'a>,
60    ) -> ParseResult<&'a LogicExpr<'a>>;
61    fn wrap_with_definiteness_and_adjectives(
62        &mut self,
63        definiteness: Option<Definiteness>,
64        noun: Symbol,
65        adjectives: &[Symbol],
66        predicate: &'a LogicExpr<'a>,
67    ) -> ParseResult<&'a LogicExpr<'a>>;
68    fn wrap_with_definiteness_and_adjectives_and_pps(
69        &mut self,
70        definiteness: Option<Definiteness>,
71        noun: Symbol,
72        adjectives: &[Symbol],
73        pps: &[&'a LogicExpr<'a>],
74        predicate: &'a LogicExpr<'a>,
75    ) -> ParseResult<&'a LogicExpr<'a>>;
76    fn wrap_with_definiteness_for_object(
77        &mut self,
78        definiteness: Option<Definiteness>,
79        noun: Symbol,
80        predicate: &'a LogicExpr<'a>,
81    ) -> ParseResult<&'a LogicExpr<'a>>;
82    fn substitute_pp_placeholder(&mut self, pp: &'a LogicExpr<'a>, var: Symbol) -> &'a LogicExpr<'a>;
83    fn substitute_constant_with_var(
84        &self,
85        expr: &'a LogicExpr<'a>,
86        constant_name: Symbol,
87        var_name: Symbol,
88    ) -> ParseResult<&'a LogicExpr<'a>>;
89    fn substitute_constant_with_var_sym(
90        &self,
91        expr: &'a LogicExpr<'a>,
92        constant_name: Symbol,
93        var_name: Symbol,
94    ) -> ParseResult<&'a LogicExpr<'a>>;
95    fn substitute_constant_with_sigma(
96        &self,
97        expr: &'a LogicExpr<'a>,
98        constant_name: Symbol,
99        sigma_term: Term<'a>,
100    ) -> ParseResult<&'a LogicExpr<'a>>;
101    fn find_main_verb_name(&self, expr: &LogicExpr<'a>) -> Option<Symbol>;
102    fn transform_cardinal_to_group(&mut self, expr: &'a LogicExpr<'a>) -> ParseResult<&'a LogicExpr<'a>>;
103    fn build_verb_neo_event(
104        &mut self,
105        verb: Symbol,
106        subject_var: Symbol,
107        object: Option<Term<'a>>,
108        modifiers: Vec<Symbol>,
109    ) -> &'a LogicExpr<'a>;
110}
111
112impl<'a, 'ctx, 'int> QuantifierParsing<'a, 'ctx, 'int> for Parser<'a, 'ctx, 'int> {
113    fn parse_quantified(&mut self) -> ParseResult<&'a LogicExpr<'a>> {
114        let quantifier_token = self.previous().kind.clone();
115        let var_name = self.next_var_name();
116
117        // Track if we're inside a "No" quantifier - referents introduced here
118        // are inaccessible for cross-sentence anaphora
119        let was_in_negative_quantifier = self.in_negative_quantifier;
120        if matches!(quantifier_token, TokenType::No) {
121            self.in_negative_quantifier = true;
122        }
123
124        // "At most one of X, Y, and Z is P" — counting quantifier with explicit list
125        if matches!(quantifier_token, TokenType::AtMost(_) | TokenType::AtLeast(_) | TokenType::Cardinal(_))
126            && self.check_preposition_is("of")
127        {
128            self.advance(); // consume "of"
129
130            // Parse comma-separated list of identifiers: "grant0, grant1, and grant2"
131            let mut signal_names: Vec<Symbol> = Vec::new();
132            loop {
133                let name = self.consume_content_word()?;
134                signal_names.push(name);
135
136                if self.check(&TokenType::Comma) {
137                    self.advance(); // consume ","
138                    // Skip optional "and" after comma: "X, Y, and Z"
139                    if self.check(&TokenType::And) {
140                        self.advance();
141                    }
142                } else if self.check(&TokenType::And) {
143                    self.advance(); // consume "and" (two-element: "X and Y")
144                } else {
145                    break;
146                }
147            }
148
149            // Now parse the predicate: "is asserted", "is valid", etc.
150            // In hardware context, the predicate is implicit — what matters
151            // is each signal name being high/low. Consume but don't use.
152            let mut is_negated = false;
153            if self.check(&TokenType::Is) || self.check(&TokenType::Are) {
154                self.advance(); // consume copula
155                is_negated = self.check(&TokenType::Not);
156                if is_negated {
157                    self.advance();
158                }
159                // Consume the predicate adjective/verb (e.g., "asserted", "valid")
160                let _ = self.consume_content_word();
161                // Consume optional trailing "at any time"
162                while self.check_preposition_is("at") {
163                    self.advance();
164                    if self.check(&TokenType::Any) {
165                        self.advance();
166                    }
167                    if self.check_content_word() {
168                        self.advance();
169                    }
170                }
171            }
172
173            // Build the body: each signal as an Atom, joined by OR
174            // SVA synthesis maps Atom(sig) → signal name directly
175            let mut signal_exprs: Vec<&'a LogicExpr<'a>> = Vec::new();
176            for &sig in &signal_names {
177                let atom: &'a LogicExpr<'a> = self.ctx.exprs.alloc(LogicExpr::Atom(sig));
178                let sig_expr = if is_negated {
179                    self.ctx.exprs.alloc(LogicExpr::UnaryOp {
180                        op: TokenType::Not,
181                        operand: atom,
182                    })
183                } else {
184                    atom
185                };
186                signal_exprs.push(sig_expr);
187            }
188
189            let body = if signal_exprs.len() == 1 {
190                signal_exprs[0]
191            } else {
192                let mut combined = signal_exprs[0];
193                for &expr in &signal_exprs[1..] {
194                    combined = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
195                        left: combined,
196                        op: TokenType::Or,
197                        right: expr,
198                    });
199                }
200                combined
201            };
202
203            let kind = match quantifier_token {
204                TokenType::AtMost(n) => QuantifierKind::AtMost(n),
205                TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
206                TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
207                _ => unreachable!(),
208            };
209
210            self.in_negative_quantifier = was_in_negative_quantifier;
211
212            return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
213                kind,
214                variable: var_name,
215                body,
216                island_id: self.current_island,
217            }));
218        }
219
220        let subject_pred = self.parse_restriction(var_name)?;
221
222        if self.check_modal() {
223            use crate::ast::ModalFlavor;
224
225            self.advance();
226            let vector = self.token_to_vector(&self.previous().kind.clone());
227            let verb = self.consume_content_word()?;
228
229            // Parse object if present (e.g., "can enter the room" -> room is object)
230            let obj_term = if self.check_content_word() || self.check_article() {
231                let obj_np = self.parse_noun_phrase(false)?;
232                Some(self.noun_phrase_to_term(&obj_np))
233            } else {
234                None
235            };
236
237            // Collect any trailing adverbs
238            let modifiers = self.collect_adverbs();
239            let verb_pred = self.build_verb_neo_event(verb, var_name, obj_term, modifiers);
240
241            // Determine quantifier kind first (shared by both branches)
242            let kind = match quantifier_token {
243                TokenType::All | TokenType::No => QuantifierKind::Universal,
244                TokenType::Any => {
245                    if self.is_negative_context() {
246                        QuantifierKind::Existential
247                    } else {
248                        QuantifierKind::Universal
249                    }
250                }
251                TokenType::Some => QuantifierKind::Existential,
252                TokenType::Most => QuantifierKind::Most,
253                TokenType::Few => QuantifierKind::Few,
254                TokenType::Many => QuantifierKind::Many,
255                TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
256                TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
257                TokenType::AtMost(n) => QuantifierKind::AtMost(n),
258                _ => {
259                    return Err(ParseError {
260                        kind: ParseErrorKind::UnknownQuantifier {
261                            found: quantifier_token.clone(),
262                        },
263                        span: self.current_span(),
264                    })
265                }
266            };
267
268            // Branch on modal flavor for scope handling
269            if vector.flavor == ModalFlavor::Root {
270                // === NARROW SCOPE (De Re) ===
271                // Root modals (can, must, should) attach to the predicate inside the quantifier
272                // "Some birds can fly" → ∃x(Bird(x) ∧ ◇Fly(x))
273
274                // Wrap the verb predicate in the modal
275                let modal_verb = self.ctx.exprs.alloc(LogicExpr::Modal {
276                    vector,
277                    operand: verb_pred,
278                });
279
280                let body = match quantifier_token {
281                    TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
282                        left: subject_pred,
283                        op: TokenType::Implies,
284                        right: modal_verb,
285                    }),
286                    TokenType::Any => {
287                        if self.is_negative_context() {
288                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
289                                left: subject_pred,
290                                op: TokenType::And,
291                                right: modal_verb,
292                            })
293                        } else {
294                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
295                                left: subject_pred,
296                                op: TokenType::Implies,
297                                right: modal_verb,
298                            })
299                        }
300                    }
301                    TokenType::Some
302                    | TokenType::Most
303                    | TokenType::Few
304                    | TokenType::Many
305                    | TokenType::Cardinal(_)
306                    | TokenType::AtLeast(_)
307                    | TokenType::AtMost(_) => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
308                        left: subject_pred,
309                        op: TokenType::And,
310                        right: modal_verb,
311                    }),
312                    TokenType::No => {
313                        let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
314                            op: TokenType::Not,
315                            operand: modal_verb,
316                        });
317                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
318                            left: subject_pred,
319                            op: TokenType::Implies,
320                            right: neg,
321                        })
322                    }
323                    _ => {
324                        return Err(ParseError {
325                            kind: ParseErrorKind::UnknownQuantifier {
326                                found: quantifier_token.clone(),
327                            },
328                            span: self.current_span(),
329                        })
330                    }
331                };
332
333                // Build quantifier (modal is inside)
334                let mut result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
335                    kind,
336                    variable: var_name,
337                    body,
338                    island_id: self.current_island,
339                });
340
341                // Process donkey bindings for indefinites in restrictions (e.g., "who lacks a key")
342                for (_noun, donkey_var, used, wide_neg) in self.donkey_bindings.iter().rev() {
343                    if *used {
344                        // Donkey anaphora: wrap with ∀ at outer scope
345                        result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
346                            kind: QuantifierKind::Universal,
347                            variable: *donkey_var,
348                            body: result,
349                            island_id: self.current_island,
350                        });
351                    } else {
352                        // Non-donkey: wrap with ∃ INSIDE the restriction
353                        result = self.wrap_donkey_in_restriction(result, *donkey_var, *wide_neg);
354                    }
355                }
356                self.donkey_bindings.clear();
357
358                self.in_negative_quantifier = was_in_negative_quantifier;
359                return Ok(result);
360
361            } else {
362                // === WIDE SCOPE (De Dicto) ===
363                // Epistemic modals (might, may) wrap the entire quantifier
364                // "Some unicorns might exist" → ◇∃x(Unicorn(x) ∧ Exist(x))
365
366                let body = match quantifier_token {
367                    TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
368                        left: subject_pred,
369                        op: TokenType::Implies,
370                        right: verb_pred,
371                    }),
372                    TokenType::Any => {
373                        if self.is_negative_context() {
374                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
375                                left: subject_pred,
376                                op: TokenType::And,
377                                right: verb_pred,
378                            })
379                        } else {
380                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
381                                left: subject_pred,
382                                op: TokenType::Implies,
383                                right: verb_pred,
384                            })
385                        }
386                    }
387                    TokenType::Some
388                    | TokenType::Most
389                    | TokenType::Few
390                    | TokenType::Many
391                    | TokenType::Cardinal(_)
392                    | TokenType::AtLeast(_)
393                    | TokenType::AtMost(_) => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
394                        left: subject_pred,
395                        op: TokenType::And,
396                        right: verb_pred,
397                    }),
398                    TokenType::No => {
399                        let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
400                            op: TokenType::Not,
401                            operand: verb_pred,
402                        });
403                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
404                            left: subject_pred,
405                            op: TokenType::Implies,
406                            right: neg,
407                        })
408                    }
409                    _ => {
410                        return Err(ParseError {
411                            kind: ParseErrorKind::UnknownQuantifier {
412                                found: quantifier_token.clone(),
413                            },
414                            span: self.current_span(),
415                        })
416                    }
417                };
418
419                let mut result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
420                    kind,
421                    variable: var_name,
422                    body,
423                    island_id: self.current_island,
424                });
425
426                // Process donkey bindings for indefinites in restrictions (e.g., "who lacks a key")
427                for (_noun, donkey_var, used, wide_neg) in self.donkey_bindings.iter().rev() {
428                    if *used {
429                        // Donkey anaphora: wrap with ∀ at outer scope
430                        result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
431                            kind: QuantifierKind::Universal,
432                            variable: *donkey_var,
433                            body: result,
434                            island_id: self.current_island,
435                        });
436                    } else {
437                        // Non-donkey: wrap with ∃ INSIDE the restriction
438                        result = self.wrap_donkey_in_restriction(result, *donkey_var, *wide_neg);
439                    }
440                }
441                self.donkey_bindings.clear();
442
443                // Wrap the entire quantifier in the modal
444                self.in_negative_quantifier = was_in_negative_quantifier;
445                return Ok(self.ctx.exprs.alloc(LogicExpr::Modal {
446                    vector,
447                    operand: result,
448                }));
449            }
450        }
451
452        if self.check_auxiliary() {
453            let aux_token = self.advance();
454            let aux_time = if let TokenType::Auxiliary(time) = aux_token.kind.clone() {
455                time
456            } else {
457                Time::None
458            };
459            self.pending_time = Some(aux_time);
460
461            let is_negated = self.match_token(&[TokenType::Not]);
462            if is_negated {
463                self.negative_depth += 1;
464            }
465
466            if self.check_verb() {
467                let verb = self.consume_verb();
468
469                // Convert aux_time to modifier
470                let modifiers = match aux_time {
471                    Time::Past => vec![self.interner.intern("Past")],
472                    Time::Future => vec![self.interner.intern("Future")],
473                    _ => vec![],
474                };
475
476                let verb_pred = self.build_verb_neo_event(verb, var_name, None, modifiers);
477
478                let maybe_negated = if is_negated {
479                    self.negative_depth -= 1;
480                    self.ctx.exprs.alloc(LogicExpr::UnaryOp {
481                        op: TokenType::Not,
482                        operand: verb_pred,
483                    })
484                } else {
485                    verb_pred
486                };
487
488                let body = match quantifier_token {
489                    TokenType::All | TokenType::Any => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
490                        left: subject_pred,
491                        op: TokenType::Implies,
492                        right: maybe_negated,
493                    }),
494                    _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
495                        left: subject_pred,
496                        op: TokenType::And,
497                        right: maybe_negated,
498                    }),
499                };
500
501                let kind = match quantifier_token {
502                    TokenType::All | TokenType::No => QuantifierKind::Universal,
503                    TokenType::Some => QuantifierKind::Existential,
504                    TokenType::Most => QuantifierKind::Most,
505                    TokenType::Few => QuantifierKind::Few,
506                    TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
507                    TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
508                    TokenType::AtMost(n) => QuantifierKind::AtMost(n),
509                    _ => QuantifierKind::Universal,
510                };
511
512                self.in_negative_quantifier = was_in_negative_quantifier;
513                return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
514                    kind,
515                    variable: var_name,
516                    body,
517                    island_id: self.current_island,
518                }));
519            }
520        }
521
522        // Only trigger presupposition if followed by gerund complement
523        if self.check_presup_trigger() && self.is_followed_by_gerund() {
524            let presup_kind = match self.advance().kind {
525                TokenType::PresupTrigger(kind) => kind,
526                TokenType::Verb { lemma, .. } => {
527                    let s = self.interner.resolve(lemma).to_lowercase();
528                    crate::lexicon::lookup_presup_trigger(&s)
529                        .expect("Lexicon mismatch: Verb flagged as trigger but lookup failed")
530                }
531                _ => panic!("Expected presupposition trigger"),
532            };
533
534            let complement = if self.check_verb() {
535                let verb = self.consume_verb();
536                let modifiers = self.collect_adverbs();
537                self.build_verb_neo_event(verb, var_name, None, modifiers)
538            } else {
539                let unknown = self.interner.intern("?");
540                self.ctx.exprs.alloc(LogicExpr::Atom(unknown))
541            };
542
543            let verb_pred = match presup_kind {
544                PresupKind::Stop => self.ctx.exprs.alloc(LogicExpr::UnaryOp {
545                    op: TokenType::Not,
546                    operand: complement,
547                }),
548                PresupKind::Start | PresupKind::Continue => complement,
549                PresupKind::Regret | PresupKind::Realize | PresupKind::Know => complement,
550            };
551
552            let body = match quantifier_token {
553                TokenType::All | TokenType::Any => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
554                    left: subject_pred,
555                    op: TokenType::Implies,
556                    right: verb_pred,
557                }),
558                _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
559                    left: subject_pred,
560                    op: TokenType::And,
561                    right: verb_pred,
562                }),
563            };
564
565            let kind = match quantifier_token {
566                TokenType::All | TokenType::No => QuantifierKind::Universal,
567                TokenType::Some => QuantifierKind::Existential,
568                TokenType::Most => QuantifierKind::Most,
569                TokenType::Few => QuantifierKind::Few,
570                TokenType::Many => QuantifierKind::Many,
571                TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
572                TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
573                TokenType::AtMost(n) => QuantifierKind::AtMost(n),
574                _ => QuantifierKind::Universal,
575            };
576
577            self.in_negative_quantifier = was_in_negative_quantifier;
578            return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
579                kind,
580                variable: var_name,
581                body,
582                island_id: self.current_island,
583            }));
584        }
585
586        if self.check_verb() {
587            let verb = self.consume_verb();
588            let mut args = vec![Term::Variable(var_name)];
589
590            if self.check_pronoun() {
591                let token = self.peek().clone();
592                if let TokenType::Pronoun { gender, .. } = token.kind {
593                    self.advance();
594                    if let Some(donkey_var) = self.resolve_donkey_pronoun(gender) {
595                        args.push(Term::Variable(donkey_var));
596                    } else {
597                        let resolved = self.resolve_pronoun(gender, Number::Singular)?;
598                        let term = match resolved {
599                            super::ResolvedPronoun::Variable(s) => Term::Variable(s),
600                            super::ResolvedPronoun::Constant(s) => Term::Constant(s),
601                        };
602                        args.push(term);
603                    }
604                }
605            } else if self.check_npi_object() {
606                let npi_token = self.advance().kind.clone();
607                let obj_var = self.next_var_name();
608
609                let restriction_name = match npi_token {
610                    TokenType::Anything => "Thing",
611                    TokenType::Anyone => "Person",
612                    _ => "Thing",
613                };
614
615                let restriction_sym = self.interner.intern(restriction_name);
616                let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
617                    name: restriction_sym,
618                    args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
619                    world: None,
620                });
621
622                let npi_modifiers = self.collect_adverbs();
623                let verb_with_obj = self.build_verb_neo_event(
624                    verb,
625                    var_name,
626                    Some(Term::Variable(obj_var)),
627                    npi_modifiers,
628                );
629
630                let npi_body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
631                    left: obj_restriction,
632                    op: TokenType::And,
633                    right: verb_with_obj,
634                });
635
636                let npi_quantified = self.ctx.exprs.alloc(LogicExpr::Quantifier {
637                    kind: QuantifierKind::Existential,
638                    variable: obj_var,
639                    body: npi_body,
640                    island_id: self.current_island,
641                });
642
643                let negated_npi = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
644                    op: TokenType::Not,
645                    operand: npi_quantified,
646                });
647
648                let body = match quantifier_token {
649                    TokenType::All | TokenType::No => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
650                        left: subject_pred,
651                        op: TokenType::Implies,
652                        right: negated_npi,
653                    }),
654                    _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
655                        left: subject_pred,
656                        op: TokenType::And,
657                        right: negated_npi,
658                    }),
659                };
660
661                let kind = match quantifier_token {
662                    TokenType::All | TokenType::No => QuantifierKind::Universal,
663                    TokenType::Some => QuantifierKind::Existential,
664                    TokenType::Most => QuantifierKind::Most,
665                    TokenType::Few => QuantifierKind::Few,
666                    TokenType::Many => QuantifierKind::Many,
667                    TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
668                    TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
669                    TokenType::AtMost(n) => QuantifierKind::AtMost(n),
670                    _ => QuantifierKind::Universal,
671                };
672
673                self.in_negative_quantifier = was_in_negative_quantifier;
674                return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
675                    kind,
676                    variable: var_name,
677                    body,
678                    island_id: self.current_island,
679                }));
680            } else if self.check_quantifier() || self.check_article() {
681                let obj_quantifier = if self.check_quantifier() {
682                    Some(self.advance().kind.clone())
683                } else {
684                    let art = self.advance().kind.clone();
685                    if let TokenType::Article(def) = art {
686                        if def == Definiteness::Indefinite {
687                            Some(TokenType::Some)
688                        } else {
689                            None
690                        }
691                    } else {
692                        None
693                    }
694                };
695
696                let object = self.parse_noun_phrase(false)?;
697
698                if let Some(obj_q) = obj_quantifier {
699                    let obj_var = self.next_var_name();
700
701                    // Introduce object referent in DRS for cross-sentence anaphora (telescoping)
702                    // BUT: If inside "No X" quantifier, mark with NegationScope to block accessibility
703                    let obj_gender = Self::infer_noun_gender(self.interner.resolve(object.noun));
704                    let obj_number = if Self::is_plural_noun(self.interner.resolve(object.noun)) {
705                        Number::Plural
706                    } else {
707                        Number::Singular
708                    };
709                    if self.in_negative_quantifier {
710                        self.drs.introduce_referent_with_source(obj_var, object.noun, obj_gender, obj_number, ReferentSource::NegationScope);
711                    } else {
712                        self.drs.introduce_referent(obj_var, object.noun, obj_gender, obj_number);
713                    }
714
715                    let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
716                        name: object.noun,
717                        args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
718                        world: None,
719                    });
720
721                    let obj_modifiers = self.collect_adverbs();
722                    let verb_with_obj = self.build_verb_neo_event(
723                        verb,
724                        var_name,
725                        Some(Term::Variable(obj_var)),
726                        obj_modifiers,
727                    );
728
729                    let obj_kind = match obj_q {
730                        TokenType::All => QuantifierKind::Universal,
731                        TokenType::Some => QuantifierKind::Existential,
732                        TokenType::No => QuantifierKind::Universal,
733                        TokenType::Most => QuantifierKind::Most,
734                        TokenType::Few => QuantifierKind::Few,
735                        TokenType::Many => QuantifierKind::Many,
736                        TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
737                        TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
738                        TokenType::AtMost(n) => QuantifierKind::AtMost(n),
739                        _ => QuantifierKind::Existential,
740                    };
741
742                    let obj_body = match obj_q {
743                        TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
744                            left: obj_restriction,
745                            op: TokenType::Implies,
746                            right: verb_with_obj,
747                        }),
748                        TokenType::No => {
749                            let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
750                                op: TokenType::Not,
751                                operand: verb_with_obj,
752                            });
753                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
754                                left: obj_restriction,
755                                op: TokenType::Implies,
756                                right: neg,
757                            })
758                        }
759                        _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
760                            left: obj_restriction,
761                            op: TokenType::And,
762                            right: verb_with_obj,
763                        }),
764                    };
765
766                    let obj_quantified = self.ctx.exprs.alloc(LogicExpr::Quantifier {
767                        kind: obj_kind,
768                        variable: obj_var,
769                        body: obj_body,
770                        island_id: self.current_island,
771                    });
772
773                    let subj_kind = match quantifier_token {
774                        TokenType::All | TokenType::No => QuantifierKind::Universal,
775                        TokenType::Any => {
776                            if self.is_negative_context() {
777                                QuantifierKind::Existential
778                            } else {
779                                QuantifierKind::Universal
780                            }
781                        }
782                        TokenType::Some => QuantifierKind::Existential,
783                        TokenType::Most => QuantifierKind::Most,
784                        TokenType::Few => QuantifierKind::Few,
785                        TokenType::Many => QuantifierKind::Many,
786                        TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
787                        TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
788                        TokenType::AtMost(n) => QuantifierKind::AtMost(n),
789                        _ => QuantifierKind::Universal,
790                    };
791
792                    let subj_body = match quantifier_token {
793                        TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
794                            left: subject_pred,
795                            op: TokenType::Implies,
796                            right: obj_quantified,
797                        }),
798                        TokenType::No => {
799                            let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
800                                op: TokenType::Not,
801                                operand: obj_quantified,
802                            });
803                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
804                                left: subject_pred,
805                                op: TokenType::Implies,
806                                right: neg,
807                            })
808                        }
809                        _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
810                            left: subject_pred,
811                            op: TokenType::And,
812                            right: obj_quantified,
813                        }),
814                    };
815
816                    self.in_negative_quantifier = was_in_negative_quantifier;
817                    return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
818                        kind: subj_kind,
819                        variable: var_name,
820                        body: subj_body,
821                        island_id: self.current_island,
822                    }));
823                } else {
824                    args.push(Term::Constant(object.noun));
825                }
826            } else if self.check_content_word() {
827                let object = self.parse_noun_phrase(false)?;
828                args.push(Term::Constant(object.noun));
829            }
830
831            // Extract object term from args if present (args[0] is subject, args[1] is object)
832            let obj_term = if args.len() > 1 {
833                Some(args.remove(1))
834            } else {
835                None
836            };
837            // Collect any trailing adverbs (e.g., "bark loudly")
838            let modifiers = self.collect_adverbs();
839            let verb_pred = self.build_verb_neo_event(verb, var_name, obj_term, modifiers);
840
841            let body = match quantifier_token {
842                TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
843                    left: subject_pred,
844                    op: TokenType::Implies,
845                    right: verb_pred,
846                }),
847                TokenType::Any => {
848                    if self.is_negative_context() {
849                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
850                            left: subject_pred,
851                            op: TokenType::And,
852                            right: verb_pred,
853                        })
854                    } else {
855                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
856                            left: subject_pred,
857                            op: TokenType::Implies,
858                            right: verb_pred,
859                        })
860                    }
861                }
862                TokenType::Some
863                | TokenType::Most
864                | TokenType::Few
865                | TokenType::Many
866                | TokenType::Cardinal(_)
867                | TokenType::AtLeast(_)
868                | TokenType::AtMost(_) => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
869                    left: subject_pred,
870                    op: TokenType::And,
871                    right: verb_pred,
872                }),
873                TokenType::No => {
874                    let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
875                        op: TokenType::Not,
876                        operand: verb_pred,
877                    });
878                    self.ctx.exprs.alloc(LogicExpr::BinaryOp {
879                        left: subject_pred,
880                        op: TokenType::Implies,
881                        right: neg,
882                    })
883                }
884                _ => {
885                    return Err(ParseError {
886                        kind: ParseErrorKind::UnknownQuantifier {
887                            found: quantifier_token.clone(),
888                        },
889                        span: self.current_span(),
890                    })
891                }
892            };
893
894            let kind = match quantifier_token {
895                TokenType::All | TokenType::No => QuantifierKind::Universal,
896                TokenType::Any => {
897                    if self.is_negative_context() {
898                        QuantifierKind::Existential
899                    } else {
900                        QuantifierKind::Universal
901                    }
902                }
903                TokenType::Some => QuantifierKind::Existential,
904                TokenType::Most => QuantifierKind::Most,
905                TokenType::Few => QuantifierKind::Few,
906                TokenType::Many => QuantifierKind::Many,
907                TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
908                TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
909                TokenType::AtMost(n) => QuantifierKind::AtMost(n),
910                _ => {
911                    return Err(ParseError {
912                        kind: ParseErrorKind::UnknownQuantifier {
913                            found: quantifier_token.clone(),
914                        },
915                        span: self.current_span(),
916                    })
917                }
918            };
919
920            let mut result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
921                kind,
922                variable: var_name,
923                body,
924                island_id: self.current_island,
925            });
926
927            for (_noun, donkey_var, used, wide_neg) in self.donkey_bindings.iter().rev() {
928                if *used {
929                    // Donkey anaphora: wrap with ∀ at outer scope
930                    result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
931                        kind: QuantifierKind::Universal,
932                        variable: *donkey_var,
933                        body: result,
934                        island_id: self.current_island,
935                    });
936                } else {
937                    // Non-donkey: wrap with ∃ INSIDE the restriction
938                    result = self.wrap_donkey_in_restriction(result, *donkey_var, *wide_neg);
939                }
940            }
941            self.donkey_bindings.clear();
942
943            self.in_negative_quantifier = was_in_negative_quantifier;
944            return Ok(result);
945        }
946
947        // Handle do-support: "every X does not hold" → ¬Hold(x)
948        if self.check(&TokenType::Does) || self.check(&TokenType::Do) {
949            self.advance(); // consume "does"/"do"
950            let negative = self.match_token(&[TokenType::Not]);
951            // The verb after "does not" becomes the predicate
952            let verb_sym = self.consume_verb();
953            let predicate_expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
954                name: verb_sym,
955                args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
956                world: None,
957            });
958            let final_predicate = if negative {
959                self.ctx.exprs.alloc(LogicExpr::UnaryOp {
960                    op: TokenType::Not,
961                    operand: predicate_expr,
962                })
963            } else {
964                predicate_expr
965            };
966
967            let body = match quantifier_token {
968                TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
969                    left: subject_pred,
970                    op: TokenType::Implies,
971                    right: final_predicate,
972                }),
973                _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
974                    left: subject_pred,
975                    op: TokenType::And,
976                    right: final_predicate,
977                }),
978            };
979
980            let result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
981                kind: match quantifier_token {
982                    TokenType::All => QuantifierKind::Universal,
983                    _ => QuantifierKind::Existential,
984                },
985                variable: var_name,
986                body: body,
987                island_id: self.current_island,
988            });
989            self.in_negative_quantifier = was_in_negative_quantifier;
990            return Ok(result);
991        }
992
993        self.consume_copula()?;
994
995        let negative = self.match_token(&[TokenType::Not]);
996        let predicate_np = self.parse_noun_phrase(true)?;
997
998        let predicate_expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
999            name: predicate_np.noun,
1000            args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1001            world: None,
1002        });
1003
1004        let final_predicate = if negative {
1005            self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1006                op: TokenType::Not,
1007                operand: predicate_expr,
1008            })
1009        } else {
1010            predicate_expr
1011        };
1012
1013        let body = match quantifier_token {
1014            TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1015                left: subject_pred,
1016                op: TokenType::Implies,
1017                right: final_predicate,
1018            }),
1019            TokenType::Any => {
1020                if self.is_negative_context() {
1021                    self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1022                        left: subject_pred,
1023                        op: TokenType::And,
1024                        right: final_predicate,
1025                    })
1026                } else {
1027                    self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1028                        left: subject_pred,
1029                        op: TokenType::Implies,
1030                        right: final_predicate,
1031                    })
1032                }
1033            }
1034            TokenType::Some
1035            | TokenType::Most
1036            | TokenType::Few
1037            | TokenType::Many
1038            | TokenType::Cardinal(_)
1039            | TokenType::AtLeast(_)
1040            | TokenType::AtMost(_) => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1041                left: subject_pred,
1042                op: TokenType::And,
1043                right: final_predicate,
1044            }),
1045            TokenType::No => {
1046                let neg_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1047                    name: predicate_np.noun,
1048                    args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1049                    world: None,
1050                });
1051                let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1052                    op: TokenType::Not,
1053                    operand: neg_pred,
1054                });
1055                self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1056                    left: subject_pred,
1057                    op: TokenType::Implies,
1058                    right: neg,
1059                })
1060            }
1061            _ => {
1062                return Err(ParseError {
1063                    kind: ParseErrorKind::UnknownQuantifier {
1064                        found: quantifier_token.clone(),
1065                    },
1066                    span: self.current_span(),
1067                })
1068            }
1069        };
1070
1071        let kind = match quantifier_token {
1072            TokenType::All | TokenType::No => QuantifierKind::Universal,
1073            TokenType::Any => {
1074                if self.is_negative_context() {
1075                    QuantifierKind::Existential
1076                } else {
1077                    QuantifierKind::Universal
1078                }
1079            }
1080            TokenType::Some => QuantifierKind::Existential,
1081            TokenType::Most => QuantifierKind::Most,
1082            TokenType::Few => QuantifierKind::Few,
1083            TokenType::Many => QuantifierKind::Many,
1084            TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
1085            TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
1086            TokenType::AtMost(n) => QuantifierKind::AtMost(n),
1087            _ => {
1088                return Err(ParseError {
1089                    kind: ParseErrorKind::UnknownQuantifier {
1090                        found: quantifier_token.clone(),
1091                    },
1092                    span: self.current_span(),
1093                })
1094            }
1095        };
1096
1097        let mut result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
1098            kind,
1099            variable: var_name,
1100            body,
1101            island_id: self.current_island,
1102        });
1103
1104        for (_noun, donkey_var, used, wide_neg) in self.donkey_bindings.iter().rev() {
1105            if *used {
1106                // Donkey anaphora: wrap with ∀ at outer scope
1107                result = self.ctx.exprs.alloc(LogicExpr::Quantifier {
1108                    kind: QuantifierKind::Universal,
1109                    variable: *donkey_var,
1110                    body: result,
1111                    island_id: self.current_island,
1112                });
1113            } else {
1114                // Non-donkey: wrap with ∃ INSIDE the restriction
1115                result = self.wrap_donkey_in_restriction(result, *donkey_var, *wide_neg);
1116            }
1117        }
1118        self.donkey_bindings.clear();
1119
1120        self.in_negative_quantifier = was_in_negative_quantifier;
1121        Ok(result)
1122    }
1123
1124    fn parse_restriction(&mut self, var_name: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1125        let mut conditions: Vec<&'a LogicExpr<'a>> = Vec::new();
1126
1127        loop {
1128            if self.is_at_end() {
1129                break;
1130            }
1131
1132            let is_adjective = matches!(self.peek().kind, TokenType::Adjective(_));
1133            if !is_adjective {
1134                break;
1135            }
1136
1137            let next_is_content = if self.current + 1 < self.tokens.len() {
1138                matches!(
1139                    self.tokens[self.current + 1].kind,
1140                    TokenType::Noun(_) | TokenType::Adjective(_) | TokenType::ProperName(_)
1141                )
1142            } else {
1143                false
1144            };
1145
1146            if next_is_content {
1147                if let TokenType::Adjective(adj) = self.advance().kind.clone() {
1148                    conditions.push(self.ctx.exprs.alloc(LogicExpr::Predicate {
1149                        name: adj,
1150                        args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1151                        world: None,
1152                    }));
1153                }
1154            } else {
1155                break;
1156            }
1157        }
1158
1159        let noun = self.consume_content_word()?;
1160        conditions.push(self.ctx.exprs.alloc(LogicExpr::Predicate {
1161            name: noun,
1162            args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1163            world: None,
1164        }));
1165
1166        while self.check(&TokenType::That) || self.check(&TokenType::Who) {
1167            self.advance();
1168            let clause_pred = self.parse_relative_clause(var_name)?;
1169            conditions.push(clause_pred);
1170        }
1171
1172        self.combine_with_and(conditions)
1173    }
1174
1175    fn parse_verb_phrase_for_restriction(&mut self, var_name: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1176        let var_term = Term::Variable(var_name);
1177        let verb = self.consume_verb();
1178        let verb_str_owned = self.interner.resolve(verb).to_string();
1179
1180        // Check EARLY if verb is lexically negative (e.g., "lacks" -> "Have" with negation)
1181        // This determines whether donkey bindings need wide scope negation
1182        let (canonical_verb, is_negative) = get_canonical_verb(&verb_str_owned.to_lowercase())
1183            .map(|(lemma, neg)| (self.interner.intern(lemma), neg))
1184            .unwrap_or((verb, false));
1185
1186        // Determine if this binding needs wide scope negation wrapping
1187        let needs_wide_scope = is_negative && self.negative_scope_mode == NegativeScopeMode::Wide;
1188
1189        if Lexer::is_raising_verb(&verb_str_owned) && self.check_to() {
1190            self.advance();
1191            if self.check_verb() {
1192                let inf_verb = self.consume_verb();
1193                let inf_verb_str = self.interner.resolve(inf_verb).to_lowercase();
1194
1195                if inf_verb_str == "be" && self.check_content_word() {
1196                    let adj = self.consume_content_word()?;
1197                    let embedded = self.ctx.exprs.alloc(LogicExpr::Predicate {
1198                        name: adj,
1199                        args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1200                        world: None,
1201                    });
1202                    return Ok(self.ctx.exprs.alloc(LogicExpr::Scopal {
1203                        operator: verb,
1204                        body: embedded,
1205                    }));
1206                }
1207
1208                let embedded = self.ctx.exprs.alloc(LogicExpr::Predicate {
1209                    name: inf_verb,
1210                    args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1211                    world: None,
1212                });
1213                return Ok(self.ctx.exprs.alloc(LogicExpr::Scopal {
1214                    operator: verb,
1215                    body: embedded,
1216                }));
1217            } else if self.check(&TokenType::Is) || self.check(&TokenType::Are) {
1218                self.advance();
1219                if self.check_content_word() {
1220                    let adj = self.consume_content_word()?;
1221                    let embedded = self.ctx.exprs.alloc(LogicExpr::Predicate {
1222                        name: adj,
1223                        args: self.ctx.terms.alloc_slice([Term::Variable(var_name)]),
1224                        world: None,
1225                    });
1226                    return Ok(self.ctx.exprs.alloc(LogicExpr::Scopal {
1227                        operator: verb,
1228                        body: embedded,
1229                    }));
1230                }
1231            }
1232        }
1233
1234        let mut args = vec![var_term];
1235        let mut extra_conditions: Vec<&'a LogicExpr<'a>> = Vec::new();
1236
1237        if self.check(&TokenType::Reflexive) {
1238            self.advance();
1239            args.push(Term::Variable(var_name));
1240        } else if (self.check_content_word() || self.check_article()) && !self.check_verb() {
1241            if matches!(
1242                self.peek().kind,
1243                TokenType::Article(Definiteness::Indefinite)
1244            ) {
1245                self.advance();
1246                let noun = self.consume_content_word()?;
1247                let donkey_var = self.next_var_name();
1248
1249                if needs_wide_scope {
1250                    // === WIDE SCOPE MODE ===
1251                    // Build ¬∃y(Key(y) ∧ ∃e(Have(e) ∧ Agent(e,x) ∧ Theme(e,y))) directly
1252                    //
1253                    // We capture the binding HERE and return the complete structure.
1254                    // DO NOT push to donkey_bindings - that would leak y to outer scope.
1255
1256                    // Build: Key(y)
1257                    let restriction_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1258                        name: noun,
1259                        args: self.ctx.terms.alloc_slice([Term::Variable(donkey_var)]),
1260                        world: None,
1261                    });
1262
1263                    // Build: ∃e(Have(e) ∧ Agent(e,x) ∧ Theme(e,y)) using Neo-Davidsonian semantics
1264                    // IMPORTANT: Use build_verb_neo_event() for consistent Full-tier formatting
1265                    let inner_modifiers = self.collect_adverbs();
1266                    let verb_pred = self.build_verb_neo_event(
1267                        canonical_verb,
1268                        var_name,
1269                        Some(Term::Variable(donkey_var)),
1270                        inner_modifiers,
1271                    );
1272
1273                    // Build: Key(y) ∧ ∃e(Have(e) ∧ Agent(e,x) ∧ Theme(e,y))
1274                    let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1275                        left: restriction_pred,
1276                        op: TokenType::And,
1277                        right: verb_pred,
1278                    });
1279
1280                    // Build: ∃y(Key(y) ∧ ∃e(Have(e) ∧ ...))
1281                    let existential = self.ctx.exprs.alloc(LogicExpr::Quantifier {
1282                        kind: QuantifierKind::Existential,
1283                        variable: donkey_var,
1284                        body,
1285                        island_id: self.current_island,
1286                    });
1287
1288                    // Build: ¬∃y(Key(y) ∧ ∃e(Have(e) ∧ ...))
1289                    let negated_existential = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1290                        op: TokenType::Not,
1291                        operand: existential,
1292                    });
1293
1294                    // Return the complete wide-scope structure directly
1295                    return Ok(negated_existential);
1296                }
1297
1298                // === NARROW SCOPE MODE ===
1299                // Push binding for later processing (normal donkey binding flow)
1300                self.donkey_bindings.push((noun, donkey_var, false, false));
1301
1302                extra_conditions.push(self.ctx.exprs.alloc(LogicExpr::Predicate {
1303                    name: noun,
1304                    args: self.ctx.terms.alloc_slice([Term::Variable(donkey_var)]),
1305                    world: None,
1306                }));
1307
1308                args.push(Term::Variable(donkey_var));
1309            } else {
1310                let object = self.parse_noun_phrase(false)?;
1311
1312                if self.check(&TokenType::That) || self.check(&TokenType::Who) {
1313                    self.advance();
1314                    let nested_var = self.next_var_name();
1315                    let nested_rel = self.parse_relative_clause(nested_var)?;
1316
1317                    extra_conditions.push(self.ctx.exprs.alloc(LogicExpr::Predicate {
1318                        name: object.noun,
1319                        args: self.ctx.terms.alloc_slice([Term::Variable(nested_var)]),
1320                        world: None,
1321                    }));
1322                    extra_conditions.push(nested_rel);
1323                    args.push(Term::Variable(nested_var));
1324                } else {
1325                    args.push(Term::Constant(object.noun));
1326                }
1327            }
1328        }
1329
1330        while self.check_preposition() {
1331            self.advance();
1332            if self.check(&TokenType::Reflexive) {
1333                self.advance();
1334                args.push(Term::Variable(var_name));
1335            } else if self.check_content_word() || self.check_article() {
1336                let object = self.parse_noun_phrase(false)?;
1337
1338                if self.check(&TokenType::That) || self.check(&TokenType::Who) {
1339                    self.advance();
1340                    let nested_var = self.next_var_name();
1341                    let nested_rel = self.parse_relative_clause(nested_var)?;
1342                    extra_conditions.push(self.ctx.exprs.alloc(LogicExpr::Predicate {
1343                        name: object.noun,
1344                        args: self.ctx.terms.alloc_slice([Term::Variable(nested_var)]),
1345                        world: None,
1346                    }));
1347                    extra_conditions.push(nested_rel);
1348                    args.push(Term::Variable(nested_var));
1349                } else {
1350                    args.push(Term::Constant(object.noun));
1351                }
1352            }
1353        }
1354
1355        // Use the canonical verb determined at top of function
1356        // Extract object term from args if present (args[0] is subject, args[1] is object)
1357        let obj_term = if args.len() > 1 {
1358            Some(args.remove(1))
1359        } else {
1360            None
1361        };
1362        let final_modifiers = self.collect_adverbs();
1363        let base_pred = self.build_verb_neo_event(canonical_verb, var_name, obj_term, final_modifiers);
1364
1365        // Wrap in negation only for NARROW scope mode (de re reading)
1366        // Wide scope mode: negation handled via donkey binding flag in wrap_donkey_in_restriction
1367        // - Narrow: ∃y(Key(y) ∧ ¬Have(x,y)) - "missing ANY key"
1368        // - Wide:   ¬∃y(Key(y) ∧ Have(x,y)) - "has NO keys"
1369        let verb_pred = if is_negative && self.negative_scope_mode == NegativeScopeMode::Narrow {
1370            self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1371                op: TokenType::Not,
1372                operand: base_pred,
1373            })
1374        } else {
1375            base_pred
1376        };
1377
1378        if extra_conditions.is_empty() {
1379            Ok(verb_pred)
1380        } else {
1381            extra_conditions.push(verb_pred);
1382            self.combine_with_and(extra_conditions)
1383        }
1384    }
1385
1386    fn combine_with_and(&self, mut exprs: Vec<&'a LogicExpr<'a>>) -> ParseResult<&'a LogicExpr<'a>> {
1387        if exprs.is_empty() {
1388            return Err(ParseError {
1389                kind: ParseErrorKind::EmptyRestriction,
1390                span: self.current_span(),
1391            });
1392        }
1393        if exprs.len() == 1 {
1394            return Ok(exprs.remove(0));
1395        }
1396        let mut root = exprs.remove(0);
1397        for expr in exprs {
1398            root = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1399                left: root,
1400                op: TokenType::And,
1401                right: expr,
1402            });
1403        }
1404        Ok(root)
1405    }
1406
1407    fn wrap_with_definiteness_full(
1408        &mut self,
1409        np: &NounPhrase<'a>,
1410        predicate: &'a LogicExpr<'a>,
1411    ) -> ParseResult<&'a LogicExpr<'a>> {
1412        let result = self.wrap_with_definiteness_and_adjectives_and_pps(
1413            np.definiteness,
1414            np.noun,
1415            np.adjectives,
1416            np.pps,
1417            predicate,
1418        )?;
1419
1420        // If NP has a superlative, add the superlative constraint
1421        if let Some(adj) = np.superlative {
1422            let superlative_expr = self.ctx.exprs.alloc(LogicExpr::Superlative {
1423                adjective: adj,
1424                subject: self.ctx.terms.alloc(Term::Constant(np.noun)),
1425                domain: np.noun,
1426            });
1427            Ok(self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1428                left: result,
1429                op: TokenType::And,
1430                right: superlative_expr,
1431            }))
1432        } else {
1433            Ok(result)
1434        }
1435    }
1436
1437    fn wrap_with_definiteness(
1438        &mut self,
1439        definiteness: Option<Definiteness>,
1440        noun: Symbol,
1441        predicate: &'a LogicExpr<'a>,
1442    ) -> ParseResult<&'a LogicExpr<'a>> {
1443        self.wrap_with_definiteness_and_adjectives_and_pps(definiteness, noun, &[], &[], predicate)
1444    }
1445
1446    fn wrap_with_definiteness_and_adjectives(
1447        &mut self,
1448        definiteness: Option<Definiteness>,
1449        noun: Symbol,
1450        adjectives: &[Symbol],
1451        predicate: &'a LogicExpr<'a>,
1452    ) -> ParseResult<&'a LogicExpr<'a>> {
1453        self.wrap_with_definiteness_and_adjectives_and_pps(
1454            definiteness,
1455            noun,
1456            adjectives,
1457            &[],
1458            predicate,
1459        )
1460    }
1461
1462    fn wrap_with_definiteness_and_adjectives_and_pps(
1463        &mut self,
1464        definiteness: Option<Definiteness>,
1465        noun: Symbol,
1466        adjectives: &[Symbol],
1467        pps: &[&'a LogicExpr<'a>],
1468        predicate: &'a LogicExpr<'a>,
1469    ) -> ParseResult<&'a LogicExpr<'a>> {
1470        match definiteness {
1471            Some(Definiteness::Indefinite) => {
1472                let var = self.next_var_name();
1473
1474                // Introduce referent into DRS for cross-sentence anaphora
1475                // If inside a "No" quantifier, mark as NegationScope (inaccessible)
1476                let gender = Self::infer_noun_gender(self.interner.resolve(noun));
1477                let number = if Self::is_plural_noun(self.interner.resolve(noun)) {
1478                    Number::Plural
1479                } else {
1480                    Number::Singular
1481                };
1482                if self.in_negative_quantifier {
1483                    self.drs.introduce_referent_with_source(var, noun, gender, number, ReferentSource::NegationScope);
1484                } else {
1485                    self.drs.introduce_referent(var, noun, gender, number);
1486                }
1487
1488                let mut restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
1489                    name: noun,
1490                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1491                    world: None,
1492                });
1493
1494                for adj in adjectives {
1495                    let adj_str = self.interner.resolve(*adj).to_lowercase();
1496                    let adj_pred = if is_subsective(&adj_str) {
1497                        self.ctx.exprs.alloc(LogicExpr::Predicate {
1498                            name: *adj,
1499                            args: self.ctx.terms.alloc_slice([
1500                                Term::Variable(var),
1501                                Term::Intension(noun),
1502                            ]),
1503                            world: None,
1504                        })
1505                    } else {
1506                        self.ctx.exprs.alloc(LogicExpr::Predicate {
1507                            name: *adj,
1508                            args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1509                            world: None,
1510                        })
1511                    };
1512                    restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1513                        left: restriction,
1514                        op: TokenType::And,
1515                        right: adj_pred,
1516                    });
1517                }
1518
1519                for pp in pps {
1520                    let substituted_pp = self.substitute_pp_placeholder(pp, var);
1521                    restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1522                        left: restriction,
1523                        op: TokenType::And,
1524                        right: substituted_pp,
1525                    });
1526                }
1527
1528                let substituted = self.substitute_constant_with_var_sym(predicate, noun, var)?;
1529                let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1530                    left: restriction,
1531                    op: TokenType::And,
1532                    right: substituted,
1533                });
1534                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1535                    kind: QuantifierKind::Existential,
1536                    variable: var,
1537                    body,
1538                    island_id: self.current_island,
1539                }))
1540            }
1541            Some(Definiteness::Definite) => {
1542                let noun_str = self.interner.resolve(noun).to_string();
1543
1544                if Self::is_plural_noun(&noun_str) {
1545                    let singular = Self::singularize_noun(&noun_str);
1546                    let singular_sym = self.interner.intern(&singular);
1547                    let sigma_term = Term::Sigma(singular_sym);
1548
1549                    let substituted =
1550                        self.substitute_constant_with_sigma(predicate, noun, sigma_term)?;
1551
1552                    let verb_name = self.find_main_verb_name(predicate);
1553                    let is_collective = verb_name
1554                        .map(|v| {
1555                            let lemma = self.interner.resolve(v);
1556                            Lexer::is_collective_verb(lemma)
1557                                || (Lexer::is_mixed_verb(lemma) && self.collective_mode)
1558                        })
1559                        .unwrap_or(false);
1560
1561                    // Introduce definite plural referent to DRS for cross-sentence pronoun resolution
1562                    // E.g., "The dogs ran. They barked." - "they" refers to "dogs"
1563                    // Definite descriptions presuppose existence, so they should be globally accessible.
1564                    let gender = Gender::Unknown;  // Plural entities have unknown gender
1565                    self.drs.introduce_referent_with_source(singular_sym, singular_sym, gender, Number::Plural, ReferentSource::MainClause);
1566
1567                    if is_collective {
1568                        Ok(substituted)
1569                    } else {
1570                        Ok(self.ctx.exprs.alloc(LogicExpr::Distributive {
1571                            predicate: substituted,
1572                        }))
1573                    }
1574                } else {
1575                    let x = self.next_var_name();
1576                    let y = self.next_var_name();
1577
1578                    let mut restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
1579                        name: noun,
1580                        args: self.ctx.terms.alloc_slice([Term::Variable(x)]),
1581                        world: None,
1582                    });
1583
1584                    for adj in adjectives {
1585                        let adj_str = self.interner.resolve(*adj).to_lowercase();
1586                        let adj_pred = if is_subsective(&adj_str) {
1587                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1588                                name: *adj,
1589                                args: self.ctx.terms.alloc_slice([
1590                                    Term::Variable(x),
1591                                    Term::Intension(noun),
1592                                ]),
1593                                world: None,
1594                            })
1595                        } else {
1596                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1597                                name: *adj,
1598                                args: self.ctx.terms.alloc_slice([Term::Variable(x)]),
1599                                world: None,
1600                            })
1601                        };
1602                        restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1603                            left: restriction,
1604                            op: TokenType::And,
1605                            right: adj_pred,
1606                        });
1607                    }
1608
1609                    for pp in pps {
1610                        let substituted_pp = self.substitute_pp_placeholder(pp, x);
1611                        restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1612                            left: restriction,
1613                            op: TokenType::And,
1614                            right: substituted_pp,
1615                        });
1616                    }
1617
1618                    // Bridging anaphora: check if this noun is a part of a previously mentioned whole
1619                    // E.g., "I bought a car. The engine smoked." - engine is part of car
1620                    let has_prior_antecedent = self.drs.resolve_definite(
1621                        self.drs.current_box_index(),
1622                        noun
1623                    ).is_some();
1624
1625                    if !has_prior_antecedent {
1626                        if let Some((whole_var, _whole_name)) = self.drs.resolve_bridging(self.interner, noun) {
1627                            let part_of_sym = self.interner.intern("PartOf");
1628                            let part_of_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1629                                name: part_of_sym,
1630                                args: self.ctx.terms.alloc_slice([
1631                                    Term::Variable(x),
1632                                    Term::Constant(whole_var),
1633                                ]),
1634                                world: None,
1635                            });
1636                            restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1637                                left: restriction,
1638                                op: TokenType::And,
1639                                right: part_of_pred,
1640                            });
1641                        }
1642                    }
1643
1644                    // Introduce definite referent to DRS for cross-sentence pronoun resolution
1645                    // E.g., "The engine smoked. It broke." - "it" refers to "engine"
1646                    // Definite descriptions presuppose existence, so they should be globally
1647                    // accessible even when introduced inside conditional antecedents.
1648                    let gender = Self::infer_noun_gender(self.interner.resolve(noun));
1649                    let number = if Self::is_plural_noun(self.interner.resolve(noun)) {
1650                        Number::Plural
1651                    } else {
1652                        Number::Singular
1653                    };
1654                    self.drs.introduce_referent_with_source(x, noun, gender, number, ReferentSource::MainClause);
1655
1656                    let mut y_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
1657                        name: noun,
1658                        args: self.ctx.terms.alloc_slice([Term::Variable(y)]),
1659                        world: None,
1660                    });
1661                    for adj in adjectives {
1662                        let adj_str = self.interner.resolve(*adj).to_lowercase();
1663                        let adj_pred = if is_subsective(&adj_str) {
1664                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1665                                name: *adj,
1666                                args: self.ctx.terms.alloc_slice([
1667                                    Term::Variable(y),
1668                                    Term::Intension(noun),
1669                                ]),
1670                                world: None,
1671                            })
1672                        } else {
1673                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1674                                name: *adj,
1675                                args: self.ctx.terms.alloc_slice([Term::Variable(y)]),
1676                                world: None,
1677                            })
1678                        };
1679                        y_restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1680                            left: y_restriction,
1681                            op: TokenType::And,
1682                            right: adj_pred,
1683                        });
1684                    }
1685
1686                    for pp in pps {
1687                        let substituted_pp = self.substitute_pp_placeholder(pp, y);
1688                        y_restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1689                            left: y_restriction,
1690                            op: TokenType::And,
1691                            right: substituted_pp,
1692                        });
1693                    }
1694
1695                    let identity = self.ctx.exprs.alloc(LogicExpr::Identity {
1696                        left: self.ctx.terms.alloc(Term::Variable(y)),
1697                        right: self.ctx.terms.alloc(Term::Variable(x)),
1698                    });
1699                    let uniqueness_body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1700                        left: y_restriction,
1701                        op: TokenType::Implies,
1702                        right: identity,
1703                    });
1704                    let uniqueness = self.ctx.exprs.alloc(LogicExpr::Quantifier {
1705                        kind: QuantifierKind::Universal,
1706                        variable: y,
1707                        body: uniqueness_body,
1708                        island_id: self.current_island,
1709                    });
1710
1711                    let main_pred = self.substitute_constant_with_var_sym(predicate, noun, x)?;
1712
1713                    let inner = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1714                        left: restriction,
1715                        op: TokenType::And,
1716                        right: uniqueness,
1717                    });
1718                    let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1719                        left: inner,
1720                        op: TokenType::And,
1721                        right: main_pred,
1722                    });
1723
1724                    Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1725                        kind: QuantifierKind::Existential,
1726                        variable: x,
1727                        body,
1728                        island_id: self.current_island,
1729                    }))
1730                }
1731            }
1732            Some(Definiteness::Proximal) | Some(Definiteness::Distal) => {
1733                let var = self.next_var_name();
1734
1735                let mut restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
1736                    name: noun,
1737                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1738                    world: None,
1739                });
1740
1741                let deictic_name = if matches!(definiteness, Some(Definiteness::Proximal)) {
1742                    self.interner.intern("Proximal")
1743                } else {
1744                    self.interner.intern("Distal")
1745                };
1746                let deictic_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1747                    name: deictic_name,
1748                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1749                    world: None,
1750                });
1751                restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1752                    left: restriction,
1753                    op: TokenType::And,
1754                    right: deictic_pred,
1755                });
1756
1757                for adj in adjectives {
1758                    let adj_str = self.interner.resolve(*adj).to_lowercase();
1759                    let adj_pred = if is_subsective(&adj_str) {
1760                        self.ctx.exprs.alloc(LogicExpr::Predicate {
1761                            name: *adj,
1762                            args: self.ctx.terms.alloc_slice([
1763                                Term::Variable(var),
1764                                Term::Intension(noun),
1765                            ]),
1766                            world: None,
1767                        })
1768                    } else {
1769                        self.ctx.exprs.alloc(LogicExpr::Predicate {
1770                            name: *adj,
1771                            args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1772                            world: None,
1773                        })
1774                    };
1775                    restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1776                        left: restriction,
1777                        op: TokenType::And,
1778                        right: adj_pred,
1779                    });
1780                }
1781
1782                for pp in pps {
1783                    let substituted_pp = self.substitute_pp_placeholder(pp, var);
1784                    restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1785                        left: restriction,
1786                        op: TokenType::And,
1787                        right: substituted_pp,
1788                    });
1789                }
1790
1791                let substituted = self.substitute_constant_with_var_sym(predicate, noun, var)?;
1792                let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1793                    left: restriction,
1794                    op: TokenType::And,
1795                    right: substituted,
1796                });
1797                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1798                    kind: QuantifierKind::Existential,
1799                    variable: var,
1800                    body,
1801                    island_id: self.current_island,
1802                }))
1803            }
1804            None => Ok(predicate),
1805        }
1806    }
1807
1808    fn wrap_with_definiteness_for_object(
1809        &mut self,
1810        definiteness: Option<Definiteness>,
1811        noun: Symbol,
1812        predicate: &'a LogicExpr<'a>,
1813    ) -> ParseResult<&'a LogicExpr<'a>> {
1814        match definiteness {
1815            Some(Definiteness::Indefinite) => {
1816                let var = self.next_var_name();
1817
1818                // Introduce referent into DRS for cross-sentence anaphora
1819                // If inside a "No" quantifier, mark as NegationScope (inaccessible)
1820                let gender = Self::infer_noun_gender(self.interner.resolve(noun));
1821                let number = if Self::is_plural_noun(self.interner.resolve(noun)) {
1822                    Number::Plural
1823                } else {
1824                    Number::Singular
1825                };
1826                if self.in_negative_quantifier {
1827                    self.drs.introduce_referent_with_source(var, noun, gender, number, ReferentSource::NegationScope);
1828                } else {
1829                    self.drs.introduce_referent(var, noun, gender, number);
1830                }
1831
1832                let type_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1833                    name: noun,
1834                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1835                    world: None,
1836                });
1837                let substituted = self.substitute_constant_with_var(predicate, noun, var)?;
1838                let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1839                    left: type_pred,
1840                    op: TokenType::And,
1841                    right: substituted,
1842                });
1843                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1844                    kind: QuantifierKind::Existential,
1845                    variable: var,
1846                    body,
1847                    island_id: self.current_island,
1848                }))
1849            }
1850            Some(Definiteness::Definite) => {
1851                let x = self.next_var_name();
1852                let y = self.next_var_name();
1853
1854                let type_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1855                    name: noun,
1856                    args: self.ctx.terms.alloc_slice([Term::Variable(x)]),
1857                    world: None,
1858                });
1859
1860                let identity = self.ctx.exprs.alloc(LogicExpr::Identity {
1861                    left: self.ctx.terms.alloc(Term::Variable(y)),
1862                    right: self.ctx.terms.alloc(Term::Variable(x)),
1863                });
1864                let inner_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1865                    name: noun,
1866                    args: self.ctx.terms.alloc_slice([Term::Variable(y)]),
1867                    world: None,
1868                });
1869                let uniqueness_body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1870                    left: inner_pred,
1871                    op: TokenType::Implies,
1872                    right: identity,
1873                });
1874                let uniqueness = self.ctx.exprs.alloc(LogicExpr::Quantifier {
1875                    kind: QuantifierKind::Universal,
1876                    variable: y,
1877                    body: uniqueness_body,
1878                    island_id: self.current_island,
1879                });
1880
1881                let main_pred = self.substitute_constant_with_var(predicate, noun, x)?;
1882
1883                let type_and_unique = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1884                    left: type_pred,
1885                    op: TokenType::And,
1886                    right: uniqueness,
1887                });
1888                let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1889                    left: type_and_unique,
1890                    op: TokenType::And,
1891                    right: main_pred,
1892                });
1893
1894                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1895                    kind: QuantifierKind::Existential,
1896                    variable: x,
1897                    body,
1898                    island_id: self.current_island,
1899                }))
1900            }
1901            Some(Definiteness::Proximal) | Some(Definiteness::Distal) => {
1902                let var = self.next_var_name();
1903
1904                let mut restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
1905                    name: noun,
1906                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1907                    world: None,
1908                });
1909
1910                let deictic_name = if matches!(definiteness, Some(Definiteness::Proximal)) {
1911                    self.interner.intern("Proximal")
1912                } else {
1913                    self.interner.intern("Distal")
1914                };
1915                let deictic_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1916                    name: deictic_name,
1917                    args: self.ctx.terms.alloc_slice([Term::Variable(var)]),
1918                    world: None,
1919                });
1920                restriction = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1921                    left: restriction,
1922                    op: TokenType::And,
1923                    right: deictic_pred,
1924                });
1925
1926                let substituted = self.substitute_constant_with_var(predicate, noun, var)?;
1927                let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1928                    left: restriction,
1929                    op: TokenType::And,
1930                    right: substituted,
1931                });
1932                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
1933                    kind: QuantifierKind::Existential,
1934                    variable: var,
1935                    body,
1936                    island_id: self.current_island,
1937                }))
1938            }
1939            None => Ok(predicate),
1940        }
1941    }
1942
1943    fn substitute_pp_placeholder(&mut self, pp: &'a LogicExpr<'a>, var: Symbol) -> &'a LogicExpr<'a> {
1944        let placeholder = self.interner.intern("_PP_SELF_");
1945        match pp {
1946            LogicExpr::Predicate { name, args, .. } => {
1947                let new_args: Vec<Term<'a>> = args
1948                    .iter()
1949                    .map(|arg| match arg {
1950                        Term::Variable(v) if *v == placeholder => Term::Variable(var),
1951                        other => *other,
1952                    })
1953                    .collect();
1954                self.ctx.exprs.alloc(LogicExpr::Predicate {
1955                    name: *name,
1956                    args: self.ctx.terms.alloc_slice(new_args),
1957                    world: None,
1958                })
1959            }
1960            _ => pp,
1961        }
1962    }
1963
1964    fn substitute_constant_with_var(
1965        &self,
1966        expr: &'a LogicExpr<'a>,
1967        constant_name: Symbol,
1968        var_name: Symbol,
1969    ) -> ParseResult<&'a LogicExpr<'a>> {
1970        match expr {
1971            LogicExpr::Predicate { name, args, .. } => {
1972                let new_args: Vec<Term<'a>> = args
1973                    .iter()
1974                    .map(|arg| match arg {
1975                        Term::Constant(c) if *c == constant_name => Term::Variable(var_name),
1976                        Term::Constant(c) => Term::Constant(*c),
1977                        Term::Variable(v) => Term::Variable(*v),
1978                        Term::Function(n, a) => Term::Function(*n, *a),
1979                        Term::Group(m) => Term::Group(*m),
1980                        Term::Possessed { possessor, possessed } => Term::Possessed {
1981                            possessor: *possessor,
1982                            possessed: *possessed,
1983                        },
1984                        Term::Sigma(p) => Term::Sigma(*p),
1985                        Term::Intension(p) => Term::Intension(*p),
1986                        Term::Proposition(e) => Term::Proposition(*e),
1987                        Term::Value { kind, unit, dimension } => Term::Value {
1988                            kind: *kind,
1989                            unit: *unit,
1990                            dimension: *dimension,
1991                        },
1992                    })
1993                    .collect();
1994                Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1995                    name: *name,
1996                    args: self.ctx.terms.alloc_slice(new_args),
1997                    world: None,
1998                }))
1999            }
2000            LogicExpr::Temporal { operator, body } => Ok(self.ctx.exprs.alloc(LogicExpr::Temporal {
2001                operator: *operator,
2002                body: self.substitute_constant_with_var(body, constant_name, var_name)?,
2003            })),
2004            LogicExpr::Aspectual { operator, body } => Ok(self.ctx.exprs.alloc(LogicExpr::Aspectual {
2005                operator: *operator,
2006                body: self.substitute_constant_with_var(body, constant_name, var_name)?,
2007            })),
2008            LogicExpr::UnaryOp { op, operand } => Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
2009                op: op.clone(),
2010                operand: self.substitute_constant_with_var(operand, constant_name, var_name)?,
2011            })),
2012            LogicExpr::BinaryOp { left, op, right } => Ok(self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2013                left: self.substitute_constant_with_var(left, constant_name, var_name)?,
2014                op: op.clone(),
2015                right: self.substitute_constant_with_var(right, constant_name, var_name)?,
2016            })),
2017            LogicExpr::Event { predicate, adverbs } => Ok(self.ctx.exprs.alloc(LogicExpr::Event {
2018                predicate: self.substitute_constant_with_var(predicate, constant_name, var_name)?,
2019                adverbs: *adverbs,
2020            })),
2021            LogicExpr::TemporalAnchor { anchor, body } => {
2022                Ok(self.ctx.exprs.alloc(LogicExpr::TemporalAnchor {
2023                    anchor: *anchor,
2024                    body: self.substitute_constant_with_var(body, constant_name, var_name)?,
2025                }))
2026            }
2027            LogicExpr::NeoEvent(data) => {
2028                // Substitute constants in thematic roles (Agent, Theme, etc.)
2029                let new_roles: Vec<(crate::ast::ThematicRole, Term<'a>)> = data
2030                    .roles
2031                    .iter()
2032                    .map(|(role, term)| {
2033                        let new_term = match term {
2034                            Term::Constant(c) if *c == constant_name => Term::Variable(var_name),
2035                            Term::Constant(c) => Term::Constant(*c),
2036                            Term::Variable(v) => Term::Variable(*v),
2037                            Term::Function(n, a) => Term::Function(*n, *a),
2038                            Term::Group(m) => Term::Group(*m),
2039                            Term::Possessed { possessor, possessed } => Term::Possessed {
2040                                possessor: *possessor,
2041                                possessed: *possessed,
2042                            },
2043                            Term::Sigma(p) => Term::Sigma(*p),
2044                            Term::Intension(p) => Term::Intension(*p),
2045                            Term::Proposition(e) => Term::Proposition(*e),
2046                            Term::Value { kind, unit, dimension } => Term::Value {
2047                                kind: *kind,
2048                                unit: *unit,
2049                                dimension: *dimension,
2050                            },
2051                        };
2052                        (*role, new_term)
2053                    })
2054                    .collect();
2055                Ok(self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(crate::ast::NeoEventData {
2056                    event_var: data.event_var,
2057                    verb: data.verb,
2058                    roles: self.ctx.roles.alloc_slice(new_roles),
2059                    modifiers: data.modifiers,
2060                    suppress_existential: data.suppress_existential,
2061                    world: None,
2062                }))))
2063            }
2064            // Recurse into nested quantifiers to substitute constants in their bodies
2065            LogicExpr::Quantifier { kind, variable, body, island_id } => {
2066                let new_body = self.substitute_constant_with_var(body, constant_name, var_name)?;
2067                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
2068                    kind: *kind,
2069                    variable: *variable,
2070                    body: new_body,
2071                    island_id: *island_id,
2072                }))
2073            }
2074            _ => Ok(expr),
2075        }
2076    }
2077
2078    fn substitute_constant_with_var_sym(
2079        &self,
2080        expr: &'a LogicExpr<'a>,
2081        constant_name: Symbol,
2082        var_name: Symbol,
2083    ) -> ParseResult<&'a LogicExpr<'a>> {
2084        self.substitute_constant_with_var(expr, constant_name, var_name)
2085    }
2086
2087    fn substitute_constant_with_sigma(
2088        &self,
2089        expr: &'a LogicExpr<'a>,
2090        constant_name: Symbol,
2091        sigma_term: Term<'a>,
2092    ) -> ParseResult<&'a LogicExpr<'a>> {
2093        match expr {
2094            LogicExpr::Predicate { name, args, .. } => {
2095                let new_args: Vec<Term<'a>> = args
2096                    .iter()
2097                    .map(|arg| match arg {
2098                        Term::Constant(c) if *c == constant_name => sigma_term.clone(),
2099                        Term::Constant(c) => Term::Constant(*c),
2100                        Term::Variable(v) => Term::Variable(*v),
2101                        Term::Function(n, a) => Term::Function(*n, *a),
2102                        Term::Group(m) => Term::Group(*m),
2103                        Term::Possessed { possessor, possessed } => Term::Possessed {
2104                            possessor: *possessor,
2105                            possessed: *possessed,
2106                        },
2107                        Term::Sigma(p) => Term::Sigma(*p),
2108                        Term::Intension(p) => Term::Intension(*p),
2109                        Term::Proposition(e) => Term::Proposition(*e),
2110                        Term::Value { kind, unit, dimension } => Term::Value {
2111                            kind: *kind,
2112                            unit: *unit,
2113                            dimension: *dimension,
2114                        },
2115                    })
2116                    .collect();
2117                Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
2118                    name: *name,
2119                    args: self.ctx.terms.alloc_slice(new_args),
2120                    world: None,
2121                }))
2122            }
2123            LogicExpr::Temporal { operator, body } => Ok(self.ctx.exprs.alloc(LogicExpr::Temporal {
2124                operator: *operator,
2125                body: self.substitute_constant_with_sigma(body, constant_name, sigma_term)?,
2126            })),
2127            LogicExpr::Aspectual { operator, body } => Ok(self.ctx.exprs.alloc(LogicExpr::Aspectual {
2128                operator: *operator,
2129                body: self.substitute_constant_with_sigma(body, constant_name, sigma_term)?,
2130            })),
2131            LogicExpr::UnaryOp { op, operand } => Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
2132                op: op.clone(),
2133                operand: self.substitute_constant_with_sigma(operand, constant_name, sigma_term)?,
2134            })),
2135            LogicExpr::BinaryOp { left, op, right } => Ok(self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2136                left: self.substitute_constant_with_sigma(
2137                    left,
2138                    constant_name,
2139                    sigma_term.clone(),
2140                )?,
2141                op: op.clone(),
2142                right: self.substitute_constant_with_sigma(right, constant_name, sigma_term)?,
2143            })),
2144            LogicExpr::Event { predicate, adverbs } => Ok(self.ctx.exprs.alloc(LogicExpr::Event {
2145                predicate: self.substitute_constant_with_sigma(
2146                    predicate,
2147                    constant_name,
2148                    sigma_term,
2149                )?,
2150                adverbs: *adverbs,
2151            })),
2152            LogicExpr::TemporalAnchor { anchor, body } => {
2153                Ok(self.ctx.exprs.alloc(LogicExpr::TemporalAnchor {
2154                    anchor: *anchor,
2155                    body: self.substitute_constant_with_sigma(body, constant_name, sigma_term)?,
2156                }))
2157            }
2158            LogicExpr::NeoEvent(data) => {
2159                let new_roles: Vec<(crate::ast::ThematicRole, Term<'a>)> = data
2160                    .roles
2161                    .iter()
2162                    .map(|(role, term)| {
2163                        let new_term = match term {
2164                            Term::Constant(c) if *c == constant_name => sigma_term.clone(),
2165                            Term::Constant(c) => Term::Constant(*c),
2166                            Term::Variable(v) => Term::Variable(*v),
2167                            Term::Function(n, a) => Term::Function(*n, *a),
2168                            Term::Group(m) => Term::Group(*m),
2169                            Term::Possessed { possessor, possessed } => Term::Possessed {
2170                                possessor: *possessor,
2171                                possessed: *possessed,
2172                            },
2173                            Term::Sigma(p) => Term::Sigma(*p),
2174                            Term::Intension(p) => Term::Intension(*p),
2175                            Term::Proposition(e) => Term::Proposition(*e),
2176                            Term::Value { kind, unit, dimension } => Term::Value {
2177                                kind: *kind,
2178                                unit: *unit,
2179                                dimension: *dimension,
2180                            },
2181                        };
2182                        (*role, new_term)
2183                    })
2184                    .collect();
2185                Ok(self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(crate::ast::NeoEventData {
2186                    event_var: data.event_var,
2187                    verb: data.verb,
2188                    roles: self.ctx.roles.alloc_slice(new_roles),
2189                    modifiers: data.modifiers,
2190                    suppress_existential: data.suppress_existential,
2191                    world: None,
2192                }))))
2193            }
2194            LogicExpr::Distributive { predicate } => Ok(self.ctx.exprs.alloc(LogicExpr::Distributive {
2195                predicate: self.substitute_constant_with_sigma(predicate, constant_name, sigma_term)?,
2196            })),
2197            _ => Ok(expr),
2198        }
2199    }
2200
2201    fn find_main_verb_name(&self, expr: &LogicExpr<'a>) -> Option<Symbol> {
2202        match expr {
2203            LogicExpr::Predicate { name, .. } => Some(*name),
2204            LogicExpr::NeoEvent(data) => Some(data.verb),
2205            LogicExpr::Temporal { body, .. } => self.find_main_verb_name(body),
2206            LogicExpr::Aspectual { body, .. } => self.find_main_verb_name(body),
2207            LogicExpr::Event { predicate, .. } => self.find_main_verb_name(predicate),
2208            LogicExpr::TemporalAnchor { body, .. } => self.find_main_verb_name(body),
2209            LogicExpr::UnaryOp { operand, .. } => self.find_main_verb_name(operand),
2210            LogicExpr::BinaryOp { left, .. } => self.find_main_verb_name(left),
2211            _ => None,
2212        }
2213    }
2214
2215    fn transform_cardinal_to_group(&mut self, expr: &'a LogicExpr<'a>) -> ParseResult<&'a LogicExpr<'a>> {
2216        match expr {
2217            LogicExpr::Quantifier { kind: QuantifierKind::Cardinal(n), variable, body, .. } => {
2218                let group_var = self.interner.intern("g");
2219                let member_var = *variable;
2220
2221                // Extract the restriction (first conjunct) and the body (rest)
2222                // The structure is: restriction ∧ body_rest
2223                let (restriction, body_rest) = match body {
2224                    LogicExpr::BinaryOp { left, op: TokenType::And, right } => (*left, *right),
2225                    _ => return Ok(expr),
2226                };
2227
2228                // Substitute the member variable with the group variable in the body
2229                let transformed_body = self.substitute_constant_with_var_sym(body_rest, member_var, group_var)?;
2230
2231                Ok(self.ctx.exprs.alloc(LogicExpr::GroupQuantifier {
2232                    group_var,
2233                    count: *n,
2234                    member_var,
2235                    restriction,
2236                    body: transformed_body,
2237                }))
2238            }
2239            // Recursively transform nested expressions
2240            LogicExpr::Temporal { operator, body } => {
2241                let transformed = self.transform_cardinal_to_group(body)?;
2242                Ok(self.ctx.exprs.alloc(LogicExpr::Temporal {
2243                    operator: *operator,
2244                    body: transformed,
2245                }))
2246            }
2247            LogicExpr::Aspectual { operator, body } => {
2248                let transformed = self.transform_cardinal_to_group(body)?;
2249                Ok(self.ctx.exprs.alloc(LogicExpr::Aspectual {
2250                    operator: *operator,
2251                    body: transformed,
2252                }))
2253            }
2254            LogicExpr::UnaryOp { op, operand } => {
2255                let transformed = self.transform_cardinal_to_group(operand)?;
2256                Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
2257                    op: op.clone(),
2258                    operand: transformed,
2259                }))
2260            }
2261            LogicExpr::BinaryOp { left, op, right } => {
2262                let transformed_left = self.transform_cardinal_to_group(left)?;
2263                let transformed_right = self.transform_cardinal_to_group(right)?;
2264                Ok(self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2265                    left: transformed_left,
2266                    op: op.clone(),
2267                    right: transformed_right,
2268                }))
2269            }
2270            LogicExpr::Distributive { predicate } => {
2271                let transformed = self.transform_cardinal_to_group(predicate)?;
2272                Ok(self.ctx.exprs.alloc(LogicExpr::Distributive {
2273                    predicate: transformed,
2274                }))
2275            }
2276            LogicExpr::Quantifier { kind, variable, body, island_id } => {
2277                let transformed = self.transform_cardinal_to_group(body)?;
2278                Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
2279                    kind: kind.clone(),
2280                    variable: *variable,
2281                    body: transformed,
2282                    island_id: *island_id,
2283                }))
2284            }
2285            _ => Ok(expr),
2286        }
2287    }
2288
2289    fn build_verb_neo_event(
2290        &mut self,
2291        verb: Symbol,
2292        subject_var: Symbol,
2293        object: Option<Term<'a>>,
2294        modifiers: Vec<Symbol>,
2295    ) -> &'a LogicExpr<'a> {
2296        let event_var = self.get_event_var();
2297
2298        // Check if verb is unaccusative (intransitive subject is Theme, not Agent)
2299        let verb_str = self.interner.resolve(verb).to_lowercase();
2300        let is_unaccusative = lookup_verb_db(&verb_str)
2301            .map(|meta| meta.features.contains(&Feature::Unaccusative))
2302            .unwrap_or(false);
2303
2304        // Determine subject role: unaccusative verbs without object use Theme
2305        let has_object = object.is_some();
2306        let subject_role = if is_unaccusative && !has_object {
2307            ThematicRole::Theme
2308        } else {
2309            ThematicRole::Agent
2310        };
2311
2312        // Build roles vector
2313        let mut roles = vec![(subject_role, Term::Variable(subject_var))];
2314        if let Some(obj_term) = object {
2315            roles.push((ThematicRole::Theme, obj_term));
2316        }
2317
2318        // Create NeoEventData with suppress_existential: false
2319        // Each quantified individual gets their own event (distributive reading)
2320        self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
2321            event_var,
2322            verb,
2323            roles: self.ctx.roles.alloc_slice(roles),
2324            modifiers: self.ctx.syms.alloc_slice(modifiers),
2325            suppress_existential: false,
2326            world: None,
2327        })))
2328    }
2329}
2330
2331// Helper methods for donkey binding scope handling
2332impl<'a, 'ctx, 'int> Parser<'a, 'ctx, 'int> {
2333    /// Check if an expression mentions a specific variable
2334    fn expr_mentions_var(&self, expr: &LogicExpr<'a>, var: Symbol) -> bool {
2335        match expr {
2336            LogicExpr::Predicate { args, .. } => {
2337                args.iter().any(|term| self.term_mentions_var(term, var))
2338            }
2339            LogicExpr::BinaryOp { left, right, .. } => {
2340                self.expr_mentions_var(left, var) || self.expr_mentions_var(right, var)
2341            }
2342            LogicExpr::UnaryOp { operand, .. } => self.expr_mentions_var(operand, var),
2343            LogicExpr::Quantifier { body, .. } => self.expr_mentions_var(body, var),
2344            LogicExpr::NeoEvent(data) => {
2345                data.roles.iter().any(|(_, term)| self.term_mentions_var(term, var))
2346            }
2347            LogicExpr::Temporal { body, .. } => self.expr_mentions_var(body, var),
2348            LogicExpr::Aspectual { body, .. } => self.expr_mentions_var(body, var),
2349            LogicExpr::Event { predicate, .. } => self.expr_mentions_var(predicate, var),
2350            LogicExpr::Modal { operand, .. } => self.expr_mentions_var(operand, var),
2351            LogicExpr::Scopal { body, .. } => self.expr_mentions_var(body, var),
2352            _ => false,
2353        }
2354    }
2355
2356    fn term_mentions_var(&self, term: &Term<'a>, var: Symbol) -> bool {
2357        match term {
2358            Term::Variable(v) => *v == var,
2359            Term::Function(_, args) => args.iter().any(|t| self.term_mentions_var(t, var)),
2360            _ => false,
2361        }
2362    }
2363
2364    /// Collect all conjuncts from a conjunction tree
2365    fn collect_conjuncts(&self, expr: &'a LogicExpr<'a>) -> Vec<&'a LogicExpr<'a>> {
2366        match expr {
2367            LogicExpr::BinaryOp { left, op: TokenType::And, right } => {
2368                let mut result = self.collect_conjuncts(left);
2369                result.extend(self.collect_conjuncts(right));
2370                result
2371            }
2372            _ => vec![expr],
2373        }
2374    }
2375
2376    /// Wrap unused donkey bindings inside the restriction/body of a quantifier structure.
2377    ///
2378    /// For universals (implications):
2379    ///   Transform: ∀x((P(x) ∧ Q(y)) → R(x)) with unused y
2380    ///   Into:      ∀x((P(x) ∧ ∃y(Q(y))) → R(x))
2381    ///
2382    /// For existentials (conjunctions):
2383    ///   Transform: ∃x(P(x) ∧ Q(y) ∧ R(x)) with unused y
2384    ///   Into:      ∃x(P(x) ∧ ∃y(Q(y)) ∧ R(x))
2385    ///
2386    /// If wide_scope_negation is true, wrap the existential in negation:
2387    ///   Into:      ∀x((P(x) ∧ ¬∃y(Q(y))) → R(x))
2388    fn wrap_donkey_in_restriction(
2389        &self,
2390        body: &'a LogicExpr<'a>,
2391        donkey_var: Symbol,
2392        wide_scope_negation: bool,
2393    ) -> &'a LogicExpr<'a> {
2394        // Handle Quantifier wrapping first
2395        if let LogicExpr::Quantifier { kind, variable, body: inner_body, island_id } = body {
2396            let transformed = self.wrap_donkey_in_restriction(inner_body, donkey_var, wide_scope_negation);
2397            return self.ctx.exprs.alloc(LogicExpr::Quantifier {
2398                kind: kind.clone(),
2399                variable: *variable,
2400                body: transformed,
2401                island_id: *island_id,
2402            });
2403        }
2404
2405        // Handle implication (universal quantifiers)
2406        if let LogicExpr::BinaryOp { left, op: TokenType::Implies, right } = body {
2407            return self.wrap_in_implication(*left, *right, donkey_var, wide_scope_negation);
2408        }
2409
2410        // Handle conjunction (existential quantifiers)
2411        if let LogicExpr::BinaryOp { left: _, op: TokenType::And, right: _ } = body {
2412            return self.wrap_in_conjunction(body, donkey_var, wide_scope_negation);
2413        }
2414
2415        // Not a structure we can process
2416        body
2417    }
2418
2419    /// Wrap donkey binding in an implication structure (∀x(P(x) → Q(x)))
2420    fn wrap_in_implication(
2421        &self,
2422        restriction: &'a LogicExpr<'a>,
2423        consequent: &'a LogicExpr<'a>,
2424        donkey_var: Symbol,
2425        wide_scope_negation: bool,
2426    ) -> &'a LogicExpr<'a> {
2427        // Collect all conjuncts in the restriction
2428        let conjuncts = self.collect_conjuncts(restriction);
2429
2430        // Partition into those mentioning the donkey var and those not
2431        let (with_var, without_var): (Vec<_>, Vec<_>) = conjuncts
2432            .into_iter()
2433            .partition(|c| self.expr_mentions_var(c, donkey_var));
2434
2435        if with_var.is_empty() {
2436            // Variable not found in restriction, return original implication
2437            return self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2438                left: restriction,
2439                op: TokenType::Implies,
2440                right: consequent,
2441            });
2442        }
2443
2444        // Combine the "with var" conjuncts
2445        let with_var_combined = self.combine_conjuncts(&with_var);
2446
2447        // Wrap with existential
2448        let existential = self.ctx.exprs.alloc(LogicExpr::Quantifier {
2449            kind: QuantifierKind::Existential,
2450            variable: donkey_var,
2451            body: with_var_combined,
2452            island_id: self.current_island,
2453        });
2454
2455        // For wide scope negation (de dicto reading of "lacks"), wrap ∃ in ¬
2456        let wrapped = if wide_scope_negation {
2457            self.ctx.exprs.alloc(LogicExpr::UnaryOp {
2458                op: TokenType::Not,
2459                operand: existential,
2460            })
2461        } else {
2462            existential
2463        };
2464
2465        // Combine with "without var" conjuncts
2466        let new_restriction = if without_var.is_empty() {
2467            wrapped
2468        } else {
2469            let without_combined = self.combine_conjuncts(&without_var);
2470            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2471                left: without_combined,
2472                op: TokenType::And,
2473                right: wrapped,
2474            })
2475        };
2476
2477        // Rebuild the implication
2478        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2479            left: new_restriction,
2480            op: TokenType::Implies,
2481            right: consequent,
2482        })
2483    }
2484
2485    /// Wrap donkey binding in a conjunction structure (∃x(P(x) ∧ Q(x)))
2486    fn wrap_in_conjunction(
2487        &self,
2488        body: &'a LogicExpr<'a>,
2489        donkey_var: Symbol,
2490        wide_scope_negation: bool,
2491    ) -> &'a LogicExpr<'a> {
2492        // Collect all conjuncts
2493        let conjuncts = self.collect_conjuncts(body);
2494
2495        // Partition into those mentioning the donkey var and those not
2496        let (with_var, without_var): (Vec<_>, Vec<_>) = conjuncts
2497            .into_iter()
2498            .partition(|c| self.expr_mentions_var(c, donkey_var));
2499
2500        if with_var.is_empty() {
2501            // Variable not found, return unchanged
2502            return body;
2503        }
2504
2505        // Combine the "with var" conjuncts
2506        let with_var_combined = self.combine_conjuncts(&with_var);
2507
2508        // Wrap with existential
2509        let existential = self.ctx.exprs.alloc(LogicExpr::Quantifier {
2510            kind: QuantifierKind::Existential,
2511            variable: donkey_var,
2512            body: with_var_combined,
2513            island_id: self.current_island,
2514        });
2515
2516        // For wide scope negation (de dicto reading of "lacks"), wrap ∃ in ¬
2517        let wrapped = if wide_scope_negation {
2518            self.ctx.exprs.alloc(LogicExpr::UnaryOp {
2519                op: TokenType::Not,
2520                operand: existential,
2521            })
2522        } else {
2523            existential
2524        };
2525
2526        // Combine with "without var" conjuncts
2527        if without_var.is_empty() {
2528            wrapped
2529        } else {
2530            let without_combined = self.combine_conjuncts(&without_var);
2531            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2532                left: without_combined,
2533                op: TokenType::And,
2534                right: wrapped,
2535            })
2536        }
2537    }
2538
2539    fn combine_conjuncts(&self, conjuncts: &[&'a LogicExpr<'a>]) -> &'a LogicExpr<'a> {
2540        if conjuncts.is_empty() {
2541            panic!("Cannot combine empty conjuncts");
2542        }
2543        if conjuncts.len() == 1 {
2544            return conjuncts[0];
2545        }
2546        let mut result = conjuncts[0];
2547        for c in &conjuncts[1..] {
2548            result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
2549                left: result,
2550                op: TokenType::And,
2551                right: *c,
2552            });
2553        }
2554        result
2555    }
2556}