logicaffeine_language/parser/
verb.rs

1//! Verb phrase parsing with event semantics and thematic roles.
2//!
3//! This module handles the core verbal predication including:
4//!
5//! - **Intransitive verbs**: "John runs" → `∃e(Run(e) ∧ Agent(e,John))`
6//! - **Transitive verbs**: "John loves Mary" → `∃e(Love(e) ∧ Agent(e,John) ∧ Theme(e,Mary))`
7//! - **Ditransitive verbs**: "John gives Mary a book" → with Goal role
8//! - **Copula constructions**: "John is tall", "John is a doctor"
9//! - **Control verbs**: "John wants to run" → raising/control structures
10//! - **Plural subjects**: "John and Mary run", "John and Mary love each other"
11//! - **VP respectively**: "John and Mary love Sue and Bill respectively"
12//!
13//! # Neo-Davidsonian Event Semantics
14//!
15//! Verbs introduce event variables with thematic roles:
16//! - **Agent**: The doer of the action
17//! - **Theme/Patient**: The entity affected
18//! - **Goal/Recipient**: The target of transfer
19//! - **Instrument**: The tool used
20//!
21//! Events are represented using `LogicExpr::NeoEvent` with a verb symbol and
22//! a list of (ThematicRole, Term) pairs.
23
24use super::clause::ClauseParsing;
25use super::modal::ModalParsing;
26use super::noun::NounParsing;
27use super::pragmatics::PragmaticsParsing;
28use super::{ParseResult, Parser};
29use crate::ast::{
30    AspectOperator, LogicExpr, NeoEventData, NounPhrase, QuantifierKind, TemporalOperator, Term,
31    ThematicRole,
32};
33use crate::drs::{Gender, Number, ReferentSource};
34use crate::error::{ParseError, ParseErrorKind};
35use logicaffeine_base::Symbol;
36use crate::lexer::Lexer;
37use crate::lexicon::{Aspect, Definiteness, Time};
38use crate::token::{FocusKind, Span, TokenType};
39
40use crate::ast::Stmt;
41
42/// Trait for parsing verb phrases in declarative (logic) mode.
43///
44/// Provides methods for parsing predicates with subjects, control verbs,
45/// and plural/group constructions with Neo-Davidsonian event semantics.
46pub trait LogicVerbParsing<'a, 'ctx, 'int> {
47    /// Parses a verb phrase given the subject as a constant symbol.
48    fn parse_predicate_with_subject(&mut self, subject_symbol: Symbol)
49        -> ParseResult<&'a LogicExpr<'a>>;
50    /// Parses a verb phrase with subject as a bound variable.
51    fn parse_predicate_with_subject_as_var(&mut self, subject_symbol: Symbol)
52        -> ParseResult<&'a LogicExpr<'a>>;
53    /// Attempts to parse a plural subject ("John and Mary verb").
54    /// Returns `Ok(Some(expr))` on success, `Ok(None)` if not plural, `Err` on semantic error.
55    fn try_parse_plural_subject(&mut self, first_subject: &NounPhrase<'a>)
56        -> Result<Option<&'a LogicExpr<'a>>, ParseError>;
57    /// Parses control verb structures: "wants to VP", "persuaded X to VP".
58    fn parse_control_structure(
59        &mut self,
60        subject: &NounPhrase<'a>,
61        verb: Symbol,
62        verb_time: Time,
63    ) -> ParseResult<&'a LogicExpr<'a>>;
64    /// Checks if a verb is a control verb (want, try, persuade, etc.).
65    fn is_control_verb(&self, verb: Symbol) -> bool;
66    /// Builds a predicate for intransitive verbs with multiple subjects.
67    fn build_group_predicate(
68        &mut self,
69        subjects: &[Symbol],
70        verb: Symbol,
71        verb_time: Time,
72    ) -> &'a LogicExpr<'a>;
73    /// Builds a transitive predicate with group subject and group object.
74    fn build_group_transitive(
75        &mut self,
76        subjects: &[Symbol],
77        objects: &[Symbol],
78        verb: Symbol,
79        verb_time: Time,
80    ) -> &'a LogicExpr<'a>;
81}
82
83/// Trait for parsing verb phrases in imperative (LOGOS) mode.
84///
85/// Provides methods for parsing statements rather than logical propositions.
86pub trait ImperativeVerbParsing<'a, 'ctx, 'int> {
87    /// Parses a statement with the given subject symbol.
88    fn parse_statement_with_subject(&mut self, subject_symbol: Symbol)
89        -> ParseResult<Stmt<'a>>;
90}
91
92impl<'a, 'ctx, 'int> Parser<'a, 'ctx, 'int> {
93    fn parse_predicate_impl(
94        &mut self,
95        subject_symbol: Symbol,
96        as_variable: bool,
97    ) -> ParseResult<&'a LogicExpr<'a>> {
98        let subject_term = if as_variable {
99            Term::Variable(subject_symbol)
100        } else {
101            Term::Constant(subject_symbol)
102        };
103
104        // Weather verb + expletive "it" detection: "it rains" → ∃e(Rain(e))
105        let subject_str = self.interner.resolve(subject_symbol).to_lowercase();
106        if subject_str == "it" && self.check_verb() {
107            if let TokenType::Verb { lemma, time, .. } = &self.peek().kind {
108                let lemma_str = self.interner.resolve(*lemma);
109                if Lexer::is_weather_verb(lemma_str) {
110                    let verb = *lemma;
111                    let verb_time = *time;
112                    self.advance(); // consume the weather verb
113
114                    let event_var = self.get_event_var();
115                    let suppress_existential = self.drs.in_conditional_antecedent();
116                    if suppress_existential {
117                        let event_class = self.interner.intern("Event");
118                        self.drs.introduce_referent(event_var, event_class, Gender::Neuter, Number::Singular);
119                    }
120                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
121                        event_var,
122                        verb,
123                        roles: self.ctx.roles.alloc_slice(vec![]), // No thematic roles
124                        modifiers: self.ctx.syms.alloc_slice(vec![]),
125                        suppress_existential,
126                        world: None,
127                    })));
128
129                    return Ok(match verb_time {
130                        Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
131                            operator: TemporalOperator::Past,
132                            body: neo_event,
133                        }),
134                        Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
135                            operator: TemporalOperator::Future,
136                            body: neo_event,
137                        }),
138                        _ => neo_event,
139                    });
140                }
141            }
142        }
143
144        // Weather adjective + expletive "it" detection: "it is wet" → Wet
145        // Also handle "it's wet" where 's is Possessive token
146        if subject_str == "it" && (self.check(&TokenType::Is) || self.check(&TokenType::Was) || self.check(&TokenType::Possessive)) {
147            let saved_pos = self.current;
148            self.advance(); // consume copula
149
150            if self.check_content_word() {
151                let adj_lexeme = self.peek().lexeme;
152                let adj_str = self.interner.resolve(adj_lexeme).to_lowercase();
153
154                if let Some(meta) = crate::lexicon::lookup_adjective_db(&adj_str) {
155                    if meta.features.contains(&crate::lexicon::Feature::Weather) {
156                        let adj_sym = self.consume_content_word().unwrap_or(adj_lexeme);
157                        // Atmospheric predicate: "it is wet" → Wet
158                        return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
159                            name: adj_sym,
160                            args: self.ctx.terms.alloc_slice([]),
161                            world: None,
162                        }));
163                    }
164                }
165            }
166            // Not a weather adjective, restore position
167            self.current = saved_pos;
168        }
169
170        if self.check(&TokenType::Never) {
171            self.advance();
172            let verb = self.consume_verb();
173            let verb_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
174                name: verb,
175                args: self.ctx.terms.alloc_slice([subject_term]),
176                world: None,
177            });
178            return Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
179                op: TokenType::Not,
180                operand: verb_pred,
181            }));
182        }
183
184        if self.check_modal() {
185            return self.parse_aspect_chain_with_term(subject_term.clone());
186        }
187
188        if self.check_content_word() {
189            let next_word = self.interner.resolve(self.peek().lexeme).to_lowercase();
190            if next_word == "has" || next_word == "have" || next_word == "had" {
191                // Look ahead to distinguish perfect aspect ("has eaten") from possession ("has 3 children")
192                // Perfect aspect: has/have/had + verb
193                // Possession: has/have/had + number/noun
194                let is_perfect_aspect = if self.current + 1 < self.tokens.len() {
195                    let next_token = &self.tokens[self.current + 1].kind;
196                    matches!(
197                        next_token,
198                        TokenType::Verb { .. } | TokenType::Not
199                    ) && !matches!(next_token, TokenType::Number(_))
200                } else {
201                    false
202                };
203                if is_perfect_aspect {
204                    return self.parse_aspect_chain(subject_symbol);
205                }
206                // Otherwise, treat "has" as a main verb (possession) and continue below
207            }
208        }
209
210        if self.check(&TokenType::Had) {
211            return self.parse_aspect_chain(subject_symbol);
212        }
213
214        // Handle do-support: "I do/don't know who"
215        if self.check(&TokenType::Does) || self.check(&TokenType::Do) {
216            self.advance();
217            let is_negated = self.match_token(&[TokenType::Not]);
218
219            if self.check(&TokenType::Ever) {
220                self.advance();
221            }
222
223            if self.check_verb() {
224                let verb = self.consume_verb();
225
226                // Check for embedded wh-clause with sluicing: "I don't know who"
227                if self.check_wh_word() {
228                    let wh_token = self.advance().kind.clone();
229                    let is_who = matches!(wh_token, TokenType::Who);
230                    let is_what = matches!(wh_token, TokenType::What);
231
232                    let is_sluicing = self.is_at_end() ||
233                        self.check(&TokenType::Period) ||
234                        self.check(&TokenType::Comma);
235
236                    if is_sluicing {
237                        if let Some(template) = self.last_event_template.clone() {
238                            let wh_var = self.next_var_name();
239
240                            let roles: Vec<_> = if is_who {
241                                std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
242                                    .chain(template.non_agent_roles.iter().cloned())
243                                    .collect()
244                            } else if is_what {
245                                vec![
246                                    (ThematicRole::Agent, subject_term.clone()),
247                                    (ThematicRole::Theme, Term::Variable(wh_var)),
248                                ]
249                            } else {
250                                std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
251                                    .chain(template.non_agent_roles.iter().cloned())
252                                    .collect()
253                            };
254
255                            let event_var = self.get_event_var();
256                            let suppress_existential = self.drs.in_conditional_antecedent();
257                            let reconstructed = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
258                                event_var,
259                                verb: template.verb,
260                                roles: self.ctx.roles.alloc_slice(roles),
261                                modifiers: self.ctx.syms.alloc_slice(template.modifiers.clone()),
262                                suppress_existential,
263                                world: None,
264                            })));
265
266                            let question = self.ctx.exprs.alloc(LogicExpr::Question {
267                                wh_variable: wh_var,
268                                body: reconstructed,
269                            });
270
271                            let know_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
272                                event_var: self.get_event_var(),
273                                verb,
274                                roles: self.ctx.roles.alloc_slice(vec![
275                                    (ThematicRole::Agent, subject_term.clone()),
276                                    (ThematicRole::Theme, Term::Proposition(question)),
277                                ]),
278                                modifiers: self.ctx.syms.alloc_slice(vec![]),
279                                suppress_existential,
280                                world: None,
281                            })));
282
283                            let result = if is_negated {
284                                self.ctx.exprs.alloc(LogicExpr::UnaryOp {
285                                    op: TokenType::Not,
286                                    operand: know_event,
287                                })
288                            } else {
289                                know_event
290                            };
291
292                            return Ok(result);
293                        }
294                    }
295                }
296
297                // Regular do-support: "I do run" or "I don't run"
298                let roles: Vec<(ThematicRole, Term<'a>)> = vec![(ThematicRole::Agent, subject_term.clone())];
299                let modifiers: Vec<Symbol> = vec![];
300                let event_var = self.get_event_var();
301                let suppress_existential = self.drs.in_conditional_antecedent();
302
303                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
304                    event_var,
305                    verb,
306                    roles: self.ctx.roles.alloc_slice(roles),
307                    modifiers: self.ctx.syms.alloc_slice(modifiers),
308                    suppress_existential,
309                    world: None,
310                })));
311
312                if is_negated {
313                    return Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
314                        op: TokenType::Not,
315                        operand: neo_event,
316                    }));
317                }
318                return Ok(neo_event);
319            }
320        }
321
322        // Check for auxiliary (like "did" in "did not bark")
323        // BUT: "did it" should be parsed as verb "do" with object "it"
324        // We lookahead to check if this is truly an auxiliary usage
325        if self.check_auxiliary() && self.is_true_auxiliary_usage() {
326            let aux_time = if let TokenType::Auxiliary(time) = self.advance().kind {
327                time
328            } else {
329                Time::None
330            };
331            self.pending_time = Some(aux_time);
332
333            if self.match_token(&[TokenType::Not]) {
334                self.negative_depth += 1;
335
336                // Check for verb or "do" (TokenType::Do is separate from TokenType::Verb)
337                if self.check_verb() || self.check(&TokenType::Do) {
338                    let verb = if self.check(&TokenType::Do) {
339                        self.advance(); // consume "do"
340                        self.interner.intern("Do")
341                    } else {
342                        self.consume_verb()
343                    };
344
345                    if self.check_quantifier() {
346                        let quantifier_token = self.advance().kind.clone();
347                        let object_np = self.parse_noun_phrase(false)?;
348                        let obj_var = self.next_var_name();
349
350                        let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
351                            name: object_np.noun,
352                            args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
353                            world: None,
354                        });
355
356                        let verb_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
357                            name: verb,
358                            args: self
359                                .ctx
360                                .terms
361                                .alloc_slice([subject_term, Term::Variable(obj_var)]),
362                            world: None,
363                        });
364
365                        let (kind, body) = match quantifier_token {
366                            TokenType::Any => {
367                                if self.is_negative_context() {
368                                    (
369                                        QuantifierKind::Existential,
370                                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
371                                            left: obj_restriction,
372                                            op: TokenType::And,
373                                            right: verb_pred,
374                                        }),
375                                    )
376                                } else {
377                                    (
378                                        QuantifierKind::Universal,
379                                        self.ctx.exprs.alloc(LogicExpr::BinaryOp {
380                                            left: obj_restriction,
381                                            op: TokenType::Implies,
382                                            right: verb_pred,
383                                        }),
384                                    )
385                                }
386                            }
387                            TokenType::Some => (
388                                QuantifierKind::Existential,
389                                self.ctx.exprs.alloc(LogicExpr::BinaryOp {
390                                    left: obj_restriction,
391                                    op: TokenType::And,
392                                    right: verb_pred,
393                                }),
394                            ),
395                            TokenType::All => (
396                                QuantifierKind::Universal,
397                                self.ctx.exprs.alloc(LogicExpr::BinaryOp {
398                                    left: obj_restriction,
399                                    op: TokenType::Implies,
400                                    right: verb_pred,
401                                }),
402                            ),
403                            _ => (
404                                QuantifierKind::Existential,
405                                self.ctx.exprs.alloc(LogicExpr::BinaryOp {
406                                    left: obj_restriction,
407                                    op: TokenType::And,
408                                    right: verb_pred,
409                                }),
410                            ),
411                        };
412
413                        let quantified = self.ctx.exprs.alloc(LogicExpr::Quantifier {
414                            kind,
415                            variable: obj_var,
416                            body,
417                            island_id: self.current_island,
418                        });
419
420                        let effective_time = self.pending_time.take().unwrap_or(Time::None);
421                        let with_time = match effective_time {
422                            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
423                                operator: TemporalOperator::Past,
424                                body: quantified,
425                            }),
426                            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
427                                operator: TemporalOperator::Future,
428                                body: quantified,
429                            }),
430                            _ => quantified,
431                        };
432
433                        self.negative_depth -= 1;
434                        return Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
435                            op: TokenType::Not,
436                            operand: with_time,
437                        }));
438                    }
439
440                    if self.check_npi_object() {
441                        let npi_token = self.advance().kind.clone();
442                        let obj_var = self.next_var_name();
443
444                        let restriction_name = match npi_token {
445                            TokenType::Anything => "Thing",
446                            TokenType::Anyone => "Person",
447                            _ => "Thing",
448                        };
449
450                        let restriction_sym = self.interner.intern(restriction_name);
451                        let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
452                            name: restriction_sym,
453                            args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
454                            world: None,
455                        });
456
457                        let verb_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
458                            name: verb,
459                            args: self.ctx.terms.alloc_slice([subject_term, Term::Variable(obj_var)]),
460                            world: None,
461                        });
462
463                        let body = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
464                            left: obj_restriction,
465                            op: TokenType::And,
466                            right: verb_pred,
467                        });
468
469                        let quantified = self.ctx.exprs.alloc(LogicExpr::Quantifier {
470                            kind: QuantifierKind::Existential,
471                            variable: obj_var,
472                            body,
473                            island_id: self.current_island,
474                        });
475
476                        let effective_time = self.pending_time.take().unwrap_or(Time::None);
477                        let with_time = match effective_time {
478                            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
479                                operator: TemporalOperator::Past,
480                                body: quantified,
481                            }),
482                            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
483                                operator: TemporalOperator::Future,
484                                body: quantified,
485                            }),
486                            _ => quantified,
487                        };
488
489                        self.negative_depth -= 1;
490                        return Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
491                            op: TokenType::Not,
492                            operand: with_time,
493                        }));
494                    }
495
496                    let mut roles: Vec<(ThematicRole, Term<'a>)> =
497                        vec![(ThematicRole::Agent, subject_term)];
498
499                    // Check for object: NP, article+NP, or pronoun (like "it")
500                    if self.check_content_word() || self.check_article() || self.check_pronoun() {
501                        if self.check_pronoun() {
502                            // Handle pronoun object like "it" in "did not do it"
503                            let pronoun_token = self.advance().clone();
504                            let term = if let TokenType::Pronoun { gender, number, .. } = pronoun_token.kind {
505                                let resolved = self.resolve_pronoun(gender, number)?;
506                                match resolved {
507                                    super::ResolvedPronoun::Variable(s) => Term::Variable(s),
508                                    super::ResolvedPronoun::Constant(s) => Term::Constant(s),
509                                }
510                            } else {
511                                // Fallback to lexeme if somehow not a pronoun token
512                                Term::Constant(pronoun_token.lexeme)
513                            };
514                            roles.push((ThematicRole::Theme, term));
515                        } else {
516                            let object = self.parse_noun_phrase(false)?;
517                            let object_term = self.noun_phrase_to_term(&object);
518                            roles.push((ThematicRole::Theme, object_term));
519                        }
520                    }
521
522                    let event_var = self.get_event_var();
523                    let suppress_existential = self.drs.in_conditional_antecedent();
524                    let effective_time = self.pending_time.take().unwrap_or(Time::None);
525                    let mut modifiers = Vec::new();
526                    match effective_time {
527                        Time::Past => modifiers.push(self.interner.intern("Past")),
528                        Time::Future => modifiers.push(self.interner.intern("Future")),
529                        _ => {}
530                    }
531
532                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
533                        event_var,
534                        verb,
535                        roles: self.ctx.roles.alloc_slice(roles),
536                        modifiers: self.ctx.syms.alloc_slice(modifiers),
537                        suppress_existential,
538                        world: None,
539                    })));
540
541                    self.negative_depth -= 1;
542                    return Ok(self.ctx.exprs.alloc(LogicExpr::UnaryOp {
543                        op: TokenType::Not,
544                        operand: neo_event,
545                    }));
546                }
547
548                self.negative_depth -= 1;
549            }
550        }
551
552        if self.check(&TokenType::Is)
553            || self.check(&TokenType::Are)
554            || self.check(&TokenType::Was)
555            || self.check(&TokenType::Were)
556        {
557            let copula_time = if self.check(&TokenType::Was) || self.check(&TokenType::Were) {
558                Time::Past
559            } else {
560                Time::Present
561            };
562            self.advance();
563
564            // Check for negation: "was not caught", "is not happy"
565            let is_negated = self.check(&TokenType::Not);
566            if is_negated {
567                self.advance(); // consume "not"
568            }
569
570            // Check for temporal adverbs after copula: "is eventually Y", "is always Y", "is never Y"
571            let mut copula_temporal: Option<super::CopulaTemporal> = None;
572            if !is_negated {
573                if self.check(&TokenType::Never) {
574                    self.advance();
575                    copula_temporal = Some(super::CopulaTemporal::Never);
576                } else if let TokenType::Adverb(sym) | TokenType::ScopalAdverb(sym) | TokenType::TemporalAdverb(sym) = &self.peek().kind {
577                    let resolved = self.interner.resolve(*sym).to_string();
578                    if resolved == "Always" || resolved == "always" {
579                        self.advance();
580                        copula_temporal = Some(super::CopulaTemporal::Always);
581                    } else if resolved == "Eventually" || resolved == "eventually" {
582                        self.advance();
583                        copula_temporal = Some(super::CopulaTemporal::Eventually);
584                    }
585                }
586            }
587
588            if self.check_verb() {
589                let (verb, _verb_time, verb_aspect, verb_class) = self.consume_verb_with_metadata();
590
591                // Stative verbs cannot be progressive
592                if verb_class.is_stative() && verb_aspect == Aspect::Progressive {
593                    return Err(crate::error::ParseError {
594                        kind: crate::error::ParseErrorKind::StativeProgressiveConflict,
595                        span: self.current_span(),
596                    });
597                }
598
599                let predicate = self.ctx.exprs.alloc(LogicExpr::Predicate {
600                    name: verb,
601                    args: self.ctx.terms.alloc_slice([subject_term]),
602                    world: None,
603                });
604
605                let with_aspect = if verb_aspect == Aspect::Progressive {
606                    // Semelfactive + Progressive → Iterative
607                    let operator = if verb_class == crate::lexicon::VerbClass::Semelfactive {
608                        AspectOperator::Iterative
609                    } else {
610                        AspectOperator::Progressive
611                    };
612                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
613                        operator,
614                        body: predicate,
615                    })
616                } else {
617                    predicate
618                };
619
620                let with_time = if copula_time == Time::Past {
621                    self.ctx.exprs.alloc(LogicExpr::Temporal {
622                        operator: TemporalOperator::Past,
623                        body: with_aspect,
624                    })
625                } else {
626                    with_aspect
627                };
628
629                let with_neg = if is_negated {
630                    self.ctx.exprs.alloc(LogicExpr::UnaryOp {
631                        op: TokenType::Not,
632                        operand: with_time,
633                    })
634                } else {
635                    with_time
636                };
637
638                let result = match copula_temporal {
639                    Some(super::CopulaTemporal::Always) => {
640                        self.ctx.exprs.alloc(LogicExpr::Temporal {
641                            operator: TemporalOperator::Always,
642                            body: with_neg,
643                        })
644                    }
645                    Some(super::CopulaTemporal::Never) => {
646                        let negated = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
647                            op: TokenType::Not,
648                            operand: with_time,
649                        });
650                        self.ctx.exprs.alloc(LogicExpr::Temporal {
651                            operator: TemporalOperator::Always,
652                            body: negated,
653                        })
654                    }
655                    Some(super::CopulaTemporal::Eventually) => {
656                        self.ctx.exprs.alloc(LogicExpr::Temporal {
657                            operator: TemporalOperator::Eventually,
658                            body: with_neg,
659                        })
660                    }
661                    None => with_neg,
662                };
663
664                return Ok(result);
665            }
666
667            let predicate = self.consume_content_word()?;
668            let base_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
669                name: predicate,
670                args: self.ctx.terms.alloc_slice([subject_term]),
671                world: None,
672            });
673
674            let with_time = if copula_time == Time::Past {
675                self.ctx.exprs.alloc(LogicExpr::Temporal {
676                    operator: TemporalOperator::Past,
677                    body: base_pred,
678                })
679            } else {
680                base_pred
681            };
682
683            let with_neg = if is_negated {
684                self.ctx.exprs.alloc(LogicExpr::UnaryOp {
685                    op: TokenType::Not,
686                    operand: with_time,
687                })
688            } else {
689                with_time
690            };
691
692            let result = match copula_temporal {
693                Some(super::CopulaTemporal::Always) => {
694                    self.ctx.exprs.alloc(LogicExpr::Temporal {
695                        operator: TemporalOperator::Always,
696                        body: with_neg,
697                    })
698                }
699                Some(super::CopulaTemporal::Never) => {
700                    let negated = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
701                        op: TokenType::Not,
702                        operand: with_time,
703                    });
704                    self.ctx.exprs.alloc(LogicExpr::Temporal {
705                        operator: TemporalOperator::Always,
706                        body: negated,
707                    })
708                }
709                Some(super::CopulaTemporal::Eventually) => {
710                    self.ctx.exprs.alloc(LogicExpr::Temporal {
711                        operator: TemporalOperator::Eventually,
712                        body: with_neg,
713                    })
714                }
715                None => with_neg,
716            };
717
718            return Ok(result);
719        }
720
721        // Handle "did it" - when Auxiliary(Past) is used as a transitive verb (past of "do")
722        // This happens when we bypassed auxiliary handling because of lookahead
723        if self.check_auxiliary_as_main_verb() {
724            return self.parse_do_as_main_verb(subject_term);
725        }
726
727        if self.check_verb() {
728            let (mut verb, verb_time, verb_aspect, verb_class) = self.consume_verb_with_metadata();
729            let mut args = vec![subject_term.clone()];
730
731            // Check for embedded wh-clause: "I know who/what"
732            if self.check_wh_word() {
733                let wh_token = self.advance().kind.clone();
734
735                let is_who = matches!(wh_token, TokenType::Who);
736                let is_what = matches!(wh_token, TokenType::What);
737
738                // Check for sluicing: wh-word followed by terminator
739                let is_sluicing = self.is_at_end() ||
740                    self.check(&TokenType::Period) ||
741                    self.check(&TokenType::Comma);
742
743                if is_sluicing {
744                    if let Some(template) = self.last_event_template.clone() {
745                        let wh_var = self.next_var_name();
746
747                        let roles: Vec<_> = if is_who {
748                            std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
749                                .chain(template.non_agent_roles.iter().cloned())
750                                .collect()
751                        } else if is_what {
752                            vec![
753                                (ThematicRole::Agent, subject_term.clone()),
754                                (ThematicRole::Theme, Term::Variable(wh_var)),
755                            ]
756                        } else {
757                            std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
758                                .chain(template.non_agent_roles.iter().cloned())
759                                .collect()
760                        };
761
762                        let event_var = self.get_event_var();
763                        let suppress_existential = self.drs.in_conditional_antecedent();
764                        let reconstructed = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
765                            event_var,
766                            verb: template.verb,
767                            roles: self.ctx.roles.alloc_slice(roles),
768                            modifiers: self.ctx.syms.alloc_slice(template.modifiers.clone()),
769                            suppress_existential,
770                            world: None,
771                        })));
772
773                        let question = self.ctx.exprs.alloc(LogicExpr::Question {
774                            wh_variable: wh_var,
775                            body: reconstructed,
776                        });
777
778                        let know_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
779                            event_var: self.get_event_var(),
780                            verb,
781                            roles: self.ctx.roles.alloc_slice(vec![
782                                (ThematicRole::Agent, subject_term),
783                                (ThematicRole::Theme, Term::Proposition(question)),
784                            ]),
785                            modifiers: self.ctx.syms.alloc_slice(vec![]),
786                            suppress_existential,
787                            world: None,
788                        })));
789
790                        return Ok(know_event);
791                    }
792                }
793
794                // Non-sluicing: "I know who runs"
795                let embedded = self.parse_embedded_wh_clause()?;
796                let question = self.ctx.exprs.alloc(LogicExpr::Question {
797                    wh_variable: self.interner.intern("x"),
798                    body: embedded,
799                });
800
801                let suppress_existential = self.drs.in_conditional_antecedent();
802                let know_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
803                    event_var: self.get_event_var(),
804                    verb,
805                    roles: self.ctx.roles.alloc_slice(vec![
806                        (ThematicRole::Agent, subject_term),
807                        (ThematicRole::Theme, Term::Proposition(question)),
808                    ]),
809                    modifiers: self.ctx.syms.alloc_slice(vec![]),
810                    suppress_existential,
811                    world: None,
812                })));
813
814                return Ok(know_event);
815            }
816
817            let mut object_term: Option<Term<'a>> = None;
818            let mut second_object_term: Option<Term<'a>> = None;
819            let mut object_pps: &[&LogicExpr<'a>] = &[];  // PPs attached to object NP (for NP-attachment mode)
820            if self.check(&TokenType::Reflexive) {
821                self.advance();
822                let term = Term::Constant(subject_symbol);
823                object_term = Some(term);
824                args.push(term);
825            } else if self.check_pronoun() {
826                let token = self.advance().clone();
827                let (gender, number) = match &token.kind {
828                    TokenType::Pronoun { gender, number, .. } => (*gender, *number),
829                    TokenType::Ambiguous { primary, alternatives } => {
830                        if let TokenType::Pronoun { gender, number, .. } = **primary {
831                            (gender, number)
832                        } else {
833                            alternatives.iter().find_map(|t| {
834                                if let TokenType::Pronoun { gender, number, .. } = t {
835                                    Some((*gender, *number))
836                                } else {
837                                    None
838                                }
839                            }).unwrap_or((Gender::Unknown, Number::Singular))
840                        }
841                    }
842                    _ => (Gender::Unknown, Number::Singular),
843                };
844
845                let resolved = self.resolve_pronoun(gender, number)?;
846                let term = match resolved {
847                    super::ResolvedPronoun::Variable(s) => Term::Variable(s),
848                    super::ResolvedPronoun::Constant(s) => Term::Constant(s),
849                };
850                object_term = Some(term);
851                args.push(term);
852
853                let verb_str = self.interner.resolve(verb);
854                if Lexer::is_ditransitive_verb(verb_str)
855                    && (self.check_content_word() || self.check_article())
856                {
857                    let second_np = self.parse_noun_phrase(false)?;
858                    let second_term = Term::Constant(second_np.noun);
859                    second_object_term = Some(second_term);
860                    args.push(second_term);
861                }
862            } else if self.check_quantifier() || self.check_article() {
863                let obj_quantifier = if self.check_quantifier() {
864                    Some(self.advance().kind.clone())
865                } else {
866                    let art = self.advance().kind.clone();
867                    if let TokenType::Article(def) = art {
868                        if def == Definiteness::Indefinite {
869                            Some(TokenType::Some)
870                        } else {
871                            None
872                        }
873                    } else {
874                        None
875                    }
876                };
877
878                let object_np = self.parse_noun_phrase(false)?;
879
880                if let Some(obj_q) = obj_quantifier {
881                    let obj_var = self.next_var_name();
882
883                    // Introduce object referent in DRS for cross-sentence anaphora
884                    let obj_gender = Self::infer_noun_gender(self.interner.resolve(object_np.noun));
885                    let obj_number = if Self::is_plural_noun(self.interner.resolve(object_np.noun)) {
886                        Number::Plural
887                    } else {
888                        Number::Singular
889                    };
890                    // Definite descriptions presuppose existence, so they should be globally accessible
891                    if object_np.definiteness == Some(Definiteness::Definite) {
892                        self.drs.introduce_referent_with_source(obj_var, object_np.noun, obj_gender, obj_number, ReferentSource::MainClause);
893                    } else {
894                        self.drs.introduce_referent(obj_var, object_np.noun, obj_gender, obj_number);
895                    }
896
897                    let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
898                        name: object_np.noun,
899                        args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
900                        world: None,
901                    });
902
903                    let event_var = self.get_event_var();
904                    let mut modifiers = self.collect_adverbs();
905                    let effective_time = self.pending_time.take().unwrap_or(verb_time);
906                    match effective_time {
907                        Time::Past => modifiers.push(self.interner.intern("Past")),
908                        Time::Future => modifiers.push(self.interner.intern("Future")),
909                        _ => {}
910                    }
911
912                    let roles = vec![
913                        (ThematicRole::Agent, subject_term),
914                        (ThematicRole::Theme, Term::Variable(obj_var)),
915                    ];
916
917                    let suppress_existential = self.drs.in_conditional_antecedent();
918                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
919                        event_var,
920                        verb,
921                        roles: self.ctx.roles.alloc_slice(roles),
922                        modifiers: self.ctx.syms.alloc_slice(modifiers),
923                        suppress_existential,
924                        world: None,
925                    })));
926
927                    let obj_kind = match obj_q {
928                        TokenType::All => QuantifierKind::Universal,
929                        TokenType::Some => QuantifierKind::Existential,
930                        TokenType::No => QuantifierKind::Universal,
931                        TokenType::Most => QuantifierKind::Most,
932                        TokenType::Few => QuantifierKind::Few,
933                        TokenType::Many => QuantifierKind::Many,
934                        TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
935                        TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
936                        TokenType::AtMost(n) => QuantifierKind::AtMost(n),
937                        _ => QuantifierKind::Existential,
938                    };
939
940                    let obj_body = match obj_q {
941                        TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
942                            left: obj_restriction,
943                            op: TokenType::Implies,
944                            right: neo_event,
945                        }),
946                        TokenType::No => {
947                            let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
948                                op: TokenType::Not,
949                                operand: neo_event,
950                            });
951                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
952                                left: obj_restriction,
953                                op: TokenType::Implies,
954                                right: neg,
955                            })
956                        }
957                        _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
958                            left: obj_restriction,
959                            op: TokenType::And,
960                            right: neo_event,
961                        }),
962                    };
963
964                    return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
965                        kind: obj_kind,
966                        variable: obj_var,
967                        body: obj_body,
968                        island_id: self.current_island,
969                    }));
970                } else {
971                    // Definite object NP (e.g., "the house")
972                    // Introduce to DRS for cross-sentence bridging anaphora
973                    // E.g., "John entered the house. The door was open." - door bridges to house
974                    if object_np.definiteness == Some(Definiteness::Definite) {
975                        let obj_gender = Self::infer_noun_gender(self.interner.resolve(object_np.noun));
976                        let obj_number = if Self::is_plural_noun(self.interner.resolve(object_np.noun)) {
977                            Number::Plural
978                        } else {
979                            Number::Singular
980                        };
981                        // Definite descriptions presuppose existence, so they should be globally accessible
982                        self.drs.introduce_referent_with_source(object_np.noun, object_np.noun, obj_gender, obj_number, ReferentSource::MainClause);
983                    }
984
985                    let term = Term::Constant(object_np.noun);
986                    object_term = Some(term);
987                    // Store PPs attached to object NP for NP-attachment mode
988                    object_pps = object_np.pps;
989                    args.push(term);
990                }
991            } else if self.check_focus() {
992                let focus_kind = if let TokenType::Focus(k) = self.advance().kind {
993                    k
994                } else {
995                    FocusKind::Only
996                };
997
998                let event_var = self.get_event_var();
999                let mut modifiers = self.collect_adverbs();
1000                let effective_time = self.pending_time.take().unwrap_or(verb_time);
1001                match effective_time {
1002                    Time::Past => modifiers.push(self.interner.intern("Past")),
1003                    Time::Future => modifiers.push(self.interner.intern("Future")),
1004                    _ => {}
1005                }
1006
1007                if self.check_preposition() {
1008                    let prep_token = self.advance().clone();
1009                    let prep_name = if let TokenType::Preposition(sym) = prep_token.kind {
1010                        sym
1011                    } else {
1012                        self.interner.intern("to")
1013                    };
1014                    let pp_obj = self.parse_noun_phrase(false)?;
1015                    let pp_obj_term = Term::Constant(pp_obj.noun);
1016
1017                    let roles = vec![(ThematicRole::Agent, subject_term)];
1018                    let suppress_existential = self.drs.in_conditional_antecedent();
1019                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1020                        event_var,
1021                        verb,
1022                        roles: self.ctx.roles.alloc_slice(roles),
1023                        modifiers: self.ctx.syms.alloc_slice(modifiers),
1024                        suppress_existential,
1025                        world: None,
1026                    })));
1027
1028                    let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1029                        name: prep_name,
1030                        args: self.ctx.terms.alloc_slice([Term::Variable(event_var), pp_obj_term]),
1031                        world: None,
1032                    });
1033
1034                    let with_pp = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1035                        left: neo_event,
1036                        op: TokenType::And,
1037                        right: pp_pred,
1038                    });
1039
1040                    let focused_ref = self.ctx.terms.alloc(pp_obj_term);
1041                    return Ok(self.ctx.exprs.alloc(LogicExpr::Focus {
1042                        kind: focus_kind,
1043                        focused: focused_ref,
1044                        scope: with_pp,
1045                    }));
1046                }
1047
1048                let focused_np = self.parse_noun_phrase(false)?;
1049                let focused_term = Term::Constant(focused_np.noun);
1050                args.push(focused_term);
1051
1052                let roles = vec![
1053                    (ThematicRole::Agent, subject_term),
1054                    (ThematicRole::Theme, focused_term),
1055                ];
1056
1057                let suppress_existential = self.drs.in_conditional_antecedent();
1058                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1059                    event_var,
1060                    verb,
1061                    roles: self.ctx.roles.alloc_slice(roles),
1062                    modifiers: self.ctx.syms.alloc_slice(modifiers),
1063                    suppress_existential,
1064                    world: None,
1065                })));
1066
1067                let focused_ref = self.ctx.terms.alloc(focused_term);
1068                return Ok(self.ctx.exprs.alloc(LogicExpr::Focus {
1069                    kind: focus_kind,
1070                    focused: focused_ref,
1071                    scope: neo_event,
1072                }));
1073            } else if self.check_number() {
1074                let measure = self.parse_measure_phrase()?;
1075                if self.check_content_word() {
1076                    let noun_sym = self.consume_content_word()?;
1077                    args.push(*measure);
1078                    args.push(Term::Constant(noun_sym));
1079                } else {
1080                    args.push(*measure);
1081                }
1082            } else if self.check_content_word() {
1083                let potential_object = self.parse_noun_phrase(false)?;
1084                // Store PPs attached to object NP for NP-attachment mode
1085                object_pps = potential_object.pps;
1086
1087                if self.check_verb() && self.filler_gap.is_some() {
1088                    let embedded_subject = potential_object.noun;
1089                    let embedded_pred = self.parse_predicate_with_subject(embedded_subject)?;
1090
1091                    let embedded_term = Term::Proposition(embedded_pred);
1092                    let main_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1093                        name: verb,
1094                        args: self.ctx.terms.alloc_slice([subject_term, embedded_term]),
1095                        world: None,
1096                    });
1097
1098                    let effective_time = self.pending_time.take().unwrap_or(verb_time);
1099                    return Ok(if effective_time == Time::Past {
1100                        self.ctx.exprs.alloc(LogicExpr::Temporal {
1101                            operator: TemporalOperator::Past,
1102                            body: main_pred,
1103                        })
1104                    } else {
1105                        main_pred
1106                    });
1107                }
1108
1109                // Collect all objects for potential "respectively" handling
1110                let mut all_objects: Vec<Symbol> = vec![potential_object.noun];
1111
1112                // Check for coordinated objects: "Tom and Jerry and Bob"
1113                while self.check(&TokenType::And) {
1114                    let saved = self.current;
1115                    self.advance(); // consume "and"
1116                    if self.check_content_word() || self.check_article() {
1117                        let next_obj = match self.parse_noun_phrase(false) {
1118                            Ok(np) => np,
1119                            Err(_) => {
1120                                self.current = saved;
1121                                break;
1122                            }
1123                        };
1124                        all_objects.push(next_obj.noun);
1125                    } else {
1126                        self.current = saved;
1127                        break;
1128                    }
1129                }
1130
1131                // Check for "respectively" with single subject
1132                if self.check(&TokenType::Respectively) {
1133                    let respectively_span = self.peek().span;
1134                    // Single subject with multiple objects + respectively = error
1135                    if all_objects.len() > 1 {
1136                        return Err(ParseError {
1137                            kind: ParseErrorKind::RespectivelyLengthMismatch {
1138                                subject_count: 1,
1139                                object_count: all_objects.len(),
1140                            },
1141                            span: respectively_span,
1142                        });
1143                    }
1144                    // Single subject, single object + respectively is valid (trivially pairwise)
1145                    self.advance(); // consume "respectively"
1146                }
1147
1148                // Use the first object (or only object) for normal processing
1149                let term = Term::Constant(all_objects[0]);
1150                object_term = Some(term);
1151                args.push(term);
1152
1153                // For multiple objects without "respectively", use group semantics
1154                if all_objects.len() > 1 {
1155                    let obj_members: Vec<Term<'a>> = all_objects.iter()
1156                        .map(|o| Term::Constant(*o))
1157                        .collect();
1158                    let obj_group = Term::Group(self.ctx.terms.alloc_slice(obj_members));
1159                    // Replace the single object with the group
1160                    args.pop();
1161                    args.push(obj_group);
1162                }
1163
1164                let verb_str = self.interner.resolve(verb);
1165                if Lexer::is_ditransitive_verb(verb_str)
1166                    && (self.check_content_word() || self.check_article())
1167                {
1168                    let second_np = self.parse_noun_phrase(false)?;
1169                    let second_term = Term::Constant(second_np.noun);
1170                    second_object_term = Some(second_term);
1171                    args.push(second_term);
1172                }
1173            } else if self.filler_gap.is_some() && !self.check_content_word() && !self.check_pronoun()
1174            {
1175                let gap_var = self.filler_gap.take().unwrap();
1176                let term = Term::Variable(gap_var);
1177                object_term = Some(term);
1178                args.push(term);
1179            }
1180
1181            // Check for distanced phrasal verb particle: "gave the book up"
1182            if let TokenType::Particle(particle_sym) = self.peek().kind {
1183                let verb_str = self.interner.resolve(verb).to_lowercase();
1184                let particle_str = self.interner.resolve(particle_sym).to_lowercase();
1185                if let Some((phrasal_lemma, _class)) = crate::lexicon::lookup_phrasal_verb(&verb_str, &particle_str) {
1186                    self.advance(); // consume the particle
1187                    verb = self.interner.intern(phrasal_lemma);
1188                }
1189            }
1190
1191            let unknown = self.interner.intern("?");
1192            let mut pp_predicates: Vec<&'a LogicExpr<'a>> = Vec::new();
1193            while self.check_preposition() || self.check_to() {
1194                // "within N cycles" is a temporal bound, not a PP — leave for try_wrap_bounded_delay
1195                if self.check_preposition_is("within") && self.current + 1 < self.tokens.len()
1196                    && matches!(self.tokens[self.current + 1].kind, TokenType::Cardinal(_) | TokenType::Number(_))
1197                {
1198                    break;
1199                }
1200                let prep_token = self.advance().clone();
1201                let prep_name = if let TokenType::Preposition(sym) = prep_token.kind {
1202                    sym
1203                } else if matches!(prep_token.kind, TokenType::To) {
1204                    self.interner.intern("To")
1205                } else {
1206                    continue;
1207                };
1208
1209                let pp_obj_term = if self.check(&TokenType::Reflexive) {
1210                    self.advance();
1211                    Term::Constant(subject_symbol)
1212                } else if self.check_pronoun() {
1213                    let token = self.advance().clone();
1214                    let (gender, number) = match &token.kind {
1215                        TokenType::Pronoun { gender, number, .. } => (*gender, *number),
1216                        TokenType::Ambiguous { primary, alternatives } => {
1217                            if let TokenType::Pronoun { gender, number, .. } = **primary {
1218                                (gender, number)
1219                            } else {
1220                                alternatives.iter().find_map(|t| {
1221                                    if let TokenType::Pronoun { gender, number, .. } = t {
1222                                        Some((*gender, *number))
1223                                    } else {
1224                                        None
1225                                    }
1226                                }).unwrap_or((Gender::Unknown, Number::Singular))
1227                            }
1228                        }
1229                        _ => (Gender::Unknown, Number::Singular),
1230                    };
1231                    let resolved = self.resolve_pronoun(gender, number)?;
1232                    match resolved {
1233                        super::ResolvedPronoun::Variable(s) => Term::Variable(s),
1234                        super::ResolvedPronoun::Constant(s) => Term::Constant(s),
1235                    }
1236                } else if self.check_content_word() || self.check_article() {
1237                    let prep_obj = self.parse_noun_phrase(false)?;
1238                    Term::Constant(prep_obj.noun)
1239                } else {
1240                    continue;
1241                };
1242
1243                if self.pp_attach_to_noun {
1244                    if let Some(obj) = object_term {
1245                        let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1246                            name: prep_name,
1247                            args: self.ctx.terms.alloc_slice([obj, pp_obj_term]),
1248                            world: None,
1249                        });
1250                        pp_predicates.push(pp_pred);
1251                    } else {
1252                        args.push(pp_obj_term);
1253                    }
1254                } else {
1255                    let event_sym = self.get_event_var();
1256                    let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1257                        name: prep_name,
1258                        args: self
1259                            .ctx
1260                            .terms
1261                            .alloc_slice([Term::Variable(event_sym), pp_obj_term]),
1262                        world: None,
1263                    });
1264                    pp_predicates.push(pp_pred);
1265                }
1266            }
1267
1268            if self.check(&TokenType::That) || self.check(&TokenType::Who) {
1269                self.advance();
1270                let rel_var = self.next_var_name();
1271                let rel_pred = self.parse_relative_clause(rel_var)?;
1272                pp_predicates.push(rel_pred);
1273            }
1274
1275            let mut modifiers = self.collect_adverbs();
1276
1277            let effective_time = self.pending_time.take().unwrap_or(verb_time);
1278            match effective_time {
1279                Time::Past => modifiers.push(self.interner.intern("Past")),
1280                Time::Future => modifiers.push(self.interner.intern("Future")),
1281                _ => {}
1282            }
1283
1284            if verb_aspect == Aspect::Progressive {
1285                modifiers.push(self.interner.intern("Progressive"));
1286            } else if verb_aspect == Aspect::Perfect {
1287                modifiers.push(self.interner.intern("Perfect"));
1288            }
1289
1290            let mut roles: Vec<(ThematicRole, Term<'a>)> = Vec::new();
1291
1292            // Check if verb is unaccusative (intransitive subject is Theme, not Agent)
1293            let verb_str = self.interner.resolve(verb).to_lowercase();
1294            let is_unaccusative = crate::lexicon::lookup_verb_db(&verb_str)
1295                .map(|meta| meta.features.contains(&crate::lexicon::Feature::Unaccusative))
1296                .unwrap_or(false);
1297
1298            // Unaccusative verbs used intransitively: subject is Theme
1299            // E.g., "The alarm triggers" → Theme(e, Alarm), not Agent(e, Alarm)
1300            let has_object = object_term.is_some() || second_object_term.is_some();
1301            let subject_role = if is_unaccusative && !has_object {
1302                ThematicRole::Theme
1303            } else {
1304                ThematicRole::Agent
1305            };
1306
1307            roles.push((subject_role, subject_term));
1308            if let Some(second_obj) = second_object_term {
1309                if let Some(first_obj) = object_term {
1310                    roles.push((ThematicRole::Recipient, first_obj));
1311                }
1312                roles.push((ThematicRole::Theme, second_obj));
1313            } else if let Some(obj) = object_term {
1314                roles.push((ThematicRole::Theme, obj));
1315            }
1316
1317            let event_var = self.get_event_var();
1318            let suppress_existential = self.drs.in_conditional_antecedent();
1319            if suppress_existential {
1320                let event_class = self.interner.intern("Event");
1321                self.drs.introduce_referent(event_var, event_class, Gender::Neuter, Number::Singular);
1322            }
1323            let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1324                event_var,
1325                verb,
1326                roles: self.ctx.roles.alloc_slice(roles.clone()),
1327                modifiers: self.ctx.syms.alloc_slice(modifiers.clone()),
1328                suppress_existential,
1329                world: None,
1330            })));
1331
1332            // Capture template for ellipsis reconstruction
1333            self.capture_event_template(verb, &roles, &modifiers);
1334
1335            let with_pps = if pp_predicates.is_empty() {
1336                neo_event
1337            } else {
1338                let mut combined = neo_event;
1339                for pp in pp_predicates {
1340                    combined = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1341                        left: combined,
1342                        op: TokenType::And,
1343                        right: pp,
1344                    });
1345                }
1346                combined
1347            };
1348
1349            // Include PPs attached to object NP (for NP-attachment mode)
1350            // These have _PP_SELF_ placeholder that needs to be replaced with the object term
1351            let with_object_pps = if object_pps.is_empty() {
1352                with_pps
1353            } else if let Some(obj_term) = object_term {
1354                let placeholder = self.interner.intern("_PP_SELF_");
1355                let mut combined = with_pps;
1356                for pp in object_pps {
1357                    // Substitute _PP_SELF_ placeholder with the object term
1358                    let substituted = match pp {
1359                        LogicExpr::Predicate { name, args, .. } => {
1360                            let new_args: Vec<Term<'a>> = args
1361                                .iter()
1362                                .map(|arg| match arg {
1363                                    Term::Variable(v) if *v == placeholder => obj_term,
1364                                    other => *other,
1365                                })
1366                                .collect();
1367                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1368                                name: *name,
1369                                args: self.ctx.terms.alloc_slice(new_args),
1370                                world: None,
1371                            })
1372                        }
1373                        _ => *pp,
1374                    };
1375                    combined = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1376                        left: combined,
1377                        op: TokenType::And,
1378                        right: substituted,
1379                    });
1380                }
1381                combined
1382            } else {
1383                with_pps
1384            };
1385
1386            // Apply aspectual operators based on verb class
1387            let with_aspect = if verb_aspect == Aspect::Simple && effective_time == Time::Present {
1388                // Non-state verbs in simple present get Habitual reading
1389                if !verb_class.is_stative() {
1390                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
1391                        operator: AspectOperator::Habitual,
1392                        body: with_object_pps,
1393                    })
1394                } else {
1395                    with_object_pps
1396                }
1397            } else if verb_aspect == Aspect::Progressive {
1398                // Semelfactive + Progressive → Iterative
1399                if verb_class == crate::lexicon::VerbClass::Semelfactive {
1400                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
1401                        operator: AspectOperator::Iterative,
1402                        body: with_object_pps,
1403                    })
1404                } else {
1405                    with_object_pps
1406                }
1407            } else {
1408                with_object_pps
1409            };
1410
1411            Ok(with_aspect)
1412        } else {
1413            Ok(self.ctx.exprs.alloc(LogicExpr::Atom(subject_symbol)))
1414        }
1415    }
1416}
1417
1418impl<'a, 'ctx, 'int> LogicVerbParsing<'a, 'ctx, 'int> for Parser<'a, 'ctx, 'int> {
1419    fn parse_predicate_with_subject(&mut self, subject_symbol: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1420        let result = self.parse_predicate_impl(subject_symbol, false)?;
1421        Ok(self.try_wrap_bounded_delay(result))
1422    }
1423
1424    fn parse_predicate_with_subject_as_var(&mut self, subject_symbol: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1425        let result = self.parse_predicate_impl(subject_symbol, true)?;
1426        Ok(self.try_wrap_bounded_delay(result))
1427    }
1428
1429    fn try_parse_plural_subject(
1430        &mut self,
1431        first_subject: &NounPhrase<'a>,
1432    ) -> Result<Option<&'a LogicExpr<'a>>, ParseError> {
1433        let saved_pos = self.current;
1434
1435        // Consume the 'and' we already peeked
1436        self.advance();
1437
1438        if !self.check_content_word() {
1439            self.current = saved_pos;
1440            return Ok(None);
1441        }
1442
1443        // Collect all subjects: "John and Mary and Sue"
1444        let mut subjects: Vec<Symbol> = vec![first_subject.noun];
1445
1446        loop {
1447            if !self.check_content_word() {
1448                break;
1449            }
1450            let next_subject = match self.parse_noun_phrase(true) {
1451                Ok(np) => np,
1452                Err(_) => {
1453                    self.current = saved_pos;
1454                    return Ok(None);
1455                }
1456            };
1457            subjects.push(next_subject.noun);
1458
1459            if self.check(&TokenType::And) {
1460                self.advance();
1461            } else {
1462                break;
1463            }
1464        }
1465
1466        // Check for copula (is/are/was/were) with predicate nominative
1467        // "Both Socrates and Plato are men" -> M(s) ∧ M(p)
1468        if self.check(&TokenType::Is) || self.check(&TokenType::Are)
1469            || self.check(&TokenType::Was) || self.check(&TokenType::Were)
1470        {
1471            let copula_time = if self.check(&TokenType::Was) || self.check(&TokenType::Were) {
1472                Time::Past
1473            } else {
1474                Time::Present
1475            };
1476            self.advance(); // consume the copula
1477
1478            // Check for negation: "are not valid", "are not both valid"
1479            let is_negated = self.check(&TokenType::Not);
1480            if is_negated {
1481                self.advance(); // consume "not"
1482            }
1483
1484            // Check for "both" modifier: "are not both valid"
1485            // "both" scopes negation over the conjunction: ¬(P(A) ∧ P(B))
1486            // Without "both": negation distributes: ¬P(A) ∧ ¬P(B)
1487            let has_both = self.check(&TokenType::Both);
1488            if has_both {
1489                self.advance(); // consume "both"
1490            }
1491
1492            // Parse the predicate (e.g., "men" in "are men", "valid" in "are valid")
1493            if !self.check_content_word() && !self.check_article() {
1494                self.current = saved_pos;
1495                return Ok(None);
1496            }
1497
1498            let predicate_np = match self.parse_noun_phrase(false) {
1499                Ok(np) => np,
1500                Err(_) => {
1501                    self.current = saved_pos;
1502                    return Ok(None);
1503                }
1504            };
1505            let predicate = predicate_np.noun;
1506
1507            // Build distributed predicate: P(s1) ∧ P(s2) ∧ ...
1508            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1509            for subj in &subjects {
1510                let pred_expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1511                    name: predicate,
1512                    args: self.ctx.terms.alloc_slice([Term::Constant(*subj)]),
1513                    world: None,
1514                });
1515                conjuncts.push(pred_expr);
1516            }
1517
1518            if is_negated && !has_both {
1519                // "are not valid" → ¬P(s1) ∧ ¬P(s2) (negation distributes)
1520                for conjunct in &mut conjuncts {
1521                    *conjunct = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1522                        op: TokenType::Not,
1523                        operand: *conjunct,
1524                    });
1525                }
1526            }
1527
1528            // Fold conjuncts into binary conjunction tree
1529            let mut result = conjuncts[0];
1530            for conjunct in &conjuncts[1..] {
1531                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1532                    left: result,
1533                    op: TokenType::And,
1534                    right: *conjunct,
1535                });
1536            }
1537
1538            // "are not both valid" → ¬(P(s1) ∧ P(s2)) (negation over conjunction)
1539            if is_negated && has_both {
1540                result = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1541                    op: TokenType::Not,
1542                    operand: result,
1543                });
1544            }
1545
1546            // Apply temporal modifier for past tense
1547            let with_time = match copula_time {
1548                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1549                    operator: TemporalOperator::Past,
1550                    body: result,
1551                }),
1552                _ => result,
1553            };
1554
1555            return Ok(Some(with_time));
1556        }
1557
1558        if !self.check_verb() {
1559            self.current = saved_pos;
1560            return Ok(None);
1561        }
1562
1563        // Coordinated subjects registered in DRS via introduce_referent
1564
1565        let (verb, verb_time, _verb_aspect, _) = self.consume_verb_with_metadata();
1566
1567        // Check for reciprocal: "John and Mary kicked each other"
1568        if self.check(&TokenType::Reciprocal) {
1569            self.advance();
1570            if subjects.len() != 2 {
1571                self.current = saved_pos;
1572                return Ok(None);
1573            }
1574            let pred1 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1575                name: verb,
1576                args: self.ctx.terms.alloc_slice([
1577                    Term::Constant(subjects[0]),
1578                    Term::Constant(subjects[1]),
1579                ]),
1580                world: None,
1581            });
1582            let pred2 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1583                name: verb,
1584                args: self.ctx.terms.alloc_slice([
1585                    Term::Constant(subjects[1]),
1586                    Term::Constant(subjects[0]),
1587                ]),
1588                world: None,
1589            });
1590            let expr = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1591                left: pred1,
1592                op: TokenType::And,
1593                right: pred2,
1594            });
1595
1596            let with_time = match verb_time {
1597                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1598                    operator: TemporalOperator::Past,
1599                    body: expr,
1600                }),
1601                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1602                    operator: TemporalOperator::Future,
1603                    body: expr,
1604                }),
1605                _ => expr,
1606            };
1607            return Ok(Some(with_time));
1608        }
1609
1610        // Check for objects (for transitive verbs with "respectively")
1611        let mut objects: Vec<Symbol> = Vec::new();
1612        if self.check_content_word() || self.check_article() {
1613            // Parse first object
1614            let first_obj = match self.parse_noun_phrase(false) {
1615                Ok(np) => np,
1616                Err(_) => {
1617                    // No objects, continue with intransitive
1618                    return Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)));
1619                }
1620            };
1621            objects.push(first_obj.noun);
1622
1623            // Parse additional objects: "Tom and Jerry and Bob"
1624            while self.check(&TokenType::And) {
1625                self.advance();
1626                if self.check_content_word() || self.check_article() {
1627                    let next_obj = match self.parse_noun_phrase(false) {
1628                        Ok(np) => np,
1629                        Err(_) => break,
1630                    };
1631                    objects.push(next_obj.noun);
1632                } else {
1633                    break;
1634                }
1635            }
1636        }
1637
1638        // Check for "respectively" - triggers pairwise interpretation
1639        if self.check(&TokenType::Respectively) {
1640            let respectively_span = self.peek().span;
1641            self.advance(); // consume "respectively"
1642
1643            if subjects.len() != objects.len() {
1644                return Err(ParseError {
1645                    kind: ParseErrorKind::RespectivelyLengthMismatch {
1646                        subject_count: subjects.len(),
1647                        object_count: objects.len(),
1648                    },
1649                    span: respectively_span,
1650                });
1651            }
1652
1653            // Build pairwise predicates: See(J,T) ∧ See(M,J) ∧ ...
1654            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1655            let suppress_existential = self.drs.in_conditional_antecedent();
1656            for (subj, obj) in subjects.iter().zip(objects.iter()) {
1657                let event_var = self.get_event_var();
1658                let roles = vec![
1659                    (ThematicRole::Agent, Term::Constant(*subj)),
1660                    (ThematicRole::Theme, Term::Constant(*obj)),
1661                ];
1662                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1663                    event_var,
1664                    verb,
1665                    roles: self.ctx.roles.alloc_slice(roles),
1666                    modifiers: self.ctx.syms.alloc_slice(vec![]),
1667                    suppress_existential,
1668                    world: None,
1669                })));
1670                conjuncts.push(neo_event);
1671            }
1672
1673            // Fold conjuncts into binary conjunction tree
1674            let mut result = conjuncts[0];
1675            for conjunct in &conjuncts[1..] {
1676                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1677                    left: result,
1678                    op: TokenType::And,
1679                    right: *conjunct,
1680                });
1681            }
1682
1683            // Apply temporal modifier
1684            let with_time = match verb_time {
1685                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1686                    operator: TemporalOperator::Past,
1687                    body: result,
1688                }),
1689                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1690                    operator: TemporalOperator::Future,
1691                    body: result,
1692                }),
1693                _ => result,
1694            };
1695
1696            return Ok(Some(with_time));
1697        }
1698
1699        // No "respectively" - use group semantics
1700        if objects.is_empty() {
1701            // Intransitive: group subject
1702            Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)))
1703        } else {
1704            // Transitive without "respectively": group subject, group object
1705            Ok(Some(self.build_group_transitive(&subjects, &objects, verb, verb_time)))
1706        }
1707    }
1708
1709    /// Build a group predicate for intransitive verbs
1710    fn build_group_predicate(
1711        &mut self,
1712        subjects: &[Symbol],
1713        verb: Symbol,
1714        verb_time: Time,
1715    ) -> &'a LogicExpr<'a> {
1716        let group_members: Vec<Term<'a>> = subjects.iter()
1717            .map(|s| Term::Constant(*s))
1718            .collect();
1719        let group_members_slice = self.ctx.terms.alloc_slice(group_members);
1720
1721        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1722            name: verb,
1723            args: self.ctx.terms.alloc_slice([Term::Group(group_members_slice)]),
1724            world: None,
1725        });
1726
1727        match verb_time {
1728            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1729                operator: TemporalOperator::Past,
1730                body: expr,
1731            }),
1732            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1733                operator: TemporalOperator::Future,
1734                body: expr,
1735            }),
1736            _ => expr,
1737        }
1738    }
1739
1740    /// Build a transitive predicate with group subject and group object
1741    fn build_group_transitive(
1742        &mut self,
1743        subjects: &[Symbol],
1744        objects: &[Symbol],
1745        verb: Symbol,
1746        verb_time: Time,
1747    ) -> &'a LogicExpr<'a> {
1748        let subj_members: Vec<Term<'a>> = subjects.iter()
1749            .map(|s| Term::Constant(*s))
1750            .collect();
1751        let obj_members: Vec<Term<'a>> = objects.iter()
1752            .map(|o| Term::Constant(*o))
1753            .collect();
1754
1755        let subj_group = Term::Group(self.ctx.terms.alloc_slice(subj_members));
1756        let obj_group = Term::Group(self.ctx.terms.alloc_slice(obj_members));
1757
1758        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1759            name: verb,
1760            args: self.ctx.terms.alloc_slice([subj_group, obj_group]),
1761            world: None,
1762        });
1763
1764        match verb_time {
1765            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1766                operator: TemporalOperator::Past,
1767                body: expr,
1768            }),
1769            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1770                operator: TemporalOperator::Future,
1771                body: expr,
1772            }),
1773            _ => expr,
1774        }
1775    }
1776
1777    fn parse_control_structure(
1778        &mut self,
1779        subject: &NounPhrase<'a>,
1780        verb: Symbol,
1781        verb_time: Time,
1782    ) -> ParseResult<&'a LogicExpr<'a>> {
1783        let subject_sym = subject.noun;
1784        let verb_str = self.interner.resolve(verb);
1785
1786        if Lexer::is_raising_verb(verb_str) {
1787            if !self.check_to() {
1788                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1789                    name: verb,
1790                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1791                    world: None,
1792                }));
1793            }
1794            self.advance();
1795
1796            if !self.check_verb() {
1797                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1798                    name: verb,
1799                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1800                    world: None,
1801                }));
1802            }
1803
1804            let inf_verb = self.consume_verb();
1805
1806            let embedded = if self.is_control_verb(inf_verb) {
1807                let raised_np = NounPhrase {
1808                    noun: subject_sym,
1809                    definiteness: None,
1810                    adjectives: &[],
1811                    possessor: None,
1812                    pps: &[],
1813                    superlative: None,
1814                };
1815                self.parse_control_structure(&raised_np, inf_verb, Time::None)?
1816            } else {
1817                self.ctx.exprs.alloc(LogicExpr::Predicate {
1818                    name: inf_verb,
1819                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1820                    world: None,
1821                })
1822            };
1823
1824            let result = self.ctx.exprs.alloc(LogicExpr::Scopal {
1825                operator: verb,
1826                body: embedded,
1827            });
1828
1829            return Ok(match verb_time {
1830                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1831                    operator: TemporalOperator::Past,
1832                    body: result,
1833                }),
1834                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1835                    operator: TemporalOperator::Future,
1836                    body: result,
1837                }),
1838                _ => result,
1839            });
1840        }
1841
1842        let is_object_control = Lexer::is_object_control_verb(self.interner.resolve(verb));
1843        let (object_term, pro_controller_sym) = if self.check_to() {
1844            (None, subject_sym)
1845        } else if self.check_content_word() {
1846            let object_np = self.parse_noun_phrase(false)?;
1847            let obj_sym = object_np.noun;
1848
1849            let controller = if is_object_control {
1850                obj_sym
1851            } else {
1852                subject_sym
1853            };
1854            (
1855                Some(self.ctx.terms.alloc(Term::Constant(obj_sym))),
1856                controller,
1857            )
1858        } else {
1859            (None, subject_sym)
1860        };
1861
1862        if !self.check_to() {
1863            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1864                name: verb,
1865                args: match object_term {
1866                    Some(obj) => self.ctx.terms.alloc_slice([
1867                        Term::Constant(subject_sym),
1868                        Term::Constant(match obj {
1869                            Term::Constant(s) => *s,
1870                            _ => subject_sym,
1871                        }),
1872                    ]),
1873                    None => self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1874                },
1875                world: None,
1876            }));
1877        }
1878        self.advance();
1879
1880        if !self.check_verb() {
1881            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1882                name: verb,
1883                args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1884                world: None,
1885            }));
1886        }
1887
1888        let inf_verb = self.consume_verb();
1889        let inf_verb_str = self.interner.resolve(inf_verb).to_lowercase();
1890
1891        let infinitive = if inf_verb_str == "be" && self.check_verb() {
1892            let passive_verb = self.consume_verb();
1893            let passive_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1894                name: passive_verb,
1895                args: self
1896                    .ctx
1897                    .terms
1898                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1899                world: None,
1900            });
1901            self.ctx.voice(crate::ast::VoiceOperator::Passive, passive_pred)
1902        } else if self.is_control_verb(inf_verb) {
1903            let controller_np = NounPhrase {
1904                noun: pro_controller_sym,
1905                definiteness: None,
1906                adjectives: &[],
1907                possessor: None,
1908                pps: &[],
1909                superlative: None,
1910            };
1911            self.parse_control_structure(&controller_np, inf_verb, Time::None)?
1912        } else {
1913            self.ctx.exprs.alloc(LogicExpr::Predicate {
1914                name: inf_verb,
1915                args: self
1916                    .ctx
1917                    .terms
1918                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1919                world: None,
1920            })
1921        };
1922
1923        let control = self.ctx.exprs.alloc(LogicExpr::Control {
1924            verb,
1925            subject: self.ctx.terms.alloc(Term::Constant(subject_sym)),
1926            object: object_term,
1927            infinitive,
1928        });
1929
1930        Ok(match verb_time {
1931            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1932                operator: TemporalOperator::Past,
1933                body: control,
1934            }),
1935            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1936                operator: TemporalOperator::Future,
1937                body: control,
1938            }),
1939            _ => control,
1940        })
1941    }
1942
1943    fn is_control_verb(&self, verb: Symbol) -> bool {
1944        let lemma = self.interner.resolve(verb);
1945        Lexer::is_subject_control_verb(lemma)
1946            || Lexer::is_object_control_verb(lemma)
1947            || Lexer::is_raising_verb(lemma)
1948    }
1949}