logicaffeine_language/parser/
question.rs

1//! Question parsing: wh-questions and yes/no questions.
2//!
3//! This module handles interrogative sentences including:
4//!
5//! - **Wh-questions**: "Who runs?", "What does John love?", "Where did Mary go?"
6//! - **Yes/no questions**: "Does John run?", "Is Mary tall?"
7//! - **Pied-piping**: "To whom did John give the book?"
8//!
9//! # Wh-Movement
10//!
11//! Wh-words introduce a question variable that is moved to sentence-initial
12//! position. The parser tracks the "gap" (extraction site) using the filler-gap
13//! dependency mechanism.
14//!
15//! Questions are represented using `LogicExpr::Question` with a `wh_variable`
16//! binding the questioned entity.
17
18use super::noun::NounParsing;
19use super::quantifier::QuantifierParsing;
20use super::verb::LogicVerbParsing;
21use super::{ParseResult, Parser};
22use crate::ast::{AspectOperator, LogicExpr, ModalDomain, ModalVector, TemporalOperator, Term};
23use crate::lexicon::{Aspect, Time};
24use crate::token::TokenType;
25
26/// Trait for parsing interrogative sentences.
27///
28/// Provides methods for parsing wh-questions (who, what, where) and
29/// yes/no questions with subject-auxiliary inversion.
30pub trait QuestionParsing<'a, 'ctx, 'int> {
31    /// Parses a wh-question: "Who runs?", "What does John love?".
32    fn parse_wh_question(&mut self) -> ParseResult<&'a LogicExpr<'a>>;
33    /// Parses a yes/no question: "Does John run?", "Is Mary tall?".
34    fn parse_yes_no_question(&mut self) -> ParseResult<&'a LogicExpr<'a>>;
35    /// Converts an auxiliary token to its modal vector for questions.
36    fn aux_token_to_modal_vector(&self, token: &TokenType) -> ModalVector;
37}
38
39impl<'a, 'ctx, 'int> QuestionParsing<'a, 'ctx, 'int> for Parser<'a, 'ctx, 'int> {
40    fn parse_wh_question(&mut self) -> ParseResult<&'a LogicExpr<'a>> {
41        let pied_piping_prep = if self.check_preposition() {
42            let prep = self.advance().kind.clone();
43            Some(prep)
44        } else {
45            None
46        };
47
48        let wh_token = self.advance().kind.clone();
49        let var_name = self.interner.intern("x");
50        let var_term = Term::Variable(var_name);
51
52        if pied_piping_prep.is_some() && self.check_auxiliary() {
53            let aux_token = self.advance().clone();
54            if let TokenType::Auxiliary(time) = aux_token.kind {
55                self.pending_time = Some(time);
56            }
57
58            let subject = self.parse_noun_phrase(true)?;
59            let verb = self.consume_verb();
60
61            let mut args = vec![Term::Constant(subject.noun)];
62            if self.check_content_word() || self.check_article() {
63                let object = self.parse_noun_phrase(false)?;
64                args.push(Term::Constant(object.noun));
65            }
66            args.push(var_term);
67
68            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
69                name: verb,
70                args: self.ctx.terms.alloc_slice(args),
71                world: None,
72            });
73            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
74                wh_variable: var_name,
75                body,
76            }));
77        }
78
79        if self.check_verb() {
80            let verb = self.consume_verb();
81            let mut args = vec![var_term];
82
83            if self.check_content_word() {
84                let object = self.parse_noun_phrase(false)?;
85                args.push(Term::Constant(object.noun));
86            }
87
88            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
89                name: verb,
90                args: self.ctx.terms.alloc_slice(args),
91                world: None,
92            });
93            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
94                wh_variable: var_name,
95                body,
96            }));
97        }
98
99        if self.check(&TokenType::Does) || self.check(&TokenType::Do) {
100            self.advance();
101            let subject = self.parse_noun_phrase(true)?;
102            let verb = self.consume_verb();
103
104            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
105                name: verb,
106                args: self.ctx.terms.alloc_slice([Term::Constant(subject.noun), var_term]),
107                world: None,
108            });
109            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
110                wh_variable: var_name,
111                body,
112            }));
113        }
114
115        if self.check_auxiliary() {
116            let aux_token = self.advance().clone();
117            if let TokenType::Auxiliary(time) = aux_token.kind {
118                self.pending_time = Some(time);
119            }
120
121            self.filler_gap = Some(var_name);
122
123            let subject = self.parse_noun_phrase(true)?;
124            let body = self.parse_predicate_with_subject(subject.noun)?;
125
126            self.filler_gap = None;
127
128            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
129                wh_variable: var_name,
130                body,
131            }));
132        }
133
134        let unknown = self.interner.intern(&format!("{:?}", wh_token));
135        Ok(self.ctx.exprs.alloc(LogicExpr::Atom(unknown)))
136    }
137
138    fn parse_yes_no_question(&mut self) -> ParseResult<&'a LogicExpr<'a>> {
139        let aux_token = self.advance().kind.clone();
140
141        let is_modal = matches!(aux_token, TokenType::Can | TokenType::Could | TokenType::Would | TokenType::May | TokenType::Must | TokenType::Should);
142        let is_copula = matches!(aux_token, TokenType::Is | TokenType::Are | TokenType::Was | TokenType::Were);
143        let copula_time = if matches!(aux_token, TokenType::Was | TokenType::Were) {
144            Time::Past
145        } else {
146            Time::Present
147        };
148
149        if self.check_quantifier() {
150            self.advance();
151            let quantified = self.parse_quantified()?;
152            let wrapped = if is_modal {
153                let vector = self.aux_token_to_modal_vector(&aux_token);
154                self.ctx.exprs.alloc(LogicExpr::Modal {
155                    vector,
156                    operand: quantified,
157                })
158            } else {
159                quantified
160            };
161            return Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body: wrapped }));
162        }
163
164        let subject_symbol = if self.check_pronoun() {
165            let token = self.advance().clone();
166            if let TokenType::Pronoun { gender, number, .. } = token.kind {
167                let token_text = self.interner.resolve(token.lexeme);
168                if token_text.eq_ignore_ascii_case("you") {
169                    self.interner.intern("Addressee")
170                } else {
171                    let resolved = self.resolve_pronoun(gender, number)?;
172                    match resolved {
173                        super::ResolvedPronoun::Variable(s) | super::ResolvedPronoun::Constant(s) => s,
174                    }
175                }
176            } else {
177                self.interner.intern("?")
178            }
179        } else {
180            self.parse_noun_phrase(true)?.noun
181        };
182
183        let please_sym = self.interner.intern("please");
184        self.match_token(&[TokenType::Adverb(please_sym)]);
185
186        if is_copula {
187            let body = if self.check_verb() {
188                let (verb, _, verb_aspect, _) = self.consume_verb_with_metadata();
189                let predicate = self.ctx.exprs.alloc(LogicExpr::Predicate {
190                    name: verb,
191                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_symbol)]),
192                    world: None,
193                });
194                let with_aspect = if verb_aspect == Aspect::Progressive {
195                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
196                        operator: AspectOperator::Progressive,
197                        body: predicate,
198                    })
199                } else {
200                    predicate
201                };
202                if copula_time == Time::Past {
203                    self.ctx.exprs.alloc(LogicExpr::Temporal {
204                        operator: TemporalOperator::Past,
205                        body: with_aspect,
206                    })
207                } else {
208                    with_aspect
209                }
210            } else if self.check_content_word() {
211                let adj = self.consume_content_word()?;
212                self.ctx.exprs.alloc(LogicExpr::Predicate {
213                    name: adj,
214                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_symbol)]),
215                    world: None,
216                })
217            } else {
218                self.ctx.exprs.alloc(LogicExpr::Atom(subject_symbol))
219            };
220            return Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body }));
221        }
222
223        let body = self.parse_predicate_with_subject(subject_symbol)?;
224
225        let wrapped_body = if is_modal {
226            let vector = self.aux_token_to_modal_vector(&aux_token);
227            self.ctx.exprs.alloc(LogicExpr::Modal {
228                vector,
229                operand: body,
230            })
231        } else {
232            body
233        };
234
235        Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body: wrapped_body }))
236    }
237
238    fn aux_token_to_modal_vector(&self, token: &TokenType) -> ModalVector {
239        use crate::ast::ModalFlavor;
240        match token {
241            // Root modals (narrow scope)
242            TokenType::Can => ModalVector {
243                domain: ModalDomain::Alethic,
244                force: 0.5,
245                flavor: ModalFlavor::Root,
246            },
247            TokenType::Could => ModalVector {
248                domain: ModalDomain::Alethic,
249                force: 0.4,
250                flavor: ModalFlavor::Root,
251            },
252            TokenType::Would => ModalVector {
253                domain: ModalDomain::Alethic,
254                force: 0.6,
255                flavor: ModalFlavor::Root,
256            },
257            TokenType::Must => ModalVector {
258                domain: ModalDomain::Alethic,
259                force: 1.0,
260                flavor: ModalFlavor::Root,
261            },
262            TokenType::Should => ModalVector {
263                domain: ModalDomain::Deontic,
264                force: 0.6,
265                flavor: ModalFlavor::Root,
266            },
267            // Epistemic modals (wide scope)
268            TokenType::May => ModalVector {
269                domain: ModalDomain::Deontic,
270                force: 0.5,
271                flavor: ModalFlavor::Epistemic,
272            },
273            TokenType::Might => ModalVector {
274                domain: ModalDomain::Alethic,
275                force: 0.3,
276                flavor: ModalFlavor::Epistemic,
277            },
278            _ => ModalVector {
279                domain: ModalDomain::Alethic,
280                force: 0.5,
281                flavor: ModalFlavor::Root,
282            },
283        }
284    }
285}