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::Exclamative { body, .. } => {
142 v.visit_expr(body);
143 }
144 LogicExpr::Optative { wish } => {
145 v.visit_expr(wish);
146 }
147 LogicExpr::Implicature { assertion, implicature } => {
148 v.visit_expr(assertion);
149 v.visit_expr(implicature);
150 }
151 LogicExpr::Imperative { action } => {
152 v.visit_expr(action);
153 }
154
155 LogicExpr::SpeechAct { content, .. } => {
156 v.visit_expr(content);
157 }
158
159 LogicExpr::Counterfactual { antecedent, consequent } => {
160 v.visit_expr(antecedent);
161 v.visit_expr(consequent);
162 }
163
164 LogicExpr::Causal { effect, cause } => {
165 v.visit_expr(cause);
166 v.visit_expr(effect);
167 }
168 LogicExpr::Concessive { main, concession } => {
169 v.visit_expr(main);
170 v.visit_expr(concession);
171 }
172
173 LogicExpr::Comparative { subject, object, .. } => {
174 v.visit_term(subject);
175 v.visit_term(object);
176 }
177
178 LogicExpr::Superlative { subject, .. } => {
179 v.visit_term(subject);
180 }
181
182 LogicExpr::Scopal { body, .. } => {
183 v.visit_expr(body);
184 }
185
186 LogicExpr::Control { subject, object, infinitive, .. } => {
187 v.visit_term(subject);
188 if let Some(obj) = object {
189 v.visit_term(obj);
190 }
191 v.visit_expr(infinitive);
192 }
193
194 LogicExpr::Presupposition { assertion, presupposition } => {
195 v.visit_expr(assertion);
196 v.visit_expr(presupposition);
197 }
198
199 LogicExpr::Focus { focused, scope, .. } => {
200 v.visit_term(focused);
201 v.visit_expr(scope);
202 }
203
204 LogicExpr::TemporalAnchor { body, .. } => {
205 v.visit_expr(body);
206 }
207
208 LogicExpr::Distributive { predicate } => {
209 v.visit_expr(predicate);
210 }
211
212 LogicExpr::GroupQuantifier { restriction, body, .. } => {
213 v.visit_expr(restriction);
214 v.visit_expr(body);
215 }
216 }
217}
218
219pub fn walk_term<'a, V: Visitor<'a>>(v: &mut V, term: &'a Term<'a>) {
220 match term {
221 Term::Constant(_) | Term::Variable(_) | Term::Sigma(_) | Term::Intension(_) | Term::Kind(_) | Term::Value { .. } => {}
222
223 Term::Function(_, args) => {
224 for arg in *args {
225 v.visit_term(arg);
226 }
227 }
228
229 Term::Group(members) => {
230 for m in *members {
231 v.visit_term(m);
232 }
233 }
234
235 Term::Possessed { possessor, .. } => {
236 v.visit_term(possessor);
237 }
238
239 Term::Proposition(expr) => {
240 v.visit_expr(expr);
241 }
242 }
243}
244
245pub fn walk_np<'a, V: Visitor<'a>>(v: &mut V, np: &'a NounPhrase<'a>) {
246 if let Some(poss) = np.possessor {
247 v.visit_np(poss);
248 }
249 for pp in np.pps.iter() {
250 v.visit_expr(pp);
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use logicaffeine_base::Symbol;
258
259 struct VariableCollector {
260 variables: Vec<Symbol>,
261 }
262
263 impl<'a> Visitor<'a> for VariableCollector {
264 fn visit_term(&mut self, term: &'a Term<'a>) {
265 if let Term::Variable(sym) = term {
266 self.variables.push(*sym);
267 }
268 walk_term(self, term);
269 }
270 }
271
272 struct ExprCounter {
273 count: usize,
274 }
275
276 impl<'a> Visitor<'a> for ExprCounter {
277 fn visit_expr(&mut self, expr: &'a LogicExpr<'a>) {
278 self.count += 1;
279 walk_expr(self, expr);
280 }
281 }
282
283 #[test]
284 fn variable_collector_finds_variables() {
285 use logicaffeine_base::Arena;
286 use logicaffeine_base::Interner;
287
288 let mut interner = Interner::new();
289 let x = interner.intern("x");
290 let y = interner.intern("y");
291
292 let term_arena: Arena<Term> = Arena::new();
293 let terms = term_arena.alloc_slice([Term::Variable(x), Term::Variable(y)]);
294
295 let expr_arena: Arena<LogicExpr> = Arena::new();
296 let pred = interner.intern("P");
297 let expr = expr_arena.alloc(LogicExpr::Predicate { name: pred, args: terms, world: None });
298
299 let mut collector = VariableCollector { variables: vec![] };
300 collector.visit_expr(expr);
301
302 assert_eq!(collector.variables.len(), 2);
303 assert!(collector.variables.contains(&x));
304 assert!(collector.variables.contains(&y));
305 }
306
307 #[test]
308 fn expr_counter_counts_nested() {
309 use logicaffeine_base::Arena;
310 use logicaffeine_base::Interner;
311 use crate::token::TokenType;
312
313 let mut interner = Interner::new();
314 let p = interner.intern("P");
315 let q = interner.intern("Q");
316
317 let expr_arena: Arena<LogicExpr> = Arena::new();
318
319 let left = expr_arena.alloc(LogicExpr::Atom(p));
320 let right = expr_arena.alloc(LogicExpr::Atom(q));
321 let binary = expr_arena.alloc(LogicExpr::BinaryOp {
322 left,
323 op: TokenType::And,
324 right,
325 });
326
327 let mut counter = ExprCounter { count: 0 };
328 counter.visit_expr(binary);
329
330 assert_eq!(counter.count, 3);
331 }
332}