Skip to main content

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        // "Who is a lawyer?" / "Who is the doctor?" / "Who is mortal?" — a COPULA
80        // wh-question. The complement (a nominal or an adjective) is predicated of
81        // the answer variable → Question{x, P(x)}; the wh-word IS the subject, so a
82        // bare predicate over `x` is exactly the goal whose witness is the answer.
83        if matches!(
84            self.peek().kind,
85            TokenType::Is | TokenType::Are | TokenType::Was | TokenType::Were
86        ) {
87            self.advance(); // copula
88            // "Who is in Maine?" / "What is on the table?" — a PP complement →
89            // Question{x, In(x, Maine)}: the answer is the entity standing in that
90            // relation. The locative question a logic grid asks ("who is in <state>",
91            // "who is in <year>").
92            if self.check_preposition() && !self.check_by_preposition() {
93                let prep_sym = match self.advance().kind {
94                    TokenType::Preposition(s) => s,
95                    _ => unreachable!("guarded by check_preposition"),
96                };
97                let obj = self.parse_noun_phrase(false)?;
98                let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
99                    name: prep_sym,
100                    args: self
101                        .ctx
102                        .terms
103                        .alloc_slice([var_term, Term::Constant(obj.noun)]),
104                    world: None,
105                });
106                return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
107                    wh_variable: var_name,
108                    body,
109                }));
110            }
111            if matches!(self.peek().kind, TokenType::Article(_)) {
112                self.advance(); // optional determiner ("a", "an", "the")
113            }
114            let pred = self.consume_content_word()?;
115            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
116                name: pred,
117                args: self.ctx.terms.alloc_slice([var_term]),
118                world: None,
119            });
120            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
121                wh_variable: var_name,
122                body,
123            }));
124        }
125
126        if self.check_verb() {
127            let verb = self.consume_verb();
128            let mut args = vec![var_term];
129
130            if self.check_content_word() {
131                let object = self.parse_noun_phrase(false)?;
132                args.push(Term::Constant(object.noun));
133            }
134
135            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
136                name: verb,
137                args: self.ctx.terms.alloc_slice(args),
138                world: None,
139            });
140            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
141                wh_variable: var_name,
142                body,
143            }));
144        }
145
146        if self.check(&TokenType::Does) || self.check(&TokenType::Do) {
147            self.advance();
148            let subject = self.parse_noun_phrase(true)?;
149            let verb = self.consume_verb();
150
151            let body = self.ctx.exprs.alloc(LogicExpr::Predicate {
152                name: verb,
153                args: self.ctx.terms.alloc_slice([Term::Constant(subject.noun), var_term]),
154                world: None,
155            });
156            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
157                wh_variable: var_name,
158                body,
159            }));
160        }
161
162        if self.check_auxiliary() {
163            let aux_token = self.advance().clone();
164            if let TokenType::Auxiliary(time) = aux_token.kind {
165                self.pending_time = Some(time);
166            }
167
168            self.filler_gap = Some(var_name);
169
170            let subject = self.parse_noun_phrase(true)?;
171            let body = self.parse_predicate_with_subject(subject.noun)?;
172
173            self.filler_gap = None;
174
175            return Ok(self.ctx.exprs.alloc(LogicExpr::Question {
176                wh_variable: var_name,
177                body,
178            }));
179        }
180
181        let unknown = self.interner.intern(&format!("{:?}", wh_token));
182        Ok(self.ctx.exprs.alloc(LogicExpr::Atom(unknown)))
183    }
184
185    fn parse_yes_no_question(&mut self) -> ParseResult<&'a LogicExpr<'a>> {
186        let aux_token = self.advance().kind.clone();
187
188        let is_modal = matches!(aux_token, TokenType::Can | TokenType::Could | TokenType::Would | TokenType::May | TokenType::Must | TokenType::Should);
189        let is_copula = matches!(aux_token, TokenType::Is | TokenType::Are | TokenType::Was | TokenType::Were);
190        let copula_time = if matches!(aux_token, TokenType::Was | TokenType::Were) {
191            Time::Past
192        } else {
193            Time::Present
194        };
195
196        if self.check_quantifier() {
197            self.advance();
198            let quantified = self.parse_quantified()?;
199            let wrapped = if is_modal {
200                let vector = self.aux_token_to_modal_vector(&aux_token);
201                self.ctx.exprs.alloc(LogicExpr::Modal {
202                    vector,
203                    operand: quantified,
204                })
205            } else {
206                quantified
207            };
208            return Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body: wrapped }));
209        }
210
211        let subject_symbol = if self.check_pronoun() {
212            let token = self.advance().clone();
213            if let TokenType::Pronoun { gender, number, .. } = token.kind {
214                let token_text = self.interner.resolve(token.lexeme);
215                if token_text.eq_ignore_ascii_case("you") {
216                    self.interner.intern("Addressee")
217                } else {
218                    let resolved = self.resolve_pronoun(gender, number)?;
219                    match resolved {
220                        super::ResolvedPronoun::Variable(s) | super::ResolvedPronoun::Constant(s) => s,
221                    }
222                }
223            } else {
224                self.interner.intern("?")
225            }
226        } else {
227            self.parse_noun_phrase(true)?.noun
228        };
229
230        let please_sym = self.interner.intern("please");
231        self.match_token(&[TokenType::Adverb(please_sym)]);
232
233        if is_copula {
234            let body = if self.check_verb() {
235                let (verb, _, verb_aspect, _) = self.consume_verb_with_metadata();
236                let predicate = self.ctx.exprs.alloc(LogicExpr::Predicate {
237                    name: verb,
238                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_symbol)]),
239                    world: None,
240                });
241                let with_aspect = if verb_aspect == Aspect::Progressive {
242                    self.ctx.exprs.alloc(LogicExpr::Aspectual {
243                        operator: AspectOperator::Progressive,
244                        body: predicate,
245                    })
246                } else {
247                    predicate
248                };
249                if copula_time == Time::Past {
250                    self.ctx.exprs.alloc(LogicExpr::Temporal {
251                        operator: TemporalOperator::Past,
252                        body: with_aspect,
253                    })
254                } else {
255                    with_aspect
256                }
257            } else if self.check_content_word() {
258                let adj = self.consume_content_word()?;
259                self.ctx.exprs.alloc(LogicExpr::Predicate {
260                    name: adj,
261                    args: self.ctx.terms.alloc_slice([Term::Constant(subject_symbol)]),
262                    world: None,
263                })
264            } else {
265                self.ctx.exprs.alloc(LogicExpr::Atom(subject_symbol))
266            };
267            return Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body }));
268        }
269
270        let body = self.parse_predicate_with_subject(subject_symbol)?;
271
272        let wrapped_body = if is_modal {
273            let vector = self.aux_token_to_modal_vector(&aux_token);
274            self.ctx.exprs.alloc(LogicExpr::Modal {
275                vector,
276                operand: body,
277            })
278        } else {
279            body
280        };
281
282        Ok(self.ctx.exprs.alloc(LogicExpr::YesNoQuestion { body: wrapped_body }))
283    }
284
285    fn aux_token_to_modal_vector(&self, token: &TokenType) -> ModalVector {
286        use crate::ast::ModalFlavor;
287        match token {
288            // Root modals (narrow scope)
289            TokenType::Can => ModalVector {
290                domain: ModalDomain::Alethic,
291                force: 0.5,
292                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
293            },
294            TokenType::Could => ModalVector {
295                domain: ModalDomain::Alethic,
296                force: 0.4,
297                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
298            },
299            TokenType::Would => ModalVector {
300                domain: ModalDomain::Alethic,
301                force: 0.6,
302                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
303            },
304            TokenType::Must => ModalVector {
305                domain: ModalDomain::Alethic,
306                force: 1.0,
307                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
308            },
309            TokenType::Should => ModalVector {
310                domain: ModalDomain::Deontic,
311                force: 0.6,
312                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
313            },
314            // Epistemic modals (wide scope)
315            TokenType::May => ModalVector {
316                domain: ModalDomain::Deontic,
317                force: 0.5,
318                flavor: ModalFlavor::Epistemic, modal_base: None, ordering_source: None
319            },
320            TokenType::Might => ModalVector {
321                domain: ModalDomain::Alethic,
322                force: 0.3,
323                flavor: ModalFlavor::Epistemic, modal_base: None, ordering_source: None
324            },
325            _ => ModalVector {
326                domain: ModalDomain::Alethic,
327                force: 0.5,
328                flavor: ModalFlavor::Root, modal_base: None, ordering_source: None
329            },
330        }
331    }
332}