Skip to main content

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            if self.check_verb() {
571                let (verb, _verb_time, verb_aspect, verb_class) = self.consume_verb_with_metadata();
572
573                // Stative verbs cannot be progressive
574                if verb_class.is_stative() && verb_aspect == Aspect::Progressive {
575                    return Err(crate::error::ParseError {
576                        kind: crate::error::ParseErrorKind::StativeProgressiveConflict,
577                        span: self.current_span(),
578                    });
579                }
580
581                let predicate = self.ctx.exprs.alloc(LogicExpr::Predicate {
582                    name: verb,
583                    args: self.ctx.terms.alloc_slice([subject_term]),
584                    world: None,
585                });
586
587                let with_aspect = if verb_aspect == Aspect::Progressive {
588                    // Semelfactive + Progressive → Iterative
589                    let operator = if verb_class == crate::lexicon::VerbClass::Semelfactive {
590                        AspectOperator::Iterative
591                    } else {
592                        AspectOperator::Progressive
593                    };
594                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
595                        operator,
596                        body: predicate,
597                    })
598                } else {
599                    predicate
600                };
601
602                let with_time = if copula_time == Time::Past {
603                    self.ctx.exprs.alloc(LogicExpr::Temporal {
604                        operator: TemporalOperator::Past,
605                        body: with_aspect,
606                    })
607                } else {
608                    with_aspect
609                };
610
611                return Ok(if is_negated {
612                    self.ctx.exprs.alloc(LogicExpr::UnaryOp {
613                        op: TokenType::Not,
614                        operand: with_time,
615                    })
616                } else {
617                    with_time
618                });
619            }
620
621            let predicate = self.consume_content_word()?;
622            let base_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
623                name: predicate,
624                args: self.ctx.terms.alloc_slice([subject_term]),
625                world: None,
626            });
627
628            let with_time = if copula_time == Time::Past {
629                self.ctx.exprs.alloc(LogicExpr::Temporal {
630                    operator: TemporalOperator::Past,
631                    body: base_pred,
632                })
633            } else {
634                base_pred
635            };
636
637            return Ok(if is_negated {
638                self.ctx.exprs.alloc(LogicExpr::UnaryOp {
639                    op: TokenType::Not,
640                    operand: with_time,
641                })
642            } else {
643                with_time
644            });
645        }
646
647        // Handle "did it" - when Auxiliary(Past) is used as a transitive verb (past of "do")
648        // This happens when we bypassed auxiliary handling because of lookahead
649        if self.check_auxiliary_as_main_verb() {
650            return self.parse_do_as_main_verb(subject_term);
651        }
652
653        if self.check_verb() {
654            let (mut verb, verb_time, verb_aspect, verb_class) = self.consume_verb_with_metadata();
655            let mut args = vec![subject_term.clone()];
656
657            // Check for embedded wh-clause: "I know who/what"
658            if self.check_wh_word() {
659                let wh_token = self.advance().kind.clone();
660
661                let is_who = matches!(wh_token, TokenType::Who);
662                let is_what = matches!(wh_token, TokenType::What);
663
664                // Check for sluicing: wh-word followed by terminator
665                let is_sluicing = self.is_at_end() ||
666                    self.check(&TokenType::Period) ||
667                    self.check(&TokenType::Comma);
668
669                if is_sluicing {
670                    if let Some(template) = self.last_event_template.clone() {
671                        let wh_var = self.next_var_name();
672
673                        let roles: Vec<_> = if is_who {
674                            std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
675                                .chain(template.non_agent_roles.iter().cloned())
676                                .collect()
677                        } else if is_what {
678                            vec![
679                                (ThematicRole::Agent, subject_term.clone()),
680                                (ThematicRole::Theme, Term::Variable(wh_var)),
681                            ]
682                        } else {
683                            std::iter::once((ThematicRole::Agent, Term::Variable(wh_var)))
684                                .chain(template.non_agent_roles.iter().cloned())
685                                .collect()
686                        };
687
688                        let event_var = self.get_event_var();
689                        let suppress_existential = self.drs.in_conditional_antecedent();
690                        let reconstructed = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
691                            event_var,
692                            verb: template.verb,
693                            roles: self.ctx.roles.alloc_slice(roles),
694                            modifiers: self.ctx.syms.alloc_slice(template.modifiers.clone()),
695                            suppress_existential,
696                            world: None,
697                        })));
698
699                        let question = self.ctx.exprs.alloc(LogicExpr::Question {
700                            wh_variable: wh_var,
701                            body: reconstructed,
702                        });
703
704                        let know_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
705                            event_var: self.get_event_var(),
706                            verb,
707                            roles: self.ctx.roles.alloc_slice(vec![
708                                (ThematicRole::Agent, subject_term),
709                                (ThematicRole::Theme, Term::Proposition(question)),
710                            ]),
711                            modifiers: self.ctx.syms.alloc_slice(vec![]),
712                            suppress_existential,
713                            world: None,
714                        })));
715
716                        return Ok(know_event);
717                    }
718                }
719
720                // Non-sluicing: "I know who runs"
721                let embedded = self.parse_embedded_wh_clause()?;
722                let question = self.ctx.exprs.alloc(LogicExpr::Question {
723                    wh_variable: self.interner.intern("x"),
724                    body: embedded,
725                });
726
727                let suppress_existential = self.drs.in_conditional_antecedent();
728                let know_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
729                    event_var: self.get_event_var(),
730                    verb,
731                    roles: self.ctx.roles.alloc_slice(vec![
732                        (ThematicRole::Agent, subject_term),
733                        (ThematicRole::Theme, Term::Proposition(question)),
734                    ]),
735                    modifiers: self.ctx.syms.alloc_slice(vec![]),
736                    suppress_existential,
737                    world: None,
738                })));
739
740                return Ok(know_event);
741            }
742
743            let mut object_term: Option<Term<'a>> = None;
744            let mut second_object_term: Option<Term<'a>> = None;
745            let mut object_pps: &[&LogicExpr<'a>] = &[];  // PPs attached to object NP (for NP-attachment mode)
746            if self.check(&TokenType::Reflexive) {
747                self.advance();
748                let term = Term::Constant(subject_symbol);
749                object_term = Some(term);
750                args.push(term);
751            } else if self.check_pronoun() {
752                let token = self.advance().clone();
753                let (gender, number) = match &token.kind {
754                    TokenType::Pronoun { gender, number, .. } => (*gender, *number),
755                    TokenType::Ambiguous { primary, alternatives } => {
756                        if let TokenType::Pronoun { gender, number, .. } = **primary {
757                            (gender, number)
758                        } else {
759                            alternatives.iter().find_map(|t| {
760                                if let TokenType::Pronoun { gender, number, .. } = t {
761                                    Some((*gender, *number))
762                                } else {
763                                    None
764                                }
765                            }).unwrap_or((Gender::Unknown, Number::Singular))
766                        }
767                    }
768                    _ => (Gender::Unknown, Number::Singular),
769                };
770
771                let resolved = self.resolve_pronoun(gender, number)?;
772                let term = match resolved {
773                    super::ResolvedPronoun::Variable(s) => Term::Variable(s),
774                    super::ResolvedPronoun::Constant(s) => Term::Constant(s),
775                };
776                object_term = Some(term);
777                args.push(term);
778
779                let verb_str = self.interner.resolve(verb);
780                if Lexer::is_ditransitive_verb(verb_str)
781                    && (self.check_content_word() || self.check_article())
782                {
783                    let second_np = self.parse_noun_phrase(false)?;
784                    let second_term = Term::Constant(second_np.noun);
785                    second_object_term = Some(second_term);
786                    args.push(second_term);
787                }
788            } else if self.check_quantifier() || self.check_article() {
789                let obj_quantifier = if self.check_quantifier() {
790                    Some(self.advance().kind.clone())
791                } else {
792                    let art = self.advance().kind.clone();
793                    if let TokenType::Article(def) = art {
794                        if def == Definiteness::Indefinite {
795                            Some(TokenType::Some)
796                        } else {
797                            None
798                        }
799                    } else {
800                        None
801                    }
802                };
803
804                let object_np = self.parse_noun_phrase(false)?;
805
806                if let Some(obj_q) = obj_quantifier {
807                    let obj_var = self.next_var_name();
808
809                    // Introduce object referent in DRS for cross-sentence anaphora
810                    let obj_gender = Self::infer_noun_gender(self.interner.resolve(object_np.noun));
811                    let obj_number = if Self::is_plural_noun(self.interner.resolve(object_np.noun)) {
812                        Number::Plural
813                    } else {
814                        Number::Singular
815                    };
816                    // Definite descriptions presuppose existence, so they should be globally accessible
817                    if object_np.definiteness == Some(Definiteness::Definite) {
818                        self.drs.introduce_referent_with_source(obj_var, object_np.noun, obj_gender, obj_number, ReferentSource::MainClause);
819                    } else {
820                        self.drs.introduce_referent(obj_var, object_np.noun, obj_gender, obj_number);
821                    }
822
823                    let obj_restriction = self.ctx.exprs.alloc(LogicExpr::Predicate {
824                        name: object_np.noun,
825                        args: self.ctx.terms.alloc_slice([Term::Variable(obj_var)]),
826                        world: None,
827                    });
828
829                    let event_var = self.get_event_var();
830                    let mut modifiers = self.collect_adverbs();
831                    let effective_time = self.pending_time.take().unwrap_or(verb_time);
832                    match effective_time {
833                        Time::Past => modifiers.push(self.interner.intern("Past")),
834                        Time::Future => modifiers.push(self.interner.intern("Future")),
835                        _ => {}
836                    }
837
838                    let roles = vec![
839                        (ThematicRole::Agent, subject_term),
840                        (ThematicRole::Theme, Term::Variable(obj_var)),
841                    ];
842
843                    let suppress_existential = self.drs.in_conditional_antecedent();
844                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
845                        event_var,
846                        verb,
847                        roles: self.ctx.roles.alloc_slice(roles),
848                        modifiers: self.ctx.syms.alloc_slice(modifiers),
849                        suppress_existential,
850                        world: None,
851                    })));
852
853                    let obj_kind = match obj_q {
854                        TokenType::All => QuantifierKind::Universal,
855                        TokenType::Some => QuantifierKind::Existential,
856                        TokenType::No => QuantifierKind::Universal,
857                        TokenType::Most => QuantifierKind::Most,
858                        TokenType::Few => QuantifierKind::Few,
859                        TokenType::Many => QuantifierKind::Many,
860                        TokenType::Cardinal(n) => QuantifierKind::Cardinal(n),
861                        TokenType::AtLeast(n) => QuantifierKind::AtLeast(n),
862                        TokenType::AtMost(n) => QuantifierKind::AtMost(n),
863                        _ => QuantifierKind::Existential,
864                    };
865
866                    let obj_body = match obj_q {
867                        TokenType::All => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
868                            left: obj_restriction,
869                            op: TokenType::Implies,
870                            right: neo_event,
871                        }),
872                        TokenType::No => {
873                            let neg = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
874                                op: TokenType::Not,
875                                operand: neo_event,
876                            });
877                            self.ctx.exprs.alloc(LogicExpr::BinaryOp {
878                                left: obj_restriction,
879                                op: TokenType::Implies,
880                                right: neg,
881                            })
882                        }
883                        _ => self.ctx.exprs.alloc(LogicExpr::BinaryOp {
884                            left: obj_restriction,
885                            op: TokenType::And,
886                            right: neo_event,
887                        }),
888                    };
889
890                    return Ok(self.ctx.exprs.alloc(LogicExpr::Quantifier {
891                        kind: obj_kind,
892                        variable: obj_var,
893                        body: obj_body,
894                        island_id: self.current_island,
895                    }));
896                } else {
897                    // Definite object NP (e.g., "the house")
898                    // Introduce to DRS for cross-sentence bridging anaphora
899                    // E.g., "John entered the house. The door was open." - door bridges to house
900                    if object_np.definiteness == Some(Definiteness::Definite) {
901                        let obj_gender = Self::infer_noun_gender(self.interner.resolve(object_np.noun));
902                        let obj_number = if Self::is_plural_noun(self.interner.resolve(object_np.noun)) {
903                            Number::Plural
904                        } else {
905                            Number::Singular
906                        };
907                        // Definite descriptions presuppose existence, so they should be globally accessible
908                        self.drs.introduce_referent_with_source(object_np.noun, object_np.noun, obj_gender, obj_number, ReferentSource::MainClause);
909                    }
910
911                    let term = Term::Constant(object_np.noun);
912                    object_term = Some(term);
913                    // Store PPs attached to object NP for NP-attachment mode
914                    object_pps = object_np.pps;
915                    args.push(term);
916                }
917            } else if self.check_focus() {
918                let focus_kind = if let TokenType::Focus(k) = self.advance().kind {
919                    k
920                } else {
921                    FocusKind::Only
922                };
923
924                let event_var = self.get_event_var();
925                let mut modifiers = self.collect_adverbs();
926                let effective_time = self.pending_time.take().unwrap_or(verb_time);
927                match effective_time {
928                    Time::Past => modifiers.push(self.interner.intern("Past")),
929                    Time::Future => modifiers.push(self.interner.intern("Future")),
930                    _ => {}
931                }
932
933                if self.check_preposition() {
934                    let prep_token = self.advance().clone();
935                    let prep_name = if let TokenType::Preposition(sym) = prep_token.kind {
936                        sym
937                    } else {
938                        self.interner.intern("to")
939                    };
940                    let pp_obj = self.parse_noun_phrase(false)?;
941                    let pp_obj_term = Term::Constant(pp_obj.noun);
942
943                    let roles = vec![(ThematicRole::Agent, subject_term)];
944                    let suppress_existential = self.drs.in_conditional_antecedent();
945                    let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
946                        event_var,
947                        verb,
948                        roles: self.ctx.roles.alloc_slice(roles),
949                        modifiers: self.ctx.syms.alloc_slice(modifiers),
950                        suppress_existential,
951                        world: None,
952                    })));
953
954                    let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
955                        name: prep_name,
956                        args: self.ctx.terms.alloc_slice([Term::Variable(event_var), pp_obj_term]),
957                        world: None,
958                    });
959
960                    let with_pp = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
961                        left: neo_event,
962                        op: TokenType::And,
963                        right: pp_pred,
964                    });
965
966                    let focused_ref = self.ctx.terms.alloc(pp_obj_term);
967                    return Ok(self.ctx.exprs.alloc(LogicExpr::Focus {
968                        kind: focus_kind,
969                        focused: focused_ref,
970                        scope: with_pp,
971                    }));
972                }
973
974                let focused_np = self.parse_noun_phrase(false)?;
975                let focused_term = Term::Constant(focused_np.noun);
976                args.push(focused_term);
977
978                let roles = vec![
979                    (ThematicRole::Agent, subject_term),
980                    (ThematicRole::Theme, focused_term),
981                ];
982
983                let suppress_existential = self.drs.in_conditional_antecedent();
984                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
985                    event_var,
986                    verb,
987                    roles: self.ctx.roles.alloc_slice(roles),
988                    modifiers: self.ctx.syms.alloc_slice(modifiers),
989                    suppress_existential,
990                    world: None,
991                })));
992
993                let focused_ref = self.ctx.terms.alloc(focused_term);
994                return Ok(self.ctx.exprs.alloc(LogicExpr::Focus {
995                    kind: focus_kind,
996                    focused: focused_ref,
997                    scope: neo_event,
998                }));
999            } else if self.check_number() {
1000                let measure = self.parse_measure_phrase()?;
1001                if self.check_content_word() {
1002                    let noun_sym = self.consume_content_word()?;
1003                    args.push(*measure);
1004                    args.push(Term::Constant(noun_sym));
1005                } else {
1006                    args.push(*measure);
1007                }
1008            } else if self.check_content_word() {
1009                let potential_object = self.parse_noun_phrase(false)?;
1010                // Store PPs attached to object NP for NP-attachment mode
1011                object_pps = potential_object.pps;
1012
1013                if self.check_verb() && self.filler_gap.is_some() {
1014                    let embedded_subject = potential_object.noun;
1015                    let embedded_pred = self.parse_predicate_with_subject(embedded_subject)?;
1016
1017                    let embedded_term = Term::Proposition(embedded_pred);
1018                    let main_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1019                        name: verb,
1020                        args: self.ctx.terms.alloc_slice([subject_term, embedded_term]),
1021                        world: None,
1022                    });
1023
1024                    let effective_time = self.pending_time.take().unwrap_or(verb_time);
1025                    return Ok(if effective_time == Time::Past {
1026                        self.ctx.exprs.alloc(LogicExpr::Temporal {
1027                            operator: TemporalOperator::Past,
1028                            body: main_pred,
1029                        })
1030                    } else {
1031                        main_pred
1032                    });
1033                }
1034
1035                // Collect all objects for potential "respectively" handling
1036                let mut all_objects: Vec<Symbol> = vec![potential_object.noun];
1037
1038                // Check for coordinated objects: "Tom and Jerry and Bob"
1039                while self.check(&TokenType::And) {
1040                    let saved = self.current;
1041                    self.advance(); // consume "and"
1042                    if self.check_content_word() || self.check_article() {
1043                        let next_obj = match self.parse_noun_phrase(false) {
1044                            Ok(np) => np,
1045                            Err(_) => {
1046                                self.current = saved;
1047                                break;
1048                            }
1049                        };
1050                        all_objects.push(next_obj.noun);
1051                    } else {
1052                        self.current = saved;
1053                        break;
1054                    }
1055                }
1056
1057                // Check for "respectively" with single subject
1058                if self.check(&TokenType::Respectively) {
1059                    let respectively_span = self.peek().span;
1060                    // Single subject with multiple objects + respectively = error
1061                    if all_objects.len() > 1 {
1062                        return Err(ParseError {
1063                            kind: ParseErrorKind::RespectivelyLengthMismatch {
1064                                subject_count: 1,
1065                                object_count: all_objects.len(),
1066                            },
1067                            span: respectively_span,
1068                        });
1069                    }
1070                    // Single subject, single object + respectively is valid (trivially pairwise)
1071                    self.advance(); // consume "respectively"
1072                }
1073
1074                // Use the first object (or only object) for normal processing
1075                let term = Term::Constant(all_objects[0]);
1076                object_term = Some(term);
1077                args.push(term);
1078
1079                // For multiple objects without "respectively", use group semantics
1080                if all_objects.len() > 1 {
1081                    let obj_members: Vec<Term<'a>> = all_objects.iter()
1082                        .map(|o| Term::Constant(*o))
1083                        .collect();
1084                    let obj_group = Term::Group(self.ctx.terms.alloc_slice(obj_members));
1085                    // Replace the single object with the group
1086                    args.pop();
1087                    args.push(obj_group);
1088                }
1089
1090                let verb_str = self.interner.resolve(verb);
1091                if Lexer::is_ditransitive_verb(verb_str)
1092                    && (self.check_content_word() || self.check_article())
1093                {
1094                    let second_np = self.parse_noun_phrase(false)?;
1095                    let second_term = Term::Constant(second_np.noun);
1096                    second_object_term = Some(second_term);
1097                    args.push(second_term);
1098                }
1099            } else if self.filler_gap.is_some() && !self.check_content_word() && !self.check_pronoun()
1100            {
1101                let gap_var = self.filler_gap.take().unwrap();
1102                let term = Term::Variable(gap_var);
1103                object_term = Some(term);
1104                args.push(term);
1105            }
1106
1107            // Check for distanced phrasal verb particle: "gave the book up"
1108            if let TokenType::Particle(particle_sym) = self.peek().kind {
1109                let verb_str = self.interner.resolve(verb).to_lowercase();
1110                let particle_str = self.interner.resolve(particle_sym).to_lowercase();
1111                if let Some((phrasal_lemma, _class)) = crate::lexicon::lookup_phrasal_verb(&verb_str, &particle_str) {
1112                    self.advance(); // consume the particle
1113                    verb = self.interner.intern(phrasal_lemma);
1114                }
1115            }
1116
1117            let unknown = self.interner.intern("?");
1118            let mut pp_predicates: Vec<&'a LogicExpr<'a>> = Vec::new();
1119            while self.check_preposition() || self.check_to() {
1120                let prep_token = self.advance().clone();
1121                let prep_name = if let TokenType::Preposition(sym) = prep_token.kind {
1122                    sym
1123                } else if matches!(prep_token.kind, TokenType::To) {
1124                    self.interner.intern("To")
1125                } else {
1126                    continue;
1127                };
1128
1129                let pp_obj_term = if self.check(&TokenType::Reflexive) {
1130                    self.advance();
1131                    Term::Constant(subject_symbol)
1132                } else if self.check_pronoun() {
1133                    let token = self.advance().clone();
1134                    let (gender, number) = match &token.kind {
1135                        TokenType::Pronoun { gender, number, .. } => (*gender, *number),
1136                        TokenType::Ambiguous { primary, alternatives } => {
1137                            if let TokenType::Pronoun { gender, number, .. } = **primary {
1138                                (gender, number)
1139                            } else {
1140                                alternatives.iter().find_map(|t| {
1141                                    if let TokenType::Pronoun { gender, number, .. } = t {
1142                                        Some((*gender, *number))
1143                                    } else {
1144                                        None
1145                                    }
1146                                }).unwrap_or((Gender::Unknown, Number::Singular))
1147                            }
1148                        }
1149                        _ => (Gender::Unknown, Number::Singular),
1150                    };
1151                    let resolved = self.resolve_pronoun(gender, number)?;
1152                    match resolved {
1153                        super::ResolvedPronoun::Variable(s) => Term::Variable(s),
1154                        super::ResolvedPronoun::Constant(s) => Term::Constant(s),
1155                    }
1156                } else if self.check_content_word() || self.check_article() {
1157                    let prep_obj = self.parse_noun_phrase(false)?;
1158                    Term::Constant(prep_obj.noun)
1159                } else {
1160                    continue;
1161                };
1162
1163                if self.pp_attach_to_noun {
1164                    if let Some(obj) = object_term {
1165                        let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1166                            name: prep_name,
1167                            args: self.ctx.terms.alloc_slice([obj, pp_obj_term]),
1168                            world: None,
1169                        });
1170                        pp_predicates.push(pp_pred);
1171                    } else {
1172                        args.push(pp_obj_term);
1173                    }
1174                } else {
1175                    let event_sym = self.get_event_var();
1176                    let pp_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1177                        name: prep_name,
1178                        args: self
1179                            .ctx
1180                            .terms
1181                            .alloc_slice([Term::Variable(event_sym), pp_obj_term]),
1182                        world: None,
1183                    });
1184                    pp_predicates.push(pp_pred);
1185                }
1186            }
1187
1188            if self.check(&TokenType::That) || self.check(&TokenType::Who) {
1189                self.advance();
1190                let rel_var = self.next_var_name();
1191                let rel_pred = self.parse_relative_clause(rel_var)?;
1192                pp_predicates.push(rel_pred);
1193            }
1194
1195            let mut modifiers = self.collect_adverbs();
1196
1197            let effective_time = self.pending_time.take().unwrap_or(verb_time);
1198            match effective_time {
1199                Time::Past => modifiers.push(self.interner.intern("Past")),
1200                Time::Future => modifiers.push(self.interner.intern("Future")),
1201                _ => {}
1202            }
1203
1204            if verb_aspect == Aspect::Progressive {
1205                modifiers.push(self.interner.intern("Progressive"));
1206            } else if verb_aspect == Aspect::Perfect {
1207                modifiers.push(self.interner.intern("Perfect"));
1208            }
1209
1210            let mut roles: Vec<(ThematicRole, Term<'a>)> = Vec::new();
1211
1212            // Check if verb is unaccusative (intransitive subject is Theme, not Agent)
1213            let verb_str = self.interner.resolve(verb).to_lowercase();
1214            let is_unaccusative = crate::lexicon::lookup_verb_db(&verb_str)
1215                .map(|meta| meta.features.contains(&crate::lexicon::Feature::Unaccusative))
1216                .unwrap_or(false);
1217
1218            // Unaccusative verbs used intransitively: subject is Theme
1219            // E.g., "The alarm triggers" → Theme(e, Alarm), not Agent(e, Alarm)
1220            let has_object = object_term.is_some() || second_object_term.is_some();
1221            let subject_role = if is_unaccusative && !has_object {
1222                ThematicRole::Theme
1223            } else {
1224                ThematicRole::Agent
1225            };
1226
1227            roles.push((subject_role, subject_term));
1228            if let Some(second_obj) = second_object_term {
1229                if let Some(first_obj) = object_term {
1230                    roles.push((ThematicRole::Recipient, first_obj));
1231                }
1232                roles.push((ThematicRole::Theme, second_obj));
1233            } else if let Some(obj) = object_term {
1234                roles.push((ThematicRole::Theme, obj));
1235            }
1236
1237            let event_var = self.get_event_var();
1238            let suppress_existential = self.drs.in_conditional_antecedent();
1239            if suppress_existential {
1240                let event_class = self.interner.intern("Event");
1241                self.drs.introduce_referent(event_var, event_class, Gender::Neuter, Number::Singular);
1242            }
1243            let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1244                event_var,
1245                verb,
1246                roles: self.ctx.roles.alloc_slice(roles.clone()),
1247                modifiers: self.ctx.syms.alloc_slice(modifiers.clone()),
1248                suppress_existential,
1249                world: None,
1250            })));
1251
1252            // Capture template for ellipsis reconstruction
1253            self.capture_event_template(verb, &roles, &modifiers);
1254
1255            let with_pps = if pp_predicates.is_empty() {
1256                neo_event
1257            } else {
1258                let mut combined = neo_event;
1259                for pp in pp_predicates {
1260                    combined = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1261                        left: combined,
1262                        op: TokenType::And,
1263                        right: pp,
1264                    });
1265                }
1266                combined
1267            };
1268
1269            // Include PPs attached to object NP (for NP-attachment mode)
1270            // These have _PP_SELF_ placeholder that needs to be replaced with the object term
1271            let with_object_pps = if object_pps.is_empty() {
1272                with_pps
1273            } else if let Some(obj_term) = object_term {
1274                let placeholder = self.interner.intern("_PP_SELF_");
1275                let mut combined = with_pps;
1276                for pp in object_pps {
1277                    // Substitute _PP_SELF_ placeholder with the object term
1278                    let substituted = match pp {
1279                        LogicExpr::Predicate { name, args, .. } => {
1280                            let new_args: Vec<Term<'a>> = args
1281                                .iter()
1282                                .map(|arg| match arg {
1283                                    Term::Variable(v) if *v == placeholder => obj_term,
1284                                    other => *other,
1285                                })
1286                                .collect();
1287                            self.ctx.exprs.alloc(LogicExpr::Predicate {
1288                                name: *name,
1289                                args: self.ctx.terms.alloc_slice(new_args),
1290                                world: None,
1291                            })
1292                        }
1293                        _ => *pp,
1294                    };
1295                    combined = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1296                        left: combined,
1297                        op: TokenType::And,
1298                        right: substituted,
1299                    });
1300                }
1301                combined
1302            } else {
1303                with_pps
1304            };
1305
1306            // Apply aspectual operators based on verb class
1307            let with_aspect = if verb_aspect == Aspect::Simple && effective_time == Time::Present {
1308                // Non-state verbs in simple present get Habitual reading
1309                if !verb_class.is_stative() {
1310                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
1311                        operator: AspectOperator::Habitual,
1312                        body: with_object_pps,
1313                    })
1314                } else {
1315                    with_object_pps
1316                }
1317            } else if verb_aspect == Aspect::Progressive {
1318                // Semelfactive + Progressive → Iterative
1319                if verb_class == crate::lexicon::VerbClass::Semelfactive {
1320                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
1321                        operator: AspectOperator::Iterative,
1322                        body: with_object_pps,
1323                    })
1324                } else {
1325                    with_object_pps
1326                }
1327            } else {
1328                with_object_pps
1329            };
1330
1331            Ok(with_aspect)
1332        } else {
1333            Ok(self.ctx.exprs.alloc(LogicExpr::Atom(subject_symbol)))
1334        }
1335    }
1336}
1337
1338impl<'a, 'ctx, 'int> LogicVerbParsing<'a, 'ctx, 'int> for Parser<'a, 'ctx, 'int> {
1339    fn parse_predicate_with_subject(&mut self, subject_symbol: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1340        self.parse_predicate_impl(subject_symbol, false)
1341    }
1342
1343    fn parse_predicate_with_subject_as_var(&mut self, subject_symbol: Symbol) -> ParseResult<&'a LogicExpr<'a>> {
1344        self.parse_predicate_impl(subject_symbol, true)
1345    }
1346
1347    fn try_parse_plural_subject(
1348        &mut self,
1349        first_subject: &NounPhrase<'a>,
1350    ) -> Result<Option<&'a LogicExpr<'a>>, ParseError> {
1351        let saved_pos = self.current;
1352
1353        // Consume the 'and' we already peeked
1354        self.advance();
1355
1356        if !self.check_content_word() {
1357            self.current = saved_pos;
1358            return Ok(None);
1359        }
1360
1361        // Collect all subjects: "John and Mary and Sue"
1362        let mut subjects: Vec<Symbol> = vec![first_subject.noun];
1363
1364        loop {
1365            if !self.check_content_word() {
1366                break;
1367            }
1368            let next_subject = match self.parse_noun_phrase(true) {
1369                Ok(np) => np,
1370                Err(_) => {
1371                    self.current = saved_pos;
1372                    return Ok(None);
1373                }
1374            };
1375            subjects.push(next_subject.noun);
1376
1377            if self.check(&TokenType::And) {
1378                self.advance();
1379            } else {
1380                break;
1381            }
1382        }
1383
1384        // Check for copula (is/are/was/were) with predicate nominative
1385        // "Both Socrates and Plato are men" -> M(s) ∧ M(p)
1386        if self.check(&TokenType::Is) || self.check(&TokenType::Are)
1387            || self.check(&TokenType::Was) || self.check(&TokenType::Were)
1388        {
1389            let copula_time = if self.check(&TokenType::Was) || self.check(&TokenType::Were) {
1390                Time::Past
1391            } else {
1392                Time::Present
1393            };
1394            self.advance(); // consume the copula
1395
1396            // Check for negation: "are not valid", "are not both valid"
1397            let is_negated = self.check(&TokenType::Not);
1398            if is_negated {
1399                self.advance(); // consume "not"
1400            }
1401
1402            // Check for "both" modifier: "are not both valid"
1403            // "both" scopes negation over the conjunction: ¬(P(A) ∧ P(B))
1404            // Without "both": negation distributes: ¬P(A) ∧ ¬P(B)
1405            let has_both = self.check(&TokenType::Both);
1406            if has_both {
1407                self.advance(); // consume "both"
1408            }
1409
1410            // Parse the predicate (e.g., "men" in "are men", "valid" in "are valid")
1411            if !self.check_content_word() && !self.check_article() {
1412                self.current = saved_pos;
1413                return Ok(None);
1414            }
1415
1416            let predicate_np = match self.parse_noun_phrase(false) {
1417                Ok(np) => np,
1418                Err(_) => {
1419                    self.current = saved_pos;
1420                    return Ok(None);
1421                }
1422            };
1423            let predicate = predicate_np.noun;
1424
1425            // Build distributed predicate: P(s1) ∧ P(s2) ∧ ...
1426            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1427            for subj in &subjects {
1428                let pred_expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1429                    name: predicate,
1430                    args: self.ctx.terms.alloc_slice([Term::Constant(*subj)]),
1431                    world: None,
1432                });
1433                conjuncts.push(pred_expr);
1434            }
1435
1436            if is_negated && !has_both {
1437                // "are not valid" → ¬P(s1) ∧ ¬P(s2) (negation distributes)
1438                for conjunct in &mut conjuncts {
1439                    *conjunct = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1440                        op: TokenType::Not,
1441                        operand: *conjunct,
1442                    });
1443                }
1444            }
1445
1446            // Fold conjuncts into binary conjunction tree
1447            let mut result = conjuncts[0];
1448            for conjunct in &conjuncts[1..] {
1449                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1450                    left: result,
1451                    op: TokenType::And,
1452                    right: *conjunct,
1453                });
1454            }
1455
1456            // "are not both valid" → ¬(P(s1) ∧ P(s2)) (negation over conjunction)
1457            if is_negated && has_both {
1458                result = self.ctx.exprs.alloc(LogicExpr::UnaryOp {
1459                    op: TokenType::Not,
1460                    operand: result,
1461                });
1462            }
1463
1464            // Apply temporal modifier for past tense
1465            let with_time = match copula_time {
1466                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1467                    operator: TemporalOperator::Past,
1468                    body: result,
1469                }),
1470                _ => result,
1471            };
1472
1473            return Ok(Some(with_time));
1474        }
1475
1476        if !self.check_verb() {
1477            self.current = saved_pos;
1478            return Ok(None);
1479        }
1480
1481        // Coordinated subjects registered in DRS via introduce_referent
1482
1483        let (verb, verb_time, _verb_aspect, _) = self.consume_verb_with_metadata();
1484
1485        // Check for reciprocal: "John and Mary kicked each other"
1486        if self.check(&TokenType::Reciprocal) {
1487            self.advance();
1488            if subjects.len() != 2 {
1489                self.current = saved_pos;
1490                return Ok(None);
1491            }
1492            let pred1 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1493                name: verb,
1494                args: self.ctx.terms.alloc_slice([
1495                    Term::Constant(subjects[0]),
1496                    Term::Constant(subjects[1]),
1497                ]),
1498                world: None,
1499            });
1500            let pred2 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1501                name: verb,
1502                args: self.ctx.terms.alloc_slice([
1503                    Term::Constant(subjects[1]),
1504                    Term::Constant(subjects[0]),
1505                ]),
1506                world: None,
1507            });
1508            let expr = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1509                left: pred1,
1510                op: TokenType::And,
1511                right: pred2,
1512            });
1513
1514            let with_time = match verb_time {
1515                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1516                    operator: TemporalOperator::Past,
1517                    body: expr,
1518                }),
1519                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1520                    operator: TemporalOperator::Future,
1521                    body: expr,
1522                }),
1523                _ => expr,
1524            };
1525            return Ok(Some(with_time));
1526        }
1527
1528        // Check for objects (for transitive verbs with "respectively")
1529        let mut objects: Vec<Symbol> = Vec::new();
1530        if self.check_content_word() || self.check_article() {
1531            // Parse first object
1532            let first_obj = match self.parse_noun_phrase(false) {
1533                Ok(np) => np,
1534                Err(_) => {
1535                    // No objects, continue with intransitive
1536                    return Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)));
1537                }
1538            };
1539            objects.push(first_obj.noun);
1540
1541            // Parse additional objects: "Tom and Jerry and Bob"
1542            while self.check(&TokenType::And) {
1543                self.advance();
1544                if self.check_content_word() || self.check_article() {
1545                    let next_obj = match self.parse_noun_phrase(false) {
1546                        Ok(np) => np,
1547                        Err(_) => break,
1548                    };
1549                    objects.push(next_obj.noun);
1550                } else {
1551                    break;
1552                }
1553            }
1554        }
1555
1556        // Check for "respectively" - triggers pairwise interpretation
1557        if self.check(&TokenType::Respectively) {
1558            let respectively_span = self.peek().span;
1559            self.advance(); // consume "respectively"
1560
1561            if subjects.len() != objects.len() {
1562                return Err(ParseError {
1563                    kind: ParseErrorKind::RespectivelyLengthMismatch {
1564                        subject_count: subjects.len(),
1565                        object_count: objects.len(),
1566                    },
1567                    span: respectively_span,
1568                });
1569            }
1570
1571            // Build pairwise predicates: See(J,T) ∧ See(M,J) ∧ ...
1572            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1573            let suppress_existential = self.drs.in_conditional_antecedent();
1574            for (subj, obj) in subjects.iter().zip(objects.iter()) {
1575                let event_var = self.get_event_var();
1576                let roles = vec![
1577                    (ThematicRole::Agent, Term::Constant(*subj)),
1578                    (ThematicRole::Theme, Term::Constant(*obj)),
1579                ];
1580                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1581                    event_var,
1582                    verb,
1583                    roles: self.ctx.roles.alloc_slice(roles),
1584                    modifiers: self.ctx.syms.alloc_slice(vec![]),
1585                    suppress_existential,
1586                    world: None,
1587                })));
1588                conjuncts.push(neo_event);
1589            }
1590
1591            // Fold conjuncts into binary conjunction tree
1592            let mut result = conjuncts[0];
1593            for conjunct in &conjuncts[1..] {
1594                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1595                    left: result,
1596                    op: TokenType::And,
1597                    right: *conjunct,
1598                });
1599            }
1600
1601            // Apply temporal modifier
1602            let with_time = match verb_time {
1603                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1604                    operator: TemporalOperator::Past,
1605                    body: result,
1606                }),
1607                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1608                    operator: TemporalOperator::Future,
1609                    body: result,
1610                }),
1611                _ => result,
1612            };
1613
1614            return Ok(Some(with_time));
1615        }
1616
1617        // No "respectively" - use group semantics
1618        if objects.is_empty() {
1619            // Intransitive: group subject
1620            Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)))
1621        } else {
1622            // Transitive without "respectively": group subject, group object
1623            Ok(Some(self.build_group_transitive(&subjects, &objects, verb, verb_time)))
1624        }
1625    }
1626
1627    /// Build a group predicate for intransitive verbs
1628    fn build_group_predicate(
1629        &mut self,
1630        subjects: &[Symbol],
1631        verb: Symbol,
1632        verb_time: Time,
1633    ) -> &'a LogicExpr<'a> {
1634        let group_members: Vec<Term<'a>> = subjects.iter()
1635            .map(|s| Term::Constant(*s))
1636            .collect();
1637        let group_members_slice = self.ctx.terms.alloc_slice(group_members);
1638
1639        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1640            name: verb,
1641            args: self.ctx.terms.alloc_slice([Term::Group(group_members_slice)]),
1642            world: None,
1643        });
1644
1645        match verb_time {
1646            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1647                operator: TemporalOperator::Past,
1648                body: expr,
1649            }),
1650            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1651                operator: TemporalOperator::Future,
1652                body: expr,
1653            }),
1654            _ => expr,
1655        }
1656    }
1657
1658    /// Build a transitive predicate with group subject and group object
1659    fn build_group_transitive(
1660        &mut self,
1661        subjects: &[Symbol],
1662        objects: &[Symbol],
1663        verb: Symbol,
1664        verb_time: Time,
1665    ) -> &'a LogicExpr<'a> {
1666        let subj_members: Vec<Term<'a>> = subjects.iter()
1667            .map(|s| Term::Constant(*s))
1668            .collect();
1669        let obj_members: Vec<Term<'a>> = objects.iter()
1670            .map(|o| Term::Constant(*o))
1671            .collect();
1672
1673        let subj_group = Term::Group(self.ctx.terms.alloc_slice(subj_members));
1674        let obj_group = Term::Group(self.ctx.terms.alloc_slice(obj_members));
1675
1676        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1677            name: verb,
1678            args: self.ctx.terms.alloc_slice([subj_group, obj_group]),
1679            world: None,
1680        });
1681
1682        match verb_time {
1683            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1684                operator: TemporalOperator::Past,
1685                body: expr,
1686            }),
1687            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1688                operator: TemporalOperator::Future,
1689                body: expr,
1690            }),
1691            _ => expr,
1692        }
1693    }
1694
1695    fn parse_control_structure(
1696        &mut self,
1697        subject: &NounPhrase<'a>,
1698        verb: Symbol,
1699        verb_time: Time,
1700    ) -> ParseResult<&'a LogicExpr<'a>> {
1701        let subject_sym = subject.noun;
1702        let verb_str = self.interner.resolve(verb);
1703
1704        if Lexer::is_raising_verb(verb_str) {
1705            if !self.check_to() {
1706                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1707                    name: verb,
1708                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1709                    world: None,
1710                }));
1711            }
1712            self.advance();
1713
1714            if !self.check_verb() {
1715                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1716                    name: verb,
1717                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1718                    world: None,
1719                }));
1720            }
1721
1722            let inf_verb = self.consume_verb();
1723
1724            let embedded = if self.is_control_verb(inf_verb) {
1725                let raised_np = NounPhrase {
1726                    noun: subject_sym,
1727                    definiteness: None,
1728                    adjectives: &[],
1729                    possessor: None,
1730                    pps: &[],
1731                    superlative: None,
1732                };
1733                self.parse_control_structure(&raised_np, inf_verb, Time::None)?
1734            } else {
1735                self.ctx.exprs.alloc(LogicExpr::Predicate {
1736                    name: inf_verb,
1737                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1738                    world: None,
1739                })
1740            };
1741
1742            let result = self.ctx.exprs.alloc(LogicExpr::Scopal {
1743                operator: verb,
1744                body: embedded,
1745            });
1746
1747            return Ok(match verb_time {
1748                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1749                    operator: TemporalOperator::Past,
1750                    body: result,
1751                }),
1752                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1753                    operator: TemporalOperator::Future,
1754                    body: result,
1755                }),
1756                _ => result,
1757            });
1758        }
1759
1760        let is_object_control = Lexer::is_object_control_verb(self.interner.resolve(verb));
1761        let (object_term, pro_controller_sym) = if self.check_to() {
1762            (None, subject_sym)
1763        } else if self.check_content_word() {
1764            let object_np = self.parse_noun_phrase(false)?;
1765            let obj_sym = object_np.noun;
1766
1767            let controller = if is_object_control {
1768                obj_sym
1769            } else {
1770                subject_sym
1771            };
1772            (
1773                Some(self.ctx.terms.alloc(Term::Constant(obj_sym))),
1774                controller,
1775            )
1776        } else {
1777            (None, subject_sym)
1778        };
1779
1780        if !self.check_to() {
1781            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1782                name: verb,
1783                args: match object_term {
1784                    Some(obj) => self.ctx.terms.alloc_slice([
1785                        Term::Constant(subject_sym),
1786                        Term::Constant(match obj {
1787                            Term::Constant(s) => *s,
1788                            _ => subject_sym,
1789                        }),
1790                    ]),
1791                    None => self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1792                },
1793                world: None,
1794            }));
1795        }
1796        self.advance();
1797
1798        if !self.check_verb() {
1799            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1800                name: verb,
1801                args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1802                world: None,
1803            }));
1804        }
1805
1806        let inf_verb = self.consume_verb();
1807        let inf_verb_str = self.interner.resolve(inf_verb).to_lowercase();
1808
1809        let infinitive = if inf_verb_str == "be" && self.check_verb() {
1810            let passive_verb = self.consume_verb();
1811            let passive_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1812                name: passive_verb,
1813                args: self
1814                    .ctx
1815                    .terms
1816                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1817                world: None,
1818            });
1819            self.ctx.voice(crate::ast::VoiceOperator::Passive, passive_pred)
1820        } else if self.is_control_verb(inf_verb) {
1821            let controller_np = NounPhrase {
1822                noun: pro_controller_sym,
1823                definiteness: None,
1824                adjectives: &[],
1825                possessor: None,
1826                pps: &[],
1827                superlative: None,
1828            };
1829            self.parse_control_structure(&controller_np, inf_verb, Time::None)?
1830        } else {
1831            self.ctx.exprs.alloc(LogicExpr::Predicate {
1832                name: inf_verb,
1833                args: self
1834                    .ctx
1835                    .terms
1836                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1837                world: None,
1838            })
1839        };
1840
1841        let control = self.ctx.exprs.alloc(LogicExpr::Control {
1842            verb,
1843            subject: self.ctx.terms.alloc(Term::Constant(subject_sym)),
1844            object: object_term,
1845            infinitive,
1846        });
1847
1848        Ok(match verb_time {
1849            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1850                operator: TemporalOperator::Past,
1851                body: control,
1852            }),
1853            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1854                operator: TemporalOperator::Future,
1855                body: control,
1856            }),
1857            _ => control,
1858        })
1859    }
1860
1861    fn is_control_verb(&self, verb: Symbol) -> bool {
1862        let lemma = self.interner.resolve(verb);
1863        Lexer::is_subject_control_verb(lemma)
1864            || Lexer::is_object_control_verb(lemma)
1865            || Lexer::is_raising_verb(lemma)
1866    }
1867}