logicaffeine_language/
visitor.rs1use crate::ast::{LogicExpr, NounPhrase, Term};
30
31pub trait Visitor<'a>: Sized {
33 fn visit_expr(&mut self, expr: &'a LogicExpr<'a>) {
34 walk_expr(self, expr);
35 }
36
37 fn visit_term(&mut self, term: &'a Term<'a>) {
38 walk_term(self, term);
39 }
40
41 fn visit_np(&mut self, np: &'a NounPhrase<'a>) {
42 walk_np(self, np);
43 }
44}
45
46pub fn walk_expr<'a, V: Visitor<'a>>(v: &mut V, expr: &'a LogicExpr<'a>) {
47 match expr {
48 LogicExpr::Predicate { args, .. } => {
49 for arg in *args {
50 v.visit_term(arg);
51 }
52 }
53
54 LogicExpr::Identity { left, right } => {
55 v.visit_term(left);
56 v.visit_term(right);
57 }
58
59 LogicExpr::Metaphor { tenor, vehicle } => {
60 v.visit_term(tenor);
61 v.visit_term(vehicle);
62 }
63
64 LogicExpr::Quantifier { body, .. } => {
65 v.visit_expr(body);
66 }
67
68 LogicExpr::Categorical(data) => {
69 v.visit_np(&data.subject);
70 v.visit_np(&data.predicate);
71 }
72
73 LogicExpr::Relation(data) => {
74 v.visit_np(&data.subject);
75 v.visit_np(&data.object);
76 }
77
78 LogicExpr::Modal { operand, .. } => {
79 v.visit_expr(operand);
80 }
81
82 LogicExpr::Temporal { body, .. } => {
83 v.visit_expr(body);
84 }
85
86 LogicExpr::TemporalBinary { left, right, .. } => {
87 v.visit_expr(left);
88 v.visit_expr(right);
89 }
90
91 LogicExpr::Aspectual { body, .. } => {
92 v.visit_expr(body);
93 }
94
95 LogicExpr::Voice { body, .. } => {
96 v.visit_expr(body);
97 }
98
99 LogicExpr::BinaryOp { left, right, .. } => {
100 v.visit_expr(left);
101 v.visit_expr(right);
102 }
103
104 LogicExpr::UnaryOp { operand, .. } => {
105 v.visit_expr(operand);
106 }
107
108 LogicExpr::Question { body, .. } => {
109 v.visit_expr(body);
110 }
111
112 LogicExpr::YesNoQuestion { body } => {
113 v.visit_expr(body);
114 }
115
116 LogicExpr::Atom(_) => {}
117
118 LogicExpr::Lambda { body, .. } => {
119 v.visit_expr(body);
120 }
121
122 LogicExpr::App { function, argument } => {
123 v.visit_expr(function);
124 v.visit_expr(argument);
125 }
126
127 LogicExpr::Intensional { content, .. } => {
128 v.visit_expr(content);
129 }
130
131 LogicExpr::Event { predicate, .. } => {
132 v.visit_expr(predicate);
133 }
134
135 LogicExpr::NeoEvent(data) => {
136 for (_, term) in data.roles.iter() {
137 v.visit_term(term);
138 }
139 }
140
141 LogicExpr::Imperative { action } => {
142 v.visit_expr(action);
143 }
144
145 LogicExpr::SpeechAct { content, .. } => {
146 v.visit_expr(content);
147 }
148
149 LogicExpr::Counterfactual { antecedent, consequent } => {
150 v.visit_expr(antecedent);
151 v.visit_expr(consequent);
152 }
153
154 LogicExpr::Causal { effect, cause } => {
155 v.visit_expr(cause);
156 v.visit_expr(effect);
157 }
158
159 LogicExpr::Comparative { subject, object, .. } => {
160 v.visit_term(subject);
161 v.visit_term(object);
162 }
163
164 LogicExpr::Superlative { subject, .. } => {
165 v.visit_term(subject);
166 }
167
168 LogicExpr::Scopal { body, .. } => {
169 v.visit_expr(body);
170 }
171
172 LogicExpr::Control { subject, object, infinitive, .. } => {
173 v.visit_term(subject);
174 if let Some(obj) = object {
175 v.visit_term(obj);
176 }
177 v.visit_expr(infinitive);
178 }
179
180 LogicExpr::Presupposition { assertion, presupposition } => {
181 v.visit_expr(assertion);
182 v.visit_expr(presupposition);
183 }
184
185 LogicExpr::Focus { focused, scope, .. } => {
186 v.visit_term(focused);
187 v.visit_expr(scope);
188 }
189
190 LogicExpr::TemporalAnchor { body, .. } => {
191 v.visit_expr(body);
192 }
193
194 LogicExpr::Distributive { predicate } => {
195 v.visit_expr(predicate);
196 }
197
198 LogicExpr::GroupQuantifier { restriction, body, .. } => {
199 v.visit_expr(restriction);
200 v.visit_expr(body);
201 }
202 }
203}
204
205pub fn walk_term<'a, V: Visitor<'a>>(v: &mut V, term: &'a Term<'a>) {
206 match term {
207 Term::Constant(_) | Term::Variable(_) | Term::Sigma(_) | Term::Intension(_) | Term::Value { .. } => {}
208
209 Term::Function(_, args) => {
210 for arg in *args {
211 v.visit_term(arg);
212 }
213 }
214
215 Term::Group(members) => {
216 for m in *members {
217 v.visit_term(m);
218 }
219 }
220
221 Term::Possessed { possessor, .. } => {
222 v.visit_term(possessor);
223 }
224
225 Term::Proposition(expr) => {
226 v.visit_expr(expr);
227 }
228 }
229}
230
231pub fn walk_np<'a, V: Visitor<'a>>(v: &mut V, np: &'a NounPhrase<'a>) {
232 if let Some(poss) = np.possessor {
233 v.visit_np(poss);
234 }
235 for pp in np.pps.iter() {
236 v.visit_expr(pp);
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use logicaffeine_base::Symbol;
244
245 struct VariableCollector {
246 variables: Vec<Symbol>,
247 }
248
249 impl<'a> Visitor<'a> for VariableCollector {
250 fn visit_term(&mut self, term: &'a Term<'a>) {
251 if let Term::Variable(sym) = term {
252 self.variables.push(*sym);
253 }
254 walk_term(self, term);
255 }
256 }
257
258 struct ExprCounter {
259 count: usize,
260 }
261
262 impl<'a> Visitor<'a> for ExprCounter {
263 fn visit_expr(&mut self, expr: &'a LogicExpr<'a>) {
264 self.count += 1;
265 walk_expr(self, expr);
266 }
267 }
268
269 #[test]
270 fn variable_collector_finds_variables() {
271 use logicaffeine_base::Arena;
272 use logicaffeine_base::Interner;
273
274 let mut interner = Interner::new();
275 let x = interner.intern("x");
276 let y = interner.intern("y");
277
278 let term_arena: Arena<Term> = Arena::new();
279 let terms = term_arena.alloc_slice([Term::Variable(x), Term::Variable(y)]);
280
281 let expr_arena: Arena<LogicExpr> = Arena::new();
282 let pred = interner.intern("P");
283 let expr = expr_arena.alloc(LogicExpr::Predicate { name: pred, args: terms, world: None });
284
285 let mut collector = VariableCollector { variables: vec![] };
286 collector.visit_expr(expr);
287
288 assert_eq!(collector.variables.len(), 2);
289 assert!(collector.variables.contains(&x));
290 assert!(collector.variables.contains(&y));
291 }
292
293 #[test]
294 fn expr_counter_counts_nested() {
295 use logicaffeine_base::Arena;
296 use logicaffeine_base::Interner;
297 use crate::token::TokenType;
298
299 let mut interner = Interner::new();
300 let p = interner.intern("P");
301 let q = interner.intern("Q");
302
303 let expr_arena: Arena<LogicExpr> = Arena::new();
304
305 let left = expr_arena.alloc(LogicExpr::Atom(p));
306 let right = expr_arena.alloc(LogicExpr::Atom(q));
307 let binary = expr_arena.alloc(LogicExpr::BinaryOp {
308 left,
309 op: TokenType::And,
310 right,
311 });
312
313 let mut counter = ExprCounter { count: 0 };
314 counter.visit_expr(binary);
315
316 assert_eq!(counter.count, 3);
317 }
318}