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::If,
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::If,
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::If,
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::If,
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            // Parse the predicate nominative (e.g., "men" in "are men")
1397            if !self.check_content_word() && !self.check_article() {
1398                self.current = saved_pos;
1399                return Ok(None);
1400            }
1401
1402            let predicate_np = match self.parse_noun_phrase(false) {
1403                Ok(np) => np,
1404                Err(_) => {
1405                    self.current = saved_pos;
1406                    return Ok(None);
1407                }
1408            };
1409            let predicate = predicate_np.noun;
1410
1411            // Build distributed predicate: P(s1) ∧ P(s2) ∧ ...
1412            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1413            for subj in &subjects {
1414                let pred_expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1415                    name: predicate,
1416                    args: self.ctx.terms.alloc_slice([Term::Constant(*subj)]),
1417                    world: None,
1418                });
1419                conjuncts.push(pred_expr);
1420            }
1421
1422            // Fold conjuncts into binary conjunction tree
1423            let mut result = conjuncts[0];
1424            for conjunct in &conjuncts[1..] {
1425                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1426                    left: result,
1427                    op: TokenType::And,
1428                    right: *conjunct,
1429                });
1430            }
1431
1432            // Apply temporal modifier for past tense
1433            let with_time = match copula_time {
1434                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1435                    operator: TemporalOperator::Past,
1436                    body: result,
1437                }),
1438                _ => result,
1439            };
1440
1441            return Ok(Some(with_time));
1442        }
1443
1444        if !self.check_verb() {
1445            self.current = saved_pos;
1446            return Ok(None);
1447        }
1448
1449        // Coordinated subjects registered in DRS via introduce_referent
1450
1451        let (verb, verb_time, _verb_aspect, _) = self.consume_verb_with_metadata();
1452
1453        // Check for reciprocal: "John and Mary kicked each other"
1454        if self.check(&TokenType::Reciprocal) {
1455            self.advance();
1456            if subjects.len() != 2 {
1457                self.current = saved_pos;
1458                return Ok(None);
1459            }
1460            let pred1 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1461                name: verb,
1462                args: self.ctx.terms.alloc_slice([
1463                    Term::Constant(subjects[0]),
1464                    Term::Constant(subjects[1]),
1465                ]),
1466                world: None,
1467            });
1468            let pred2 = self.ctx.exprs.alloc(LogicExpr::Predicate {
1469                name: verb,
1470                args: self.ctx.terms.alloc_slice([
1471                    Term::Constant(subjects[1]),
1472                    Term::Constant(subjects[0]),
1473                ]),
1474                world: None,
1475            });
1476            let expr = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1477                left: pred1,
1478                op: TokenType::And,
1479                right: pred2,
1480            });
1481
1482            let with_time = match verb_time {
1483                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1484                    operator: TemporalOperator::Past,
1485                    body: expr,
1486                }),
1487                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1488                    operator: TemporalOperator::Future,
1489                    body: expr,
1490                }),
1491                _ => expr,
1492            };
1493            return Ok(Some(with_time));
1494        }
1495
1496        // Check for objects (for transitive verbs with "respectively")
1497        let mut objects: Vec<Symbol> = Vec::new();
1498        if self.check_content_word() || self.check_article() {
1499            // Parse first object
1500            let first_obj = match self.parse_noun_phrase(false) {
1501                Ok(np) => np,
1502                Err(_) => {
1503                    // No objects, continue with intransitive
1504                    return Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)));
1505                }
1506            };
1507            objects.push(first_obj.noun);
1508
1509            // Parse additional objects: "Tom and Jerry and Bob"
1510            while self.check(&TokenType::And) {
1511                self.advance();
1512                if self.check_content_word() || self.check_article() {
1513                    let next_obj = match self.parse_noun_phrase(false) {
1514                        Ok(np) => np,
1515                        Err(_) => break,
1516                    };
1517                    objects.push(next_obj.noun);
1518                } else {
1519                    break;
1520                }
1521            }
1522        }
1523
1524        // Check for "respectively" - triggers pairwise interpretation
1525        if self.check(&TokenType::Respectively) {
1526            let respectively_span = self.peek().span;
1527            self.advance(); // consume "respectively"
1528
1529            if subjects.len() != objects.len() {
1530                return Err(ParseError {
1531                    kind: ParseErrorKind::RespectivelyLengthMismatch {
1532                        subject_count: subjects.len(),
1533                        object_count: objects.len(),
1534                    },
1535                    span: respectively_span,
1536                });
1537            }
1538
1539            // Build pairwise predicates: See(J,T) ∧ See(M,J) ∧ ...
1540            let mut conjuncts: Vec<&'a LogicExpr<'a>> = Vec::new();
1541            let suppress_existential = self.drs.in_conditional_antecedent();
1542            for (subj, obj) in subjects.iter().zip(objects.iter()) {
1543                let event_var = self.get_event_var();
1544                let roles = vec![
1545                    (ThematicRole::Agent, Term::Constant(*subj)),
1546                    (ThematicRole::Theme, Term::Constant(*obj)),
1547                ];
1548                let neo_event = self.ctx.exprs.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
1549                    event_var,
1550                    verb,
1551                    roles: self.ctx.roles.alloc_slice(roles),
1552                    modifiers: self.ctx.syms.alloc_slice(vec![]),
1553                    suppress_existential,
1554                    world: None,
1555                })));
1556                conjuncts.push(neo_event);
1557            }
1558
1559            // Fold conjuncts into binary conjunction tree
1560            let mut result = conjuncts[0];
1561            for conjunct in &conjuncts[1..] {
1562                result = self.ctx.exprs.alloc(LogicExpr::BinaryOp {
1563                    left: result,
1564                    op: TokenType::And,
1565                    right: *conjunct,
1566                });
1567            }
1568
1569            // Apply temporal modifier
1570            let with_time = match verb_time {
1571                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1572                    operator: TemporalOperator::Past,
1573                    body: result,
1574                }),
1575                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1576                    operator: TemporalOperator::Future,
1577                    body: result,
1578                }),
1579                _ => result,
1580            };
1581
1582            return Ok(Some(with_time));
1583        }
1584
1585        // No "respectively" - use group semantics
1586        if objects.is_empty() {
1587            // Intransitive: group subject
1588            Ok(Some(self.build_group_predicate(&subjects, verb, verb_time)))
1589        } else {
1590            // Transitive without "respectively": group subject, group object
1591            Ok(Some(self.build_group_transitive(&subjects, &objects, verb, verb_time)))
1592        }
1593    }
1594
1595    /// Build a group predicate for intransitive verbs
1596    fn build_group_predicate(
1597        &mut self,
1598        subjects: &[Symbol],
1599        verb: Symbol,
1600        verb_time: Time,
1601    ) -> &'a LogicExpr<'a> {
1602        let group_members: Vec<Term<'a>> = subjects.iter()
1603            .map(|s| Term::Constant(*s))
1604            .collect();
1605        let group_members_slice = self.ctx.terms.alloc_slice(group_members);
1606
1607        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1608            name: verb,
1609            args: self.ctx.terms.alloc_slice([Term::Group(group_members_slice)]),
1610            world: None,
1611        });
1612
1613        match verb_time {
1614            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1615                operator: TemporalOperator::Past,
1616                body: expr,
1617            }),
1618            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1619                operator: TemporalOperator::Future,
1620                body: expr,
1621            }),
1622            _ => expr,
1623        }
1624    }
1625
1626    /// Build a transitive predicate with group subject and group object
1627    fn build_group_transitive(
1628        &mut self,
1629        subjects: &[Symbol],
1630        objects: &[Symbol],
1631        verb: Symbol,
1632        verb_time: Time,
1633    ) -> &'a LogicExpr<'a> {
1634        let subj_members: Vec<Term<'a>> = subjects.iter()
1635            .map(|s| Term::Constant(*s))
1636            .collect();
1637        let obj_members: Vec<Term<'a>> = objects.iter()
1638            .map(|o| Term::Constant(*o))
1639            .collect();
1640
1641        let subj_group = Term::Group(self.ctx.terms.alloc_slice(subj_members));
1642        let obj_group = Term::Group(self.ctx.terms.alloc_slice(obj_members));
1643
1644        let expr = self.ctx.exprs.alloc(LogicExpr::Predicate {
1645            name: verb,
1646            args: self.ctx.terms.alloc_slice([subj_group, obj_group]),
1647            world: None,
1648        });
1649
1650        match verb_time {
1651            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1652                operator: TemporalOperator::Past,
1653                body: expr,
1654            }),
1655            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1656                operator: TemporalOperator::Future,
1657                body: expr,
1658            }),
1659            _ => expr,
1660        }
1661    }
1662
1663    fn parse_control_structure(
1664        &mut self,
1665        subject: &NounPhrase<'a>,
1666        verb: Symbol,
1667        verb_time: Time,
1668    ) -> ParseResult<&'a LogicExpr<'a>> {
1669        let subject_sym = subject.noun;
1670        let verb_str = self.interner.resolve(verb);
1671
1672        if Lexer::is_raising_verb(verb_str) {
1673            if !self.check_to() {
1674                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1675                    name: verb,
1676                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1677                    world: None,
1678                }));
1679            }
1680            self.advance();
1681
1682            if !self.check_verb() {
1683                return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1684                    name: verb,
1685                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1686                    world: None,
1687                }));
1688            }
1689
1690            let inf_verb = self.consume_verb();
1691
1692            let embedded = if self.is_control_verb(inf_verb) {
1693                let raised_np = NounPhrase {
1694                    noun: subject_sym,
1695                    definiteness: None,
1696                    adjectives: &[],
1697                    possessor: None,
1698                    pps: &[],
1699                    superlative: None,
1700                };
1701                self.parse_control_structure(&raised_np, inf_verb, Time::None)?
1702            } else {
1703                self.ctx.exprs.alloc(LogicExpr::Predicate {
1704                    name: inf_verb,
1705                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1706                    world: None,
1707                })
1708            };
1709
1710            let result = self.ctx.exprs.alloc(LogicExpr::Scopal {
1711                operator: verb,
1712                body: embedded,
1713            });
1714
1715            return Ok(match verb_time {
1716                Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1717                    operator: TemporalOperator::Past,
1718                    body: result,
1719                }),
1720                Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1721                    operator: TemporalOperator::Future,
1722                    body: result,
1723                }),
1724                _ => result,
1725            });
1726        }
1727
1728        let is_object_control = Lexer::is_object_control_verb(self.interner.resolve(verb));
1729        let (object_term, pro_controller_sym) = if self.check_to() {
1730            (None, subject_sym)
1731        } else if self.check_content_word() {
1732            let object_np = self.parse_noun_phrase(false)?;
1733            let obj_sym = object_np.noun;
1734
1735            let controller = if is_object_control {
1736                obj_sym
1737            } else {
1738                subject_sym
1739            };
1740            (
1741                Some(self.ctx.terms.alloc(Term::Constant(obj_sym))),
1742                controller,
1743            )
1744        } else {
1745            (None, subject_sym)
1746        };
1747
1748        if !self.check_to() {
1749            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1750                name: verb,
1751                args: match object_term {
1752                    Some(obj) => self.ctx.terms.alloc_slice([
1753                        Term::Constant(subject_sym),
1754                        Term::Constant(match obj {
1755                            Term::Constant(s) => *s,
1756                            _ => subject_sym,
1757                        }),
1758                    ]),
1759                    None => self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1760                },
1761                world: None,
1762            }));
1763        }
1764        self.advance();
1765
1766        if !self.check_verb() {
1767            return Ok(self.ctx.exprs.alloc(LogicExpr::Predicate {
1768                name: verb,
1769                args: self.ctx.terms.alloc_slice([Term::Constant(subject_sym)]),
1770                world: None,
1771            }));
1772        }
1773
1774        let inf_verb = self.consume_verb();
1775        let inf_verb_str = self.interner.resolve(inf_verb).to_lowercase();
1776
1777        let infinitive = if inf_verb_str == "be" && self.check_verb() {
1778            let passive_verb = self.consume_verb();
1779            let passive_pred = self.ctx.exprs.alloc(LogicExpr::Predicate {
1780                name: passive_verb,
1781                args: self
1782                    .ctx
1783                    .terms
1784                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1785                world: None,
1786            });
1787            self.ctx.voice(crate::ast::VoiceOperator::Passive, passive_pred)
1788        } else if self.is_control_verb(inf_verb) {
1789            let controller_np = NounPhrase {
1790                noun: pro_controller_sym,
1791                definiteness: None,
1792                adjectives: &[],
1793                possessor: None,
1794                pps: &[],
1795                superlative: None,
1796            };
1797            self.parse_control_structure(&controller_np, inf_verb, Time::None)?
1798        } else {
1799            self.ctx.exprs.alloc(LogicExpr::Predicate {
1800                name: inf_verb,
1801                args: self
1802                    .ctx
1803                    .terms
1804                    .alloc_slice([Term::Constant(pro_controller_sym)]),
1805                world: None,
1806            })
1807        };
1808
1809        let control = self.ctx.exprs.alloc(LogicExpr::Control {
1810            verb,
1811            subject: self.ctx.terms.alloc(Term::Constant(subject_sym)),
1812            object: object_term,
1813            infinitive,
1814        });
1815
1816        Ok(match verb_time {
1817            Time::Past => self.ctx.exprs.alloc(LogicExpr::Temporal {
1818                operator: TemporalOperator::Past,
1819                body: control,
1820            }),
1821            Time::Future => self.ctx.exprs.alloc(LogicExpr::Temporal {
1822                operator: TemporalOperator::Future,
1823                body: control,
1824            }),
1825            _ => control,
1826        })
1827    }
1828
1829    fn is_control_verb(&self, verb: Symbol) -> bool {
1830        let lemma = self.interner.resolve(verb);
1831        Lexer::is_subject_control_verb(lemma)
1832            || Lexer::is_object_control_verb(lemma)
1833            || Lexer::is_raising_verb(lemma)
1834    }
1835}