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