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